blob: 68588005ff89ba3f63b5c153bca69e8e72ef29d6 [file] [log] [blame]
Carmelo Cascone4c289b72019-01-22 15:30:45 -08001/*
2 * Copyright 2019-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.p4runtime.ctl.client;
18
19import com.google.common.util.concurrent.Futures;
20import io.grpc.stub.StreamObserver;
21import org.onosproject.net.pi.model.PiActionProfileId;
22import org.onosproject.net.pi.model.PiCounterId;
23import org.onosproject.net.pi.model.PiMeterId;
24import org.onosproject.net.pi.model.PiPipeconf;
25import org.onosproject.net.pi.model.PiTableId;
26import org.onosproject.net.pi.runtime.PiHandle;
27import org.onosproject.p4runtime.api.P4RuntimeReadClient;
28import org.onosproject.p4runtime.ctl.codec.CodecException;
29import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
30import org.onosproject.p4runtime.ctl.utils.PipeconfHelper;
31import org.slf4j.Logger;
32import p4.v1.P4RuntimeOuterClass;
33
34import java.util.concurrent.CompletableFuture;
35
36import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Casconed1724352020-07-17 01:26:31 -070037import static java.lang.String.format;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080038import static java.util.concurrent.CompletableFuture.completedFuture;
39import static org.onosproject.p4runtime.ctl.client.P4RuntimeClientImpl.SHORT_TIMEOUT_SECONDS;
40import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
41import static org.slf4j.LoggerFactory.getLogger;
42
43/**
44 * Handles the creation of P4Runtime ReadRequest and execution of the Read RPC
45 * on the server.
46 */
47public final class ReadRequestImpl implements P4RuntimeReadClient.ReadRequest {
48
49 private static final Logger log = getLogger(ReadRequestImpl.class);
50
51 private final P4RuntimeClientImpl client;
52 private final PiPipeconf pipeconf;
53 private final P4RuntimeOuterClass.ReadRequest.Builder requestMsg;
54
Carmelo Casconec2be50a2019-04-10 00:15:39 -070055 ReadRequestImpl(P4RuntimeClientImpl client, long p4DeviceId, PiPipeconf pipeconf) {
Carmelo Cascone4c289b72019-01-22 15:30:45 -080056 this.client = client;
57 this.pipeconf = pipeconf;
58 this.requestMsg = P4RuntimeOuterClass.ReadRequest.newBuilder()
Carmelo Casconec2be50a2019-04-10 00:15:39 -070059 .setDeviceId(p4DeviceId);
Carmelo Cascone4c289b72019-01-22 15:30:45 -080060 }
61
62 @Override
63 public P4RuntimeReadClient.ReadRequest handles(Iterable<? extends PiHandle> handles) {
64 checkNotNull(handles);
65 handles.forEach(this::handle);
66 return this;
67 }
68
69 @Override
70 public P4RuntimeReadClient.ReadRequest tableEntries(Iterable<PiTableId> tableIds) {
71 checkNotNull(tableIds);
72 tableIds.forEach(this::tableEntries);
73 return this;
74 }
75
76 @Override
77 public P4RuntimeReadClient.ReadRequest defaultTableEntry(Iterable<PiTableId> tableIds) {
78 checkNotNull(tableIds);
79 tableIds.forEach(this::defaultTableEntry);
80 return this;
81 }
82
83 @Override
84 public P4RuntimeReadClient.ReadRequest actionProfileGroups(Iterable<PiActionProfileId> actionProfileIds) {
85 checkNotNull(actionProfileIds);
86 actionProfileIds.forEach(this::actionProfileGroups);
87 return this;
88 }
89
90 @Override
91 public P4RuntimeReadClient.ReadRequest actionProfileMembers(Iterable<PiActionProfileId> actionProfileIds) {
92 checkNotNull(actionProfileIds);
93 actionProfileIds.forEach(this::actionProfileMembers);
94 return this;
95 }
96
97 @Override
98 public P4RuntimeReadClient.ReadRequest counterCells(Iterable<PiCounterId> counterIds) {
99 checkNotNull(counterIds);
100 counterIds.forEach(this::counterCells);
101 return this;
102 }
103
104 @Override
105 public P4RuntimeReadClient.ReadRequest directCounterCells(Iterable<PiTableId> tableIds) {
106 checkNotNull(tableIds);
107 tableIds.forEach(this::directCounterCells);
108 return this;
109 }
110
111 @Override
112 public P4RuntimeReadClient.ReadRequest meterCells(Iterable<PiMeterId> meterIds) {
113 checkNotNull(meterIds);
114 meterIds.forEach(this::meterCells);
115 return this;
116 }
117
118 @Override
119 public P4RuntimeReadClient.ReadRequest directMeterCells(Iterable<PiTableId> tableIds) {
120 checkNotNull(tableIds);
121 tableIds.forEach(this::directMeterCells);
122 return this;
123 }
124
125 @Override
126 public P4RuntimeReadClient.ReadRequest handle(PiHandle handle) {
127 checkNotNull(handle);
128 try {
129 requestMsg.addEntities(CODECS.handle().encode(handle, null, pipeconf));
130 } catch (CodecException e) {
131 log.warn("Unable to read {} from {}: {} [{}]",
132 handle.entityType(), client.deviceId(), e.getMessage(), handle);
133 }
134 return this;
135 }
136
137 @Override
138 public P4RuntimeReadClient.ReadRequest tableEntries(PiTableId tableId) {
139 try {
140 doTableEntry(tableId, false);
141 } catch (InternalRequestException e) {
142 log.warn("Unable to read entries for table '{}' from {}: {}",
143 tableId, client.deviceId(), e.getMessage());
144 }
145 return this;
146 }
147
148 @Override
149 public P4RuntimeReadClient.ReadRequest defaultTableEntry(PiTableId tableId) {
150 try {
151 doTableEntry(tableId, true);
152 } catch (InternalRequestException e) {
153 log.warn("Unable to read default entry for table '{}' from {}: {}",
154 tableId, client.deviceId(), e.getMessage());
155 }
156 return this;
157 }
158
159 @Override
160 public P4RuntimeReadClient.ReadRequest actionProfileGroups(PiActionProfileId actionProfileId) {
161 try {
162 requestMsg.addEntities(
163 P4RuntimeOuterClass.Entity.newBuilder()
164 .setActionProfileGroup(
165 P4RuntimeOuterClass.ActionProfileGroup.newBuilder()
166 .setActionProfileId(
167 p4ActionProfileId(actionProfileId))
168 .build())
169 .build());
170 } catch (InternalRequestException e) {
171 log.warn("Unable to read groups for action profile '{}' from {}: {}",
172 actionProfileId, client.deviceId(), e.getMessage());
173 }
174 return this;
175 }
176
177 @Override
178 public P4RuntimeReadClient.ReadRequest actionProfileMembers(PiActionProfileId actionProfileId) {
179 try {
180 requestMsg.addEntities(
181 P4RuntimeOuterClass.Entity.newBuilder()
182 .setActionProfileMember(
183 P4RuntimeOuterClass.ActionProfileMember.newBuilder()
184 .setActionProfileId(
185 p4ActionProfileId(actionProfileId))
186 .build())
187 .build());
188 } catch (InternalRequestException e) {
189 log.warn("Unable to read members for action profile '{}' from {}: {}",
190 actionProfileId, client.deviceId(), e.getMessage());
191 }
192 return this;
193 }
194
195 @Override
196 public P4RuntimeReadClient.ReadRequest counterCells(PiCounterId counterId) {
197 try {
198 requestMsg.addEntities(
199 P4RuntimeOuterClass.Entity.newBuilder()
200 .setCounterEntry(
201 P4RuntimeOuterClass.CounterEntry.newBuilder()
202 .setCounterId(p4CounterId(counterId))
203 .build())
204 .build());
205 } catch (InternalRequestException e) {
206 log.warn("Unable to read cells for counter '{}' from {}: {}",
207 counterId, client.deviceId(), e.getMessage());
208 }
209 return this;
210 }
211
212 @Override
213 public P4RuntimeReadClient.ReadRequest meterCells(PiMeterId meterId) {
214 try {
215 requestMsg.addEntities(
216 P4RuntimeOuterClass.Entity.newBuilder()
217 .setMeterEntry(
218 P4RuntimeOuterClass.MeterEntry.newBuilder()
219 .setMeterId(p4MeterId(meterId))
220 .build())
221 .build());
222 } catch (InternalRequestException e) {
223 log.warn("Unable to read cells for meter '{}' from {}: {}",
224 meterId, client.deviceId(), e.getMessage());
225 }
226 return this;
227 }
228
229 @Override
230 public P4RuntimeReadClient.ReadRequest directCounterCells(PiTableId tableId) {
231 try {
232 requestMsg.addEntities(
233 P4RuntimeOuterClass.Entity.newBuilder()
234 .setDirectCounterEntry(
235 P4RuntimeOuterClass.DirectCounterEntry.newBuilder()
236 .setTableEntry(
237 P4RuntimeOuterClass.TableEntry
238 .newBuilder()
239 .setTableId(p4TableId(tableId))
240 .build())
241 .build())
242 .build());
243 } catch (InternalRequestException e) {
244 log.warn("Unable to read direct counter cells for table '{}' from {}: {}",
245 tableId, client.deviceId(), e.getMessage());
246 }
247 return this;
248 }
249
250 @Override
251 public P4RuntimeReadClient.ReadRequest directMeterCells(PiTableId tableId) {
252 try {
253 requestMsg.addEntities(
254 P4RuntimeOuterClass.Entity.newBuilder()
255 .setDirectMeterEntry(
256 P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
257 .setTableEntry(
258 P4RuntimeOuterClass.TableEntry
259 .newBuilder()
260 .setTableId(p4TableId(tableId))
261 .build())
262 .build())
263 .build());
264 } catch (InternalRequestException e) {
265 log.warn("Unable to read direct meter cells for table '{}' from {}: {}",
266 tableId, client.deviceId(), e.getMessage());
267 }
268 return this;
269 }
270
271 private void doTableEntry(PiTableId piTableId, boolean defaultEntries)
272 throws InternalRequestException {
273 checkNotNull(piTableId);
Carmelo Casconed1724352020-07-17 01:26:31 -0700274 final var builder = P4RuntimeOuterClass.TableEntry.newBuilder()
275 .setTableId(p4TableId(piTableId))
276 .setIsDefaultAction(defaultEntries);
277 if (tableHasCounters(piTableId)) {
278 builder.setCounterData(P4RuntimeOuterClass.CounterData
279 .getDefaultInstance());
280 }
281 final var entityMsg = P4RuntimeOuterClass.Entity
282 .newBuilder().setTableEntry(builder.build()).build();
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800283 requestMsg.addEntities(entityMsg);
284 }
285
286 @Override
287 public CompletableFuture<P4RuntimeReadClient.ReadResponse> submit() {
288 final P4RuntimeOuterClass.ReadRequest readRequest = requestMsg.build();
289 log.debug("Sending read request to {} for {} entities...",
290 client.deviceId(), readRequest.getEntitiesCount());
291 if (readRequest.getEntitiesCount() == 0) {
292 // No need to ask the server.
293 return completedFuture(ReadResponseImpl.EMPTY);
294 }
295 final CompletableFuture<P4RuntimeReadClient.ReadResponse> future =
296 new CompletableFuture<>();
297 // Instantiate response builder and let stream observer populate it.
298 final ReadResponseImpl.Builder responseBuilder =
299 ReadResponseImpl.builder(client.deviceId(), pipeconf);
300 final StreamObserver<P4RuntimeOuterClass.ReadResponse> observer =
301 new StreamObserver<P4RuntimeOuterClass.ReadResponse>() {
302 @Override
303 public void onNext(P4RuntimeOuterClass.ReadResponse value) {
304 log.debug("Received read response from {} with {} entities...",
305 client.deviceId(), value.getEntitiesCount());
306 value.getEntitiesList().forEach(responseBuilder::addEntity);
307 }
308 @Override
309 public void onError(Throwable t) {
310 client.handleRpcError(t, "READ");
311 // TODO: implement parsing of trailer errors
312 future.complete(responseBuilder.fail(t));
313 }
314 @Override
315 public void onCompleted() {
316 future.complete(responseBuilder.build());
317 }
318 };
319 client.execRpc(s -> s.read(readRequest, observer), SHORT_TIMEOUT_SECONDS);
320 return future;
321 }
322
323 @Override
324 public P4RuntimeReadClient.ReadResponse submitSync() {
325 return Futures.getUnchecked(submit());
326 }
327
328 private int p4TableId(PiTableId piTableId) throws InternalRequestException {
329 try {
330 return getBrowser().tables().getByName(piTableId.id())
331 .getPreamble().getId();
332 } catch (P4InfoBrowser.NotFoundException e) {
333 throw new InternalRequestException(e.getMessage());
334 }
335 }
336
Carmelo Casconed1724352020-07-17 01:26:31 -0700337 private boolean tableHasCounters(PiTableId piTableId) throws InternalRequestException {
338 return pipeconf.pipelineModel().table(piTableId).orElseThrow(
339 () -> new InternalRequestException(format(
340 "Not such a table in pipeline model: %s", piTableId)))
341 .counters().size() > 0;
342 }
343
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800344 private int p4ActionProfileId(PiActionProfileId piActionProfileId)
345 throws InternalRequestException {
346 try {
347 return getBrowser().actionProfiles().getByName(piActionProfileId.id())
348 .getPreamble().getId();
349 } catch (P4InfoBrowser.NotFoundException e) {
350 throw new InternalRequestException(e.getMessage());
351 }
352 }
353
354 private int p4CounterId(PiCounterId counterId)
355 throws InternalRequestException {
356 try {
357 return getBrowser().counters().getByName(counterId.id())
358 .getPreamble().getId();
359 } catch (P4InfoBrowser.NotFoundException e) {
360 throw new InternalRequestException(e.getMessage());
361 }
362 }
363
364 private int p4MeterId(PiMeterId meterId)
365 throws InternalRequestException {
366 try {
367 return getBrowser().meters().getByName(meterId.id())
368 .getPreamble().getId();
369 } catch (P4InfoBrowser.NotFoundException e) {
370 throw new InternalRequestException(e.getMessage());
371 }
372 }
373
374 private P4InfoBrowser getBrowser() throws InternalRequestException {
375 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
376 if (browser == null) {
377 throw new InternalRequestException(
378 "Unable to get a P4Info browser for pipeconf " + pipeconf.id());
379 }
380 return browser;
381 }
382
383 /**
384 * Internal exception to signal that something went wrong when populating
385 * the request.
386 */
387 private final class InternalRequestException extends Exception {
388
389 private InternalRequestException(String message) {
390 super(message);
391 }
392 }
393}