blob: 4a48897cc54a7d59a178ef1c994f414c941c202b [file] [log] [blame]
Carmelo Casconee44592f2018-09-12 02:24:47 -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.drivers.p4runtime;
18
19import com.google.common.collect.ArrayListMultimap;
20import com.google.common.collect.ListMultimap;
21import com.google.common.collect.Lists;
22import com.google.common.collect.Maps;
23import com.google.common.collect.Sets;
24import com.google.common.util.concurrent.Striped;
25import org.onlab.util.SharedExecutors;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070026import org.onosproject.drivers.p4runtime.mirror.P4RuntimeActionProfileGroupMirror;
Carmelo Casconee44592f2018-09-12 02:24:47 -070027import org.onosproject.drivers.p4runtime.mirror.P4RuntimeActionProfileMemberMirror;
Carmelo Casconee44592f2018-09-12 02:24:47 -070028import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
ghj0504520ed7340c2018-10-26 13:06:35 -070029import org.onosproject.net.DefaultAnnotations;
Carmelo Casconee44592f2018-09-12 02:24:47 -070030import org.onosproject.net.DeviceId;
31import org.onosproject.net.group.DefaultGroup;
32import org.onosproject.net.group.DefaultGroupDescription;
33import org.onosproject.net.group.Group;
34import org.onosproject.net.group.GroupDescription;
35import org.onosproject.net.group.GroupOperation;
36import org.onosproject.net.group.GroupOperations;
37import org.onosproject.net.group.GroupProgrammable;
38import org.onosproject.net.group.GroupStore;
39import org.onosproject.net.pi.model.PiActionId;
40import org.onosproject.net.pi.model.PiActionProfileId;
41import org.onosproject.net.pi.model.PiActionProfileModel;
42import org.onosproject.net.pi.runtime.PiAction;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070043import org.onosproject.net.pi.runtime.PiActionProfileGroup;
44import org.onosproject.net.pi.runtime.PiActionProfileGroupHandle;
45import org.onosproject.net.pi.runtime.PiActionProfileMember;
46import org.onosproject.net.pi.runtime.PiActionProfileMemberHandle;
47import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
Carmelo Casconee44592f2018-09-12 02:24:47 -070048import org.onosproject.net.pi.service.PiGroupTranslator;
49import org.onosproject.net.pi.service.PiTranslatedEntity;
50import org.onosproject.net.pi.service.PiTranslationException;
51import org.onosproject.p4runtime.api.P4RuntimeClient;
52
53import java.util.Collection;
54import java.util.Collections;
55import java.util.List;
56import java.util.Map;
57import java.util.Objects;
58import java.util.Optional;
59import java.util.Set;
60import java.util.concurrent.locks.Lock;
61import java.util.stream.Collectors;
62import java.util.stream.Stream;
63
64import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.DELETE;
65import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
66import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.MODIFY;
67
68/**
69 * Implementation of GroupProgrammable to handle action profile groups in
70 * P4Runtime.
71 */
72public class P4RuntimeActionGroupProgrammable
73 extends AbstractP4RuntimeHandlerBehaviour
74 implements GroupProgrammable {
75
76 // If true, we avoid querying the device and return what's already known by
77 // the ONOS store.
78 private static final String READ_ACTION_GROUPS_FROM_MIRROR = "actionGroupReadFromMirror";
79 private static final boolean DEFAULT_READ_ACTION_GROUPS_FROM_MIRROR = false;
ghj0504520ed7340c2018-10-26 13:06:35 -070080 private static final String MAX_MEM_SIZE = "maxMemSize";
Carmelo Casconee44592f2018-09-12 02:24:47 -070081
82 protected GroupStore groupStore;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070083 private P4RuntimeActionProfileGroupMirror groupMirror;
Carmelo Casconee44592f2018-09-12 02:24:47 -070084 private P4RuntimeActionProfileMemberMirror memberMirror;
85 private PiGroupTranslator groupTranslator;
86
87 // Needed to synchronize operations over the same group.
88 private static final Striped<Lock> STRIPED_LOCKS = Striped.lock(30);
ghj0504520ed7340c2018-10-26 13:06:35 -070089 private static final int GROUP_MEMBERS_BUFFER_SIZE = 3;
Carmelo Casconee44592f2018-09-12 02:24:47 -070090
91 @Override
92 protected boolean setupBehaviour() {
93 if (!super.setupBehaviour()) {
94 return false;
95 }
Carmelo Casconecb4327a2018-09-11 15:17:23 -070096 groupMirror = this.handler().get(P4RuntimeActionProfileGroupMirror.class);
Carmelo Casconee44592f2018-09-12 02:24:47 -070097 memberMirror = this.handler().get(P4RuntimeActionProfileMemberMirror.class);
98 groupStore = handler().get(GroupStore.class);
Yi Tsengd7716482018-10-31 15:34:30 -070099 groupTranslator = translationService.groupTranslator();
Carmelo Casconee44592f2018-09-12 02:24:47 -0700100 return true;
101 }
102
103 @Override
104 public void performGroupOperation(DeviceId deviceId,
105 GroupOperations groupOps) {
106 if (!setupBehaviour()) {
107 return;
108 }
109
110 groupOps.operations().stream()
111 .filter(op -> !op.groupType().equals(GroupDescription.Type.ALL))
112 .forEach(op -> {
113 // ONOS-7785 We need app cookie (action profile id) from the group
114 Group groupOnStore = groupStore.getGroup(deviceId, op.groupId());
115 GroupDescription groupDesc = new DefaultGroupDescription(
116 deviceId, op.groupType(), op.buckets(), groupOnStore.appCookie(),
117 op.groupId().id(), groupOnStore.appId());
118 DefaultGroup groupToApply = new DefaultGroup(op.groupId(), groupDesc);
119 processGroupOperation(groupToApply, op.opType());
120 });
121 }
122
123 @Override
124 public Collection<Group> getGroups() {
125 if (!setupBehaviour()) {
126 return Collections.emptyList();
127 }
128 return getActionGroups();
129 }
130
131 private Collection<Group> getActionGroups() {
132
133 if (driverBoolProperty(READ_ACTION_GROUPS_FROM_MIRROR,
134 DEFAULT_READ_ACTION_GROUPS_FROM_MIRROR)) {
135 return getActionGroupsFromMirror();
136 }
137
138 final Collection<PiActionProfileId> actionProfileIds = pipeconf.pipelineModel()
139 .actionProfiles()
140 .stream()
141 .map(PiActionProfileModel::id)
142 .collect(Collectors.toList());
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700143 final List<PiActionProfileGroup> groupsOnDevice = actionProfileIds.stream()
Carmelo Casconee44592f2018-09-12 02:24:47 -0700144 .flatMap(this::streamGroupsFromDevice)
145 .collect(Collectors.toList());
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700146 final Set<PiActionProfileMemberHandle> membersOnDevice = actionProfileIds
Carmelo Casconee44592f2018-09-12 02:24:47 -0700147 .stream()
148 .flatMap(actProfId -> getMembersFromDevice(actProfId)
149 .stream()
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700150 .map(memberId -> PiActionProfileMemberHandle.of(
Carmelo Casconee44592f2018-09-12 02:24:47 -0700151 deviceId, actProfId, memberId)))
152 .collect(Collectors.toSet());
153
154 if (groupsOnDevice.isEmpty()) {
155 return Collections.emptyList();
156 }
157
158 // Sync mirrors.
159 syncGroupMirror(groupsOnDevice);
160 syncMemberMirror(membersOnDevice);
161
162 final List<Group> result = Lists.newArrayList();
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700163 final List<PiActionProfileGroup> inconsistentGroups = Lists.newArrayList();
164 final List<PiActionProfileGroup> validGroups = Lists.newArrayList();
Carmelo Casconee44592f2018-09-12 02:24:47 -0700165
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700166 for (PiActionProfileGroup piGroup : groupsOnDevice) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700167 final Group pdGroup = forgeGroupEntry(piGroup);
168 if (pdGroup == null) {
169 // Entry is on device but unknown to translation service or
170 // device mirror. Inconsistent. Mark for removal.
171 inconsistentGroups.add(piGroup);
172 } else {
173 validGroups.add(piGroup);
174 result.add(pdGroup);
175 }
176 }
177
178 // Trigger clean up of inconsistent groups and members. This will also
179 // remove all members that are not used by any group, and update the
180 // mirror accordingly.
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700181 final Set<PiActionProfileMemberHandle> membersToKeep = validGroups.stream()
Carmelo Casconee44592f2018-09-12 02:24:47 -0700182 .flatMap(g -> g.members().stream())
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700183 .map(m -> PiActionProfileMemberHandle.of(deviceId, m))
Carmelo Casconee44592f2018-09-12 02:24:47 -0700184 .collect(Collectors.toSet());
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700185 final Set<PiActionProfileMemberHandle> inconsistentMembers = Sets.difference(
Carmelo Casconee44592f2018-09-12 02:24:47 -0700186 membersOnDevice, membersToKeep);
187 SharedExecutors.getSingleThreadExecutor().execute(
188 () -> cleanUpInconsistentGroupsAndMembers(
189 inconsistentGroups, inconsistentMembers));
190
191 return result;
192 }
193
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700194 private void syncGroupMirror(Collection<PiActionProfileGroup> groups) {
195 Map<PiActionProfileGroupHandle, PiActionProfileGroup> handleMap = Maps.newHashMap();
196 groups.forEach(g -> handleMap.put(PiActionProfileGroupHandle.of(deviceId, g), g));
Carmelo Casconee44592f2018-09-12 02:24:47 -0700197 groupMirror.sync(deviceId, handleMap);
198 }
199
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700200 private void syncMemberMirror(Collection<PiActionProfileMemberHandle> memberHandles) {
201 Map<PiActionProfileMemberHandle, PiActionProfileMember> handleMap = Maps.newHashMap();
202 memberHandles.forEach(handle -> handleMap.put(
Carmelo Casconee44592f2018-09-12 02:24:47 -0700203 handle, dummyMember(handle.actionProfileId(), handle.memberId())));
204 memberMirror.sync(deviceId, handleMap);
205 }
206
207 private Collection<Group> getActionGroupsFromMirror() {
208 return groupMirror.getAll(deviceId).stream()
209 .map(TimedEntry::entry)
210 .map(this::forgeGroupEntry)
211 .filter(Objects::nonNull)
212 .collect(Collectors.toList());
213 }
214
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700215 private void cleanUpInconsistentGroupsAndMembers(Collection<PiActionProfileGroup> groupsToRemove,
216 Collection<PiActionProfileMemberHandle> membersToRemove) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700217 if (!groupsToRemove.isEmpty()) {
218 log.warn("Found {} inconsistent action profile groups on {}, removing them...",
219 groupsToRemove.size(), deviceId);
220 groupsToRemove.forEach(piGroup -> {
221 log.debug(piGroup.toString());
222 processGroup(piGroup, null, Operation.REMOVE);
223 });
224 }
225 if (!membersToRemove.isEmpty()) {
226 log.warn("Found {} inconsistent action profile members on {}, removing them...",
227 membersToRemove.size(), deviceId);
228 // FIXME: implement client call to remove members from multiple
229 // action profiles in one shot.
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700230 final ListMultimap<PiActionProfileId, PiActionProfileMemberId>
Carmelo Casconee44592f2018-09-12 02:24:47 -0700231 membersByActProfId = ArrayListMultimap.create();
232 membersToRemove.forEach(m -> membersByActProfId.put(
233 m.actionProfileId(), m.memberId()));
234 membersByActProfId.keySet().forEach(actProfId -> {
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700235 List<PiActionProfileMemberId> removedMembers = getFutureWithDeadline(
Carmelo Casconee44592f2018-09-12 02:24:47 -0700236 client.removeActionProfileMembers(
237 actProfId, membersByActProfId.get(actProfId), pipeconf),
238 "cleaning up action profile members", Collections.emptyList());
239 // Update member mirror.
240 removedMembers.stream()
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700241 .map(id -> PiActionProfileMemberHandle.of(deviceId, actProfId, id))
Carmelo Casconee44592f2018-09-12 02:24:47 -0700242 .forEach(memberMirror::remove);
243 });
244 }
245 }
246
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700247 private Stream<PiActionProfileGroup> streamGroupsFromDevice(PiActionProfileId actProfId) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700248 // TODO: implement P4Runtime client call to read all groups with one call
249 // Good if pipeline has multiple action profiles.
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700250 final Collection<PiActionProfileGroup> groups = getFutureWithDeadline(
251 client.dumpActionProfileGroups(actProfId, pipeconf),
Carmelo Casconee44592f2018-09-12 02:24:47 -0700252 "dumping groups", Collections.emptyList());
253 return groups.stream();
254 }
255
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700256 private List<PiActionProfileMemberId> getMembersFromDevice(PiActionProfileId actProfId) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700257 // TODO: implement P4Runtime client call to read all members with one call
258 // Good if pipeline has multiple action profiles.
259 return getFutureWithDeadline(
260 client.dumpActionProfileMemberIds(actProfId, pipeconf),
261 "dumping action profile ids", Collections.emptyList());
262 }
263
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700264 private Group forgeGroupEntry(PiActionProfileGroup piGroup) {
265 final PiActionProfileGroupHandle handle = PiActionProfileGroupHandle.of(deviceId, piGroup);
266 final Optional<PiTranslatedEntity<Group, PiActionProfileGroup>>
Carmelo Casconee44592f2018-09-12 02:24:47 -0700267 translatedEntity = groupTranslator.lookup(handle);
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700268 final TimedEntry<PiActionProfileGroup> timedEntry = groupMirror.get(handle);
Carmelo Casconee44592f2018-09-12 02:24:47 -0700269 // Is entry consistent with our state?
270 if (!translatedEntity.isPresent()) {
271 log.warn("Group handle not found in translation store: {}", handle);
272 return null;
273 }
274 if (!translatedEntity.get().translated().equals(piGroup)) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800275 log.warn("Group obtained from device {} is different from the one in " +
Carmelo Casconee44592f2018-09-12 02:24:47 -0700276 "translation store: device={}, store={}",
277 deviceId, piGroup, translatedEntity.get().translated());
278 return null;
279 }
280 if (timedEntry == null) {
281 log.warn("Group handle not found in device mirror: {}", handle);
282 return null;
283 }
284 return addedGroup(translatedEntity.get().original(), timedEntry.lifeSec());
285 }
286
287 private Group addedGroup(Group original, long life) {
288 final DefaultGroup forgedGroup = new DefaultGroup(original.id(), original);
289 forgedGroup.setState(Group.GroupState.ADDED);
290 forgedGroup.setLife(life);
291 return forgedGroup;
292 }
293
294 private void processGroupOperation(Group pdGroup, GroupOperation.Type opType) {
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700295 final PiActionProfileGroup piGroup;
Carmelo Casconee44592f2018-09-12 02:24:47 -0700296 try {
297 piGroup = groupTranslator.translate(pdGroup, pipeconf);
298 } catch (PiTranslationException e) {
299 log.warn("Unable to translate group, aborting {} operation: {} [{}]",
300 opType, e.getMessage(), pdGroup);
301 return;
302 }
303 final Operation operation = opType.equals(GroupOperation.Type.DELETE)
304 ? Operation.REMOVE : Operation.APPLY;
305 processGroup(piGroup, pdGroup, operation);
306 }
307
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700308 private void processGroup(PiActionProfileGroup groupToApply,
Carmelo Casconee44592f2018-09-12 02:24:47 -0700309 Group pdGroup,
310 Operation operation) {
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700311 final PiActionProfileGroupHandle handle = PiActionProfileGroupHandle.of(deviceId, groupToApply);
Carmelo Casconee44592f2018-09-12 02:24:47 -0700312 STRIPED_LOCKS.get(handle).lock();
313 try {
314 switch (operation) {
315 case APPLY:
316 if (applyGroupWithMembersOrNothing(groupToApply, handle)) {
317 groupTranslator.learn(handle, new PiTranslatedEntity<>(
318 pdGroup, groupToApply, handle));
319 }
320 return;
321 case REMOVE:
322 if (deleteGroup(groupToApply, handle)) {
323 groupTranslator.forget(handle);
324 }
325 return;
326 default:
327 log.error("Unknwon group operation type {}, cannot process group", operation);
328 break;
329 }
330 } finally {
331 STRIPED_LOCKS.get(handle).unlock();
332 }
333 }
334
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700335 private boolean applyGroupWithMembersOrNothing(PiActionProfileGroup group, PiActionProfileGroupHandle handle) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700336 // First apply members, then group, if fails, delete members.
337 if (!applyAllMembersOrNothing(group.members())) {
338 return false;
339 }
340 if (!applyGroup(group, handle)) {
341 deleteMembers(group.members());
342 return false;
343 }
344 return true;
345 }
346
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700347 private boolean applyGroup(PiActionProfileGroup group, PiActionProfileGroupHandle handle) {
ghj0504520ed7340c2018-10-26 13:06:35 -0700348 final int currentMemberSize = group.members().size();
349 if (groupMirror.get(handle) != null) {
350 String maxMemSize = "";
351 if (groupMirror.annotations(handle) != null &&
352 groupMirror.annotations(handle).value(MAX_MEM_SIZE) != null) {
353 maxMemSize = groupMirror.annotations(handle).value(MAX_MEM_SIZE);
354 }
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700355 if (maxMemSize.equals("") || currentMemberSize > Integer.parseInt(maxMemSize)) {
ghj0504520ed7340c2018-10-26 13:06:35 -0700356 deleteGroup(group, handle);
357 }
358 }
359
360 P4RuntimeClient.WriteOperationType opType =
Carmelo Casconee44592f2018-09-12 02:24:47 -0700361 groupMirror.get(handle) == null ? INSERT : MODIFY;
ghj0504520ed7340c2018-10-26 13:06:35 -0700362 int currentMaxMemberSize = opType == INSERT ? (currentMemberSize + GROUP_MEMBERS_BUFFER_SIZE) : 0;
363
Carmelo Casconee44592f2018-09-12 02:24:47 -0700364 final boolean success = getFutureWithDeadline(
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700365 client.writeActionProfileGroup(group, opType, pipeconf, currentMaxMemberSize),
Carmelo Casconee44592f2018-09-12 02:24:47 -0700366 "performing action profile group " + opType, false);
367 if (success) {
368 groupMirror.put(handle, group);
ghj0504520ed7340c2018-10-26 13:06:35 -0700369 if (opType == INSERT) {
370 groupMirror.putAnnotations(handle, DefaultAnnotations
371 .builder()
372 .set(MAX_MEM_SIZE, Integer.toString(currentMaxMemberSize))
373 .build());
374 }
Carmelo Casconee44592f2018-09-12 02:24:47 -0700375 }
376 return success;
377 }
378
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700379 private boolean deleteGroup(PiActionProfileGroup group, PiActionProfileGroupHandle handle) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700380 final boolean success = getFutureWithDeadline(
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700381 client.writeActionProfileGroup(group, DELETE, pipeconf, 0),
Carmelo Casconee44592f2018-09-12 02:24:47 -0700382 "performing action profile group " + DELETE, false);
383 if (success) {
384 groupMirror.remove(handle);
385 }
386 return success;
387 }
388
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700389 private boolean applyAllMembersOrNothing(Collection<PiActionProfileMember> members) {
390 Collection<PiActionProfileMember> appliedMembers = applyMembers(members);
Carmelo Casconee44592f2018-09-12 02:24:47 -0700391 if (appliedMembers.size() == members.size()) {
392 return true;
393 } else {
394 deleteMembers(appliedMembers);
395 return false;
396 }
397 }
398
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700399 private Collection<PiActionProfileMember> applyMembers(
400 Collection<PiActionProfileMember> members) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700401 return members.stream()
402 .filter(this::applyMember)
403 .collect(Collectors.toList());
404 }
405
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700406 private boolean applyMember(PiActionProfileMember member) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700407 // If exists, modify, otherwise insert
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700408 final PiActionProfileMemberHandle handle = PiActionProfileMemberHandle.of(
Carmelo Casconee44592f2018-09-12 02:24:47 -0700409 deviceId, member);
410 final P4RuntimeClient.WriteOperationType opType =
411 memberMirror.get(handle) == null ? INSERT : MODIFY;
412 final boolean success = getFutureWithDeadline(
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700413 client.writeActionProfileMembers(Collections.singletonList(member),
414 opType, pipeconf),
Carmelo Casconee44592f2018-09-12 02:24:47 -0700415 "performing action profile member " + opType, false);
416 if (success) {
417 memberMirror.put(handle, dummyMember(member.actionProfile(), member.id()));
418 }
419 return success;
420 }
421
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700422 private void deleteMembers(Collection<PiActionProfileMember> members) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700423 members.forEach(this::deleteMember);
424 }
425
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700426 private void deleteMember(PiActionProfileMember member) {
427 final PiActionProfileMemberHandle handle = PiActionProfileMemberHandle.of(
Carmelo Casconee44592f2018-09-12 02:24:47 -0700428 deviceId, member);
429 final boolean success = getFutureWithDeadline(
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700430 client.writeActionProfileMembers(Collections.singletonList(member),
431 DELETE, pipeconf),
Carmelo Casconee44592f2018-09-12 02:24:47 -0700432 "performing action profile member " + DELETE, false);
433 if (success) {
434 memberMirror.remove(handle);
435 }
436 }
437
438 // FIXME: this is nasty, we have to rely on a dummy member of the mirror
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700439 // because the PiActionProfileMember abstraction is broken, since it includes
Carmelo Casconee44592f2018-09-12 02:24:47 -0700440 // attributes that are not part of a P4Runtime member, e.g. weight.
441 // We should remove weight from the class, and have client methods that
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700442 // return the full PiActionProfileMember, not just the IDs.
443 private PiActionProfileMember dummyMember(
444 PiActionProfileId actionProfileId, PiActionProfileMemberId memberId) {
445 return PiActionProfileMember.builder()
Carmelo Casconee44592f2018-09-12 02:24:47 -0700446 .forActionProfile(actionProfileId)
447 .withId(memberId)
448 .withAction(PiAction.builder()
449 .withId(PiActionId.of("dummy"))
450 .build())
451 .build();
452 }
453
454 enum Operation {
455 APPLY, REMOVE
456 }
457}