blob: fcb578de6fa727d33e98d2e604239e8a5a1448f4 [file] [log] [blame]
Carmelo Casconee44592f2018-09-12 02:24:47 -07001/*
2 * Copyright 2018-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.ImmutableList;
20import com.google.common.util.concurrent.Striped;
21import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMulticastGroupMirror;
22import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
23import org.onosproject.net.DeviceId;
24import org.onosproject.net.group.DefaultGroup;
25import org.onosproject.net.group.Group;
26import org.onosproject.net.group.GroupDescription;
27import org.onosproject.net.group.GroupOperation;
28import org.onosproject.net.group.GroupOperations;
29import org.onosproject.net.group.GroupProgrammable;
30import org.onosproject.net.group.GroupStore;
31import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
32import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
33import org.onosproject.net.pi.service.PiMulticastGroupTranslator;
34import org.onosproject.net.pi.service.PiTranslatedEntity;
35import org.onosproject.net.pi.service.PiTranslationException;
36import org.onosproject.p4runtime.api.P4RuntimeClient;
37
38import java.util.Collection;
39import java.util.Collections;
40import java.util.Objects;
41import java.util.Optional;
42import java.util.concurrent.locks.Lock;
43import java.util.stream.Collectors;
44
Carmelo Cascone4c289b72019-01-22 15:30:45 -080045import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.DELETE;
46import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.INSERT;
47import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType.MODIFY;
Carmelo Casconee44592f2018-09-12 02:24:47 -070048
49/**
50 * Implementation of GroupProgrammable to handle multicast groups in P4Runtime.
51 */
52public class P4RuntimeMulticastGroupProgrammable
53 extends AbstractP4RuntimeHandlerBehaviour implements GroupProgrammable {
54
Carmelo Cascone4c289b72019-01-22 15:30:45 -080055 // TODO: implement reading groups from device and mirror sync.
56
Carmelo Casconee44592f2018-09-12 02:24:47 -070057 // Needed to synchronize operations over the same group.
58 private static final Striped<Lock> STRIPED_LOCKS = Striped.lock(30);
59
60 private GroupStore groupStore;
61 private P4RuntimeMulticastGroupMirror mcGroupMirror;
62 private PiMulticastGroupTranslator mcGroupTranslator;
63
64 @Override
65 protected boolean setupBehaviour() {
66 if (!super.setupBehaviour()) {
67 return false;
68 }
69 mcGroupMirror = this.handler().get(P4RuntimeMulticastGroupMirror.class);
70 groupStore = handler().get(GroupStore.class);
Yi Tsengd7716482018-10-31 15:34:30 -070071 mcGroupTranslator = translationService.multicastGroupTranslator();
Carmelo Casconee44592f2018-09-12 02:24:47 -070072 return true;
73 }
74
75 @Override
76 public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
77 if (!setupBehaviour()) {
78 return;
79 }
80 groupOps.operations().stream()
81 .filter(op -> op.groupType().equals(GroupDescription.Type.ALL))
82 .forEach(op -> {
83 final Group group = groupStore.getGroup(deviceId, op.groupId());
84 processMcGroupOp(group, op.opType());
85 });
86 }
87
88 @Override
89 public Collection<Group> getGroups() {
90 if (!setupBehaviour()) {
91 return Collections.emptyList();
92 }
93 return ImmutableList.copyOf(getMcGroups());
94 }
95
96 private Collection<Group> getMcGroups() {
Carmelo Cascone4c289b72019-01-22 15:30:45 -080097 // TODO: missing support for reading multicast groups in PI/Stratum.
Carmelo Casconee44592f2018-09-12 02:24:47 -070098 return getMcGroupsFromMirror();
99 }
100
101 private Collection<Group> getMcGroupsFromMirror() {
102 return mcGroupMirror.getAll(deviceId).stream()
103 .map(TimedEntry::entry)
104 .map(this::forgeMcGroupEntry)
105 .filter(Objects::nonNull)
106 .collect(Collectors.toList());
107 }
108
109 private void processMcGroupOp(Group pdGroup, GroupOperation.Type opType) {
110 final PiMulticastGroupEntry mcGroup;
111 try {
112 mcGroup = mcGroupTranslator.translate(pdGroup, pipeconf);
113 } catch (PiTranslationException e) {
114 log.warn("Unable to translate multicast group, aborting {} operation: {} [{}]",
115 opType, e.getMessage(), pdGroup);
116 return;
117 }
118 final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
119 deviceId, mcGroup);
120 final PiMulticastGroupEntry groupOnDevice = mcGroupMirror.get(handle) == null
121 ? null
122 : mcGroupMirror.get(handle).entry();
123 final Lock lock = STRIPED_LOCKS.get(handle);
124 lock.lock();
125 try {
126 processMcGroup(handle, mcGroup,
127 groupOnDevice, pdGroup, opType);
128 } finally {
129 lock.unlock();
130 }
131 }
132
133 private void processMcGroup(PiMulticastGroupEntryHandle handle,
134 PiMulticastGroupEntry groupToApply,
135 PiMulticastGroupEntry groupOnDevice,
136 Group pdGroup, GroupOperation.Type opType) {
137 switch (opType) {
138 case ADD:
139 robustMcGroupAdd(handle, groupToApply, pdGroup);
140 return;
141 case MODIFY:
142 // Since reading multicast groups is not supported yet on
143 // PI/Stratum, we cannot trust groupOnDevic) as we don't have a
144 // mechanism to enforce consistency of the mirror with the
145 // device state.
146 // if (driverBoolProperty(CHECK_MIRROR_BEFORE_UPDATE,
147 // DEFAULT_CHECK_MIRROR_BEFORE_UPDATE)
148 // && p4OpType == MODIFY
149 // && groupOnDevice != null
150 // && groupOnDevice.equals(groupToApply)) {
151 // // Ignore.
152 // return;
153 // }
154 robustMcGroupModify(handle, groupToApply, pdGroup);
155 return;
156 case DELETE:
157 mcGroupApply(handle, groupToApply, pdGroup, DELETE);
158 return;
159 default:
160 log.error("Unknown group operation type {}, " +
161 "cannot process multicast group", opType);
162 }
163 }
164
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800165 private boolean writeMcGroupOnDevice(
166 PiMulticastGroupEntry group, P4RuntimeClient.UpdateType opType) {
167 return client.write(pipeconf).entity(group, opType).submitSync().isSuccess();
Carmelo Casconee44592f2018-09-12 02:24:47 -0700168 }
169
170 private boolean mcGroupApply(PiMulticastGroupEntryHandle handle,
171 PiMulticastGroupEntry piGroup,
172 Group pdGroup,
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800173 P4RuntimeClient.UpdateType opType) {
Carmelo Casconee44592f2018-09-12 02:24:47 -0700174 switch (opType) {
175 case DELETE:
176 if (writeMcGroupOnDevice(piGroup, DELETE)) {
177 mcGroupMirror.remove(handle);
178 mcGroupTranslator.forget(handle);
179 return true;
180 } else {
181 return false;
182 }
183 case INSERT:
184 case MODIFY:
185 if (writeMcGroupOnDevice(piGroup, opType)) {
186 mcGroupMirror.put(handle, piGroup);
187 mcGroupTranslator.learn(handle, new PiTranslatedEntity<>(
188 pdGroup, piGroup, handle));
189 return true;
190 } else {
191 return false;
192 }
193 default:
194 log.warn("Unknown operation type {}, cannot apply group", opType);
195 return false;
196 }
197 }
198
199 private void robustMcGroupAdd(PiMulticastGroupEntryHandle handle,
200 PiMulticastGroupEntry piGroup,
201 Group pdGroup) {
202 if (mcGroupApply(handle, piGroup, pdGroup, INSERT)) {
203 return;
204 }
205 // Try to delete (perhaps it already exists) and re-add...
206 mcGroupApply(handle, piGroup, pdGroup, DELETE);
207 mcGroupApply(handle, piGroup, pdGroup, INSERT);
208 }
209
210 private void robustMcGroupModify(PiMulticastGroupEntryHandle handle,
211 PiMulticastGroupEntry piGroup,
212 Group pdGroup) {
213 if (mcGroupApply(handle, piGroup, pdGroup, MODIFY)) {
214 return;
215 }
216 // Not sure for which reason it cannot be modified, so try to delete and insert instead...
217 mcGroupApply(handle, piGroup, pdGroup, DELETE);
218 mcGroupApply(handle, piGroup, pdGroup, INSERT);
219 }
220
221 private Group forgeMcGroupEntry(PiMulticastGroupEntry mcGroup) {
222 final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
223 deviceId, mcGroup);
224 final Optional<PiTranslatedEntity<Group, PiMulticastGroupEntry>>
225 translatedEntity = mcGroupTranslator.lookup(handle);
226 final TimedEntry<PiMulticastGroupEntry> timedEntry = mcGroupMirror.get(handle);
227 // Is entry consistent with our state?
228 if (!translatedEntity.isPresent()) {
229 log.warn("Multicast group handle not found in translation store: {}", handle);
230 return null;
231 }
232 if (timedEntry == null) {
233 log.warn("Multicast group handle not found in device mirror: {}", handle);
234 return null;
235 }
236 return addedGroup(translatedEntity.get().original(), timedEntry.lifeSec());
237 }
238
239 private Group addedGroup(Group original, long life) {
240 final DefaultGroup forgedGroup = new DefaultGroup(original.id(), original);
241 forgedGroup.setState(Group.GroupState.ADDED);
242 forgedGroup.setLife(life);
243 return forgedGroup;
244 }
245
246}