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