blob: 1cfe0796674b629d298fa9aabe8dbdf60e48082e [file] [log] [blame]
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -04001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -04003 *
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
Andrea Campanella0288c872017-08-07 18:32:51 +020017package org.onosproject.drivers.p4runtime;
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040018
Carmelo Cascone58136812018-07-19 03:40:16 +020019import com.google.common.collect.ImmutableList;
Yi Tseng8d355132018-04-13 01:40:48 +080020import com.google.common.collect.Sets;
Carmelo Cascone58136812018-07-19 03:40:16 +020021import com.google.common.util.concurrent.Striped;
Carmelo Casconee75b7942017-11-21 17:14:49 -080022import org.onosproject.drivers.p4runtime.mirror.P4RuntimeGroupMirror;
Carmelo Cascone58136812018-07-19 03:40:16 +020023import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMulticastGroupMirror;
Yi Tseng76737cf2018-01-31 17:13:21 -080024import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040025import org.onosproject.net.DeviceId;
Carmelo Casconee75b7942017-11-21 17:14:49 -080026import org.onosproject.net.group.DefaultGroup;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080027import org.onosproject.net.group.DefaultGroupDescription;
Yi Tseng82512da2017-08-16 19:46:36 -070028import org.onosproject.net.group.Group;
Carmelo Cascone58136812018-07-19 03:40:16 +020029import org.onosproject.net.group.GroupDescription;
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040030import org.onosproject.net.group.GroupOperation;
31import org.onosproject.net.group.GroupOperations;
32import org.onosproject.net.group.GroupProgrammable;
Yi Tseng82512da2017-08-16 19:46:36 -070033import org.onosproject.net.group.GroupStore;
Carmelo Cascone87892e22017-11-13 16:01:29 -080034import org.onosproject.net.pi.model.PiActionProfileId;
Carmelo Casconee75b7942017-11-21 17:14:49 -080035import org.onosproject.net.pi.model.PiActionProfileModel;
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040036import org.onosproject.net.pi.runtime.PiActionGroup;
Carmelo Casconee75b7942017-11-21 17:14:49 -080037import org.onosproject.net.pi.runtime.PiActionGroupHandle;
Yi Tseng8d355132018-04-13 01:40:48 +080038import org.onosproject.net.pi.runtime.PiActionGroupMember;
Carmelo Cascone58136812018-07-19 03:40:16 +020039import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
40import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
Carmelo Casconee75b7942017-11-21 17:14:49 -080041import org.onosproject.net.pi.service.PiGroupTranslator;
Carmelo Cascone58136812018-07-19 03:40:16 +020042import org.onosproject.net.pi.service.PiMulticastGroupTranslator;
Carmelo Casconee75b7942017-11-21 17:14:49 -080043import org.onosproject.net.pi.service.PiTranslatedEntity;
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080044import org.onosproject.net.pi.service.PiTranslationException;
Carmelo Cascone58136812018-07-19 03:40:16 +020045import org.onosproject.p4runtime.api.P4RuntimeClient;
Yi Tseng82512da2017-08-16 19:46:36 -070046import org.slf4j.Logger;
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040047
Yi Tseng82512da2017-08-16 19:46:36 -070048import java.util.Collection;
49import java.util.Collections;
Carmelo Casconee75b7942017-11-21 17:14:49 -080050import java.util.Objects;
Yi Tseng82512da2017-08-16 19:46:36 -070051import java.util.concurrent.CompletableFuture;
Yi Tseng82512da2017-08-16 19:46:36 -070052import java.util.concurrent.locks.Lock;
Carmelo Casconee75b7942017-11-21 17:14:49 -080053import java.util.stream.Collectors;
54import java.util.stream.Stream;
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040055
Carmelo Casconee5b28722018-06-22 17:28:28 +020056import static java.lang.String.format;
Carmelo Casconee75b7942017-11-21 17:14:49 -080057import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.DELETE;
58import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
Yi Tseng8d355132018-04-13 01:40:48 +080059import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.MODIFY;
Yi Tseng82512da2017-08-16 19:46:36 -070060import static org.slf4j.LoggerFactory.getLogger;
61
62/**
63 * Implementation of the group programmable behaviour for P4Runtime.
Carmelo Cascone58136812018-07-19 03:40:16 +020064 * <p>
65 * This implementation distinguishes between ALL groups, and other types. ALL
66 * groups are handled via PRE multicast group programming, while other types are
67 * handled via action profile group programming.
Yi Tseng82512da2017-08-16 19:46:36 -070068 */
Carmelo Casconee75b7942017-11-21 17:14:49 -080069public class P4RuntimeGroupProgrammable
70 extends AbstractP4RuntimeHandlerBehaviour
71 implements GroupProgrammable {
72
Carmelo Casconee75b7942017-11-21 17:14:49 -080073 private static final String ACT_GRP_MEMS_STR = "action group members";
74 private static final String DELETE_STR = "delete";
75 private static final String ACT_GRP_STR = "action group";
76 private static final String INSERT_STR = "insert";
Yi Tseng8d355132018-04-13 01:40:48 +080077 private static final String MODIFY_STR = "modify";
Carmelo Casconee75b7942017-11-21 17:14:49 -080078
Yi Tseng82512da2017-08-16 19:46:36 -070079 private static final Logger log = getLogger(P4RuntimeGroupProgrammable.class);
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040080
Carmelo Casconee75b7942017-11-21 17:14:49 -080081 // If true, we ignore re-installing groups that are already known in the
82 // device mirror.
Yi Tsengf325a602018-06-27 18:26:33 +080083 private static final String CHECK_MIRROR_BEFORE_UPDATE = "checkMirrorBeforeUpdate";
84 private static final boolean DEFAULT_CHECK_MIRROR_BEFORE_UPDATE = true;
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -040085
Yi Tseng76737cf2018-01-31 17:13:21 -080086 // If true, we avoid querying the device and return what's already known by
87 // the ONOS store.
Yi Tsengf325a602018-06-27 18:26:33 +080088 private static final String IGNORE_DEVICE_WHEN_GET = "ignoreDeviceWhenGet";
89 private static final boolean DEFAULT_IGNORE_DEVICE_WHEN_GET = false;
Yi Tseng76737cf2018-01-31 17:13:21 -080090
Esin Karaman971fb7f2017-12-28 13:44:52 +000091 protected GroupStore groupStore;
Carmelo Casconee75b7942017-11-21 17:14:49 -080092 private P4RuntimeGroupMirror groupMirror;
Carmelo Cascone58136812018-07-19 03:40:16 +020093 private PiGroupTranslator groupTranslator;
94 private P4RuntimeMulticastGroupMirror mcGroupMirror;
95 private PiMulticastGroupTranslator mcGroupTranslator;
Yi Tseng82512da2017-08-16 19:46:36 -070096
97 // Needed to synchronize operations over the same group.
Carmelo Cascone58136812018-07-19 03:40:16 +020098 private static final Striped<Lock> STRIPED_LOCKS = Striped.lock(30);
Yi Tseng82512da2017-08-16 19:46:36 -070099
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -0400100 @Override
Carmelo Casconee75b7942017-11-21 17:14:49 -0800101 protected boolean setupBehaviour() {
102 if (!super.setupBehaviour()) {
103 return false;
104 }
105 groupMirror = this.handler().get(P4RuntimeGroupMirror.class);
Carmelo Cascone58136812018-07-19 03:40:16 +0200106 mcGroupMirror = this.handler().get(P4RuntimeMulticastGroupMirror.class);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800107 groupStore = handler().get(GroupStore.class);
Carmelo Cascone58136812018-07-19 03:40:16 +0200108 groupTranslator = piTranslationService.groupTranslator();
109 mcGroupTranslator = piTranslationService.multicastGroupTranslator();
Carmelo Casconee75b7942017-11-21 17:14:49 -0800110 return true;
111 }
112
113 @Override
114 public void performGroupOperation(DeviceId deviceId,
115 GroupOperations groupOps) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200116 if (!setupBehaviour()) {
Yi Tseng82512da2017-08-16 19:46:36 -0700117 return;
118 }
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800119 groupOps.operations().forEach(op -> {
120 // ONOS-7785 We need app cookie (action profile id) from the group
121 Group groupOnStore = groupStore.getGroup(deviceId, op.groupId());
122 GroupDescription groupDesc = new DefaultGroupDescription(deviceId,
123 op.groupType(),
124 op.buckets(),
125 groupOnStore.appCookie(),
126 op.groupId().id(),
127 groupOnStore.appId());
128 DefaultGroup groupToApply = new DefaultGroup(op.groupId(), groupDesc);
129 if (op.groupType().equals(GroupDescription.Type.ALL)) {
130 processMcGroupOp(deviceId, groupToApply, op.opType());
131 } else {
132
133 processGroupOp(deviceId, groupToApply, op.opType());
134 }
135 });
Yi Tseng82512da2017-08-16 19:46:36 -0700136 }
137
Yi Tseng82512da2017-08-16 19:46:36 -0700138 @Override
139 public Collection<Group> getGroups() {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200140 if (!setupBehaviour()) {
141 return Collections.emptyList();
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -0400142 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200143 final ImmutableList.Builder<Group> groups = ImmutableList.builder();
144
Yi Tsengf325a602018-06-27 18:26:33 +0800145 if (!driverBoolProperty(IGNORE_DEVICE_WHEN_GET, DEFAULT_IGNORE_DEVICE_WHEN_GET)) {
Carmelo Cascone58136812018-07-19 03:40:16 +0200146 groups.addAll(pipeconf.pipelineModel().actionProfiles().stream()
147 .map(PiActionProfileModel::id)
148 .flatMap(this::streamGroupsFromDevice)
149 .iterator());
150 // FIXME: enable reading MC groups from device once reading from
151 // PRE is supported in PI
152 // groups.addAll(getMcGroupsFromDevice());
Yi Tseng76737cf2018-01-31 17:13:21 -0800153 } else {
Carmelo Cascone58136812018-07-19 03:40:16 +0200154 groups.addAll(groupMirror.getAll(deviceId).stream()
155 .map(TimedEntry::entry)
156 .map(this::forgeGroupEntry)
157 .iterator());
Yi Tseng76737cf2018-01-31 17:13:21 -0800158 }
Carmelo Cascone58136812018-07-19 03:40:16 +0200159 // FIXME: same as before..
160 groups.addAll(mcGroupMirror.getAll(deviceId).stream()
161 .map(TimedEntry::entry)
162 .map(this::forgeMcGroupEntry)
163 .iterator());
164
165 return groups.build();
Carmelo Casconee75b7942017-11-21 17:14:49 -0800166 }
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -0400167
Carmelo Cascone58136812018-07-19 03:40:16 +0200168 private void processGroupOp(DeviceId deviceId, Group pdGroup, GroupOperation.Type opType) {
Carmelo Casconee75b7942017-11-21 17:14:49 -0800169 final PiActionGroup piGroup;
170 try {
Carmelo Cascone58136812018-07-19 03:40:16 +0200171 piGroup = groupTranslator.translate(pdGroup, pipeconf);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800172 } catch (PiTranslationException e) {
Carmelo Cascone58136812018-07-19 03:40:16 +0200173 log.warn("Unable to translate group, aborting {} operation: {} [{}]",
174 opType, e.getMessage(), pdGroup);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800175 return;
176 }
Carmelo Casconee75b7942017-11-21 17:14:49 -0800177 final PiActionGroupHandle handle = PiActionGroupHandle.of(deviceId, piGroup);
178
179 final PiActionGroup groupOnDevice = groupMirror.get(handle) == null
180 ? null
181 : groupMirror.get(handle).entry();
182
Carmelo Cascone58136812018-07-19 03:40:16 +0200183 final Lock lock = STRIPED_LOCKS.get(handle);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800184 lock.lock();
185 try {
Carmelo Casconee75b7942017-11-21 17:14:49 -0800186 processPiGroup(handle, piGroup,
Carmelo Cascone58136812018-07-19 03:40:16 +0200187 groupOnDevice, pdGroup, opType);
188 } finally {
189 lock.unlock();
190 }
191 }
192
193 private void processMcGroupOp(DeviceId deviceId, Group pdGroup, GroupOperation.Type opType) {
194 final PiMulticastGroupEntry mcGroup;
195 try {
196 mcGroup = mcGroupTranslator.translate(pdGroup, pipeconf);
197 } catch (PiTranslationException e) {
198 log.warn("Unable to translate multicast group, aborting {} operation: {} [{}]",
199 opType, e.getMessage(), pdGroup);
200 return;
201 }
202 final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
203 deviceId, mcGroup);
204 final PiMulticastGroupEntry groupOnDevice = mcGroupMirror.get(handle) == null
205 ? null
206 : mcGroupMirror.get(handle).entry();
207 final Lock lock = STRIPED_LOCKS.get(handle);
208 lock.lock();
209 try {
210 processMcGroup(handle, mcGroup,
211 groupOnDevice, pdGroup, opType);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800212 } finally {
213 lock.unlock();
Yi Tseng82512da2017-08-16 19:46:36 -0700214 }
215 }
216
Carmelo Casconee75b7942017-11-21 17:14:49 -0800217 private void processPiGroup(PiActionGroupHandle handle,
218 PiActionGroup groupToApply,
219 PiActionGroup groupOnDevice,
Yi Tseng8d355132018-04-13 01:40:48 +0800220 Group pdGroup, GroupOperation.Type operationType) {
221 if (operationType == GroupOperation.Type.ADD) {
Carmelo Casconee75b7942017-11-21 17:14:49 -0800222 if (groupOnDevice != null) {
Yi Tseng8d355132018-04-13 01:40:48 +0800223 log.warn("Unable to add group {} since group already on device {}",
224 groupToApply.id(), deviceId);
225 log.debug("To apply: {}", groupToApply);
226 log.debug("On device: {}", groupOnDevice);
227 return;
Carmelo Casconee75b7942017-11-21 17:14:49 -0800228 }
Yi Tseng8d355132018-04-13 01:40:48 +0800229
Carmelo Casconee75b7942017-11-21 17:14:49 -0800230 if (writeGroupToDevice(groupToApply)) {
231 groupMirror.put(handle, groupToApply);
Carmelo Cascone58136812018-07-19 03:40:16 +0200232 groupTranslator.learn(handle, new PiTranslatedEntity<>(
Carmelo Casconee75b7942017-11-21 17:14:49 -0800233 pdGroup, groupToApply, handle));
234 }
Yi Tseng8d355132018-04-13 01:40:48 +0800235 } else if (operationType == GroupOperation.Type.MODIFY) {
236 if (groupOnDevice == null) {
237 log.warn("Group {} does not exists on device {}, can not modify it",
238 groupToApply.id(), deviceId);
239 return;
240 }
Yi Tsengf325a602018-06-27 18:26:33 +0800241 if (driverBoolProperty(CHECK_MIRROR_BEFORE_UPDATE, DEFAULT_CHECK_MIRROR_BEFORE_UPDATE)
Yi Tseng8d355132018-04-13 01:40:48 +0800242 && groupOnDevice.equals(groupToApply)) {
243 // Group on device has the same members, ignore operation.
244 return;
245 }
246 if (modifyGroupFromDevice(groupToApply, groupOnDevice)) {
247 groupMirror.put(handle, groupToApply);
Carmelo Cascone58136812018-07-19 03:40:16 +0200248 groupTranslator.learn(handle,
249 new PiTranslatedEntity<>(pdGroup, groupToApply, handle));
Yi Tseng8d355132018-04-13 01:40:48 +0800250 }
Carmelo Casconee75b7942017-11-21 17:14:49 -0800251 } else {
Yi Tseng8d355132018-04-13 01:40:48 +0800252 if (groupOnDevice == null) {
253 log.warn("Unable to remove group {} from device {} since it does" +
254 "not exists on device.", groupToApply.id(), deviceId);
255 return;
256 }
257 if (deleteGroupFromDevice(groupOnDevice)) {
Carmelo Casconee75b7942017-11-21 17:14:49 -0800258 groupMirror.remove(handle);
Carmelo Cascone58136812018-07-19 03:40:16 +0200259 groupTranslator.forget(handle);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800260 }
261 }
262 }
263
Carmelo Cascone58136812018-07-19 03:40:16 +0200264 private void processMcGroup(PiMulticastGroupEntryHandle handle,
265 PiMulticastGroupEntry groupToApply,
266 PiMulticastGroupEntry groupOnDevice,
267 Group pdGroup, GroupOperation.Type opType) {
Carmelo Cascone055e9b22018-09-10 01:59:15 -0700268 switch (opType) {
269 case ADD:
270 robustMcGroupAdd(handle, groupToApply, pdGroup);
271 return;
272 case MODIFY:
273 // Since reading multicast groups is not supported yet on
274 // PI/Stratum, we cannot trust groupOnDevic) as we don't have a
275 // mechanism to enforce consistency of the mirror with the
276 // device state.
277 // if (driverBoolProperty(CHECK_MIRROR_BEFORE_UPDATE,
278 // DEFAULT_CHECK_MIRROR_BEFORE_UPDATE)
279 // && p4OpType == MODIFY
280 // && groupOnDevice != null
281 // && groupOnDevice.equals(groupToApply)) {
282 // // Ignore.
283 // return;
284 // }
285 robustMcGroupModify(handle, groupToApply, pdGroup);
286 return;
287 case DELETE:
288 mcGroupApply(handle, groupToApply, pdGroup, DELETE);
289 return;
290 default:
291 log.error("Unknown group operation type {}, " +
292 "cannot process multicast group", opType);
Carmelo Cascone58136812018-07-19 03:40:16 +0200293 }
294 }
295
296 private boolean writeMcGroupOnDevice(PiMulticastGroupEntry group, P4RuntimeClient.WriteOperationType opType) {
297 return getFutureWithDeadline(
298 client.writePreMulticastGroupEntries(
299 Collections.singleton(group), opType),
300 "performing multicast group " + opType, false);
301 }
302
Carmelo Cascone055e9b22018-09-10 01:59:15 -0700303 private boolean mcGroupApply(PiMulticastGroupEntryHandle handle,
304 PiMulticastGroupEntry piGroup,
305 Group pdGroup,
306 P4RuntimeClient.WriteOperationType opType) {
307 switch (opType) {
308 case DELETE:
309 if (writeMcGroupOnDevice(piGroup, DELETE)) {
310 mcGroupMirror.remove(handle);
311 mcGroupTranslator.forget(handle);
312 return true;
313 } else {
314 return false;
315 }
316 case INSERT:
317 case MODIFY:
318 if (writeMcGroupOnDevice(piGroup, opType)) {
319 mcGroupMirror.put(handle, piGroup);
320 mcGroupTranslator.learn(handle, new PiTranslatedEntity<>(
321 pdGroup, piGroup, handle));
322 return true;
323 } else {
324 return false;
325 }
326 default:
327 log.warn("Unknown operation type {}, cannot apply group", opType);
328 return false;
329 }
330 }
331
332 private void robustMcGroupAdd(PiMulticastGroupEntryHandle handle,
333 PiMulticastGroupEntry piGroup,
334 Group pdGroup) {
335 if (mcGroupApply(handle, piGroup, pdGroup, INSERT)) {
336 return;
337 }
338 // Try to delete (perhaps it already exists) and re-add...
339 mcGroupApply(handle, piGroup, pdGroup, DELETE);
340 mcGroupApply(handle, piGroup, pdGroup, INSERT);
341 }
342
343 private void robustMcGroupModify(PiMulticastGroupEntryHandle handle,
344 PiMulticastGroupEntry piGroup,
345 Group pdGroup) {
346 if (mcGroupApply(handle, piGroup, pdGroup, MODIFY)) {
347 return;
348 }
349 // Not sure for which reason it cannot be modified, so try to delete and insert instead...
350 mcGroupApply(handle, piGroup, pdGroup, DELETE);
351 mcGroupApply(handle, piGroup, pdGroup, INSERT);
352 }
353
Yi Tseng8d355132018-04-13 01:40:48 +0800354 private boolean modifyGroupFromDevice(PiActionGroup groupToApply, PiActionGroup groupOnDevice) {
355 PiActionProfileId groupProfileId = groupToApply.actionProfileId();
356 Collection<PiActionGroupMember> membersToRemove = Sets.newHashSet(groupOnDevice.members());
357 membersToRemove.removeAll(groupToApply.members());
358 Collection<PiActionGroupMember> membersToAdd = Sets.newHashSet(groupToApply.members());
359 membersToAdd.removeAll(groupOnDevice.members());
360
361 if (!membersToAdd.isEmpty() &&
362 !completeFuture(client.writeActionGroupMembers(groupProfileId, membersToAdd, INSERT, pipeconf),
363 ACT_GRP_MEMS_STR, INSERT_STR)) {
364 // remove what we added
365 completeFuture(client.writeActionGroupMembers(groupProfileId, membersToAdd, DELETE, pipeconf),
366 ACT_GRP_MEMS_STR, INSERT_STR);
367 return false;
368 }
369
370 if (!completeFuture(client.writeActionGroup(groupToApply, MODIFY, pipeconf),
371 ACT_GRP_STR, MODIFY_STR)) {
372 // recover group information
373 completeFuture(client.writeActionGroup(groupOnDevice, MODIFY, pipeconf),
374 ACT_GRP_STR, MODIFY_STR);
375 // remove what we added
376 completeFuture(client.writeActionGroupMembers(groupProfileId, membersToAdd, DELETE, pipeconf),
377 ACT_GRP_MEMS_STR, INSERT_STR);
378 return false;
379 }
380
381 if (!membersToRemove.isEmpty() &&
382 !completeFuture(client.writeActionGroupMembers(groupProfileId, membersToRemove, DELETE, pipeconf),
Carmelo Casconee5b28722018-06-22 17:28:28 +0200383 ACT_GRP_MEMS_STR, DELETE_STR)) {
Yi Tseng8d355132018-04-13 01:40:48 +0800384 // add what we removed
385 completeFuture(client.writeActionGroupMembers(groupProfileId, membersToRemove, INSERT, pipeconf),
386 ACT_GRP_MEMS_STR, DELETE_STR);
387 // recover group information
388 completeFuture(client.writeActionGroup(groupOnDevice, MODIFY, pipeconf),
389 ACT_GRP_STR, MODIFY_STR);
390 // remove what we added
391 completeFuture(client.writeActionGroupMembers(groupProfileId, membersToAdd, DELETE, pipeconf),
392 ACT_GRP_MEMS_STR, INSERT_STR);
393 return false;
394 }
395
396 return true;
397 }
398
Carmelo Casconee75b7942017-11-21 17:14:49 -0800399 private boolean writeGroupToDevice(PiActionGroup groupToApply) {
400 // First insert members, then group.
401 // The operation is deemed successful if both operations are successful.
402 // FIXME: add transactional semantics, i.e. remove members if group fails.
403 final boolean membersSuccess = completeFuture(
Yi Tseng8d355132018-04-13 01:40:48 +0800404 client.writeActionGroupMembers(groupToApply.actionProfileId(),
405 groupToApply.members(),
406 INSERT, pipeconf),
Carmelo Casconee75b7942017-11-21 17:14:49 -0800407 ACT_GRP_MEMS_STR, INSERT_STR);
408 return membersSuccess && completeFuture(
409 client.writeActionGroup(groupToApply, INSERT, pipeconf),
410 ACT_GRP_STR, INSERT_STR);
411 }
412
413 private boolean deleteGroupFromDevice(PiActionGroup piActionGroup) {
414 // First delete group, then members.
415 // The operation is deemed successful if both operations are successful.
416 final boolean groupSuccess = completeFuture(
417 client.writeActionGroup(piActionGroup, DELETE, pipeconf),
418 ACT_GRP_STR, DELETE_STR);
419 return groupSuccess && completeFuture(
Yi Tseng8d355132018-04-13 01:40:48 +0800420 client.writeActionGroupMembers(piActionGroup.actionProfileId(),
421 piActionGroup.members(),
422 DELETE, pipeconf),
Carmelo Casconee75b7942017-11-21 17:14:49 -0800423 ACT_GRP_MEMS_STR, DELETE_STR);
424 }
425
426 private boolean completeFuture(CompletableFuture<Boolean> completableFuture,
427 String topic, String action) {
Carmelo Casconee5b28722018-06-22 17:28:28 +0200428 return getFutureWithDeadline(
429 completableFuture, format("performing %s %s", action, topic), false);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800430 }
431
432 private Stream<Group> streamGroupsFromDevice(PiActionProfileId actProfId) {
Carmelo Casconee5b28722018-06-22 17:28:28 +0200433 // Read PI groups and return original PD one.
434 Collection<PiActionGroup> groups = getFutureWithDeadline(
435 client.dumpGroups(actProfId, pipeconf),
436 "dumping groups", Collections.emptyList());
437 return groups.stream()
438 .map(this::forgeGroupEntry)
439 .filter(Objects::nonNull);
Carmelo Casconee75b7942017-11-21 17:14:49 -0800440 }
441
Carmelo Cascone58136812018-07-19 03:40:16 +0200442 private Collection<Group> getMcGroupsFromDevice() {
443 Collection<PiMulticastGroupEntry> groups = getFutureWithDeadline(
444 client.readAllMulticastGroupEntries(),
445 "dumping multicast groups", Collections.emptyList());
446 return groups.stream()
447 .map(this::forgeMcGroupEntry)
448 .filter(Objects::nonNull)
449 .collect(Collectors.toList());
450 }
451
Carmelo Casconee75b7942017-11-21 17:14:49 -0800452 private Group forgeGroupEntry(PiActionGroup piGroup) {
453 final PiActionGroupHandle handle = PiActionGroupHandle.of(deviceId, piGroup);
Carmelo Cascone58136812018-07-19 03:40:16 +0200454 if (!groupTranslator.lookup(handle).isPresent()) {
Carmelo Casconee75b7942017-11-21 17:14:49 -0800455 log.warn("Missing PI group from translation store: {} - {}:{}",
456 pipeconf.id(), piGroup.actionProfileId(),
457 piGroup.id());
458 return null;
459 }
460 final long life = groupMirror.get(handle) != null
461 ? groupMirror.get(handle).lifeSec() : 0;
Carmelo Cascone58136812018-07-19 03:40:16 +0200462 final Group original = groupTranslator.lookup(handle).get().original();
463 return addedGroup(original, life);
464 }
465
466 private Group forgeMcGroupEntry(PiMulticastGroupEntry mcGroup) {
467 final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
468 deviceId, mcGroup);
469 if (!mcGroupTranslator.lookup(handle).isPresent()) {
470 log.warn("Missing PI multicast group {} from translation store",
471 mcGroup.groupId());
472 return null;
473 }
474 final long life = mcGroupMirror.get(handle) != null
475 ? mcGroupMirror.get(handle).lifeSec() : 0;
476 final Group original = mcGroupTranslator.lookup(handle).get().original();
477 return addedGroup(original, life);
478 }
479
480 private Group addedGroup(Group original, long life) {
Carmelo Casconee75b7942017-11-21 17:14:49 -0800481 final DefaultGroup forgedGroup = new DefaultGroup(original.id(), original);
482 forgedGroup.setState(Group.GroupState.ADDED);
483 forgedGroup.setLife(life);
484 return forgedGroup;
485 }
Carmelo Casconeb2e3dba2017-07-27 12:07:09 -0400486}