blob: 48840f4dbe2221472ffc4a90037a6b12a1958519 [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 Casconedf794592020-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
Daniele Moro46b27632021-02-01 17:12:16 +0100160 public P4RuntimeReadClient.ReadRequest allTableEntries() {
161 try {
162 doTableEntry(null, false);
163 } catch (InternalRequestException e) {
164 log.warn("Unable to read entries for all tables from {}: {}",
165 client.deviceId(), e.getMessage());
166 }
167 return this;
168 }
169
170 @Override
171 public P4RuntimeReadClient.ReadRequest allDefaultTableEntries() {
172 try {
173 doTableEntry(null, true);
174 } catch (InternalRequestException e) {
175 log.warn("Unable to read default entries for all tables from {}: {}",
176 client.deviceId(), e.getMessage());
177 }
178 return this;
179 }
180
181 @Override
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800182 public P4RuntimeReadClient.ReadRequest actionProfileGroups(PiActionProfileId actionProfileId) {
183 try {
184 requestMsg.addEntities(
185 P4RuntimeOuterClass.Entity.newBuilder()
186 .setActionProfileGroup(
187 P4RuntimeOuterClass.ActionProfileGroup.newBuilder()
188 .setActionProfileId(
189 p4ActionProfileId(actionProfileId))
190 .build())
191 .build());
192 } catch (InternalRequestException e) {
193 log.warn("Unable to read groups for action profile '{}' from {}: {}",
194 actionProfileId, client.deviceId(), e.getMessage());
195 }
196 return this;
197 }
198
199 @Override
200 public P4RuntimeReadClient.ReadRequest actionProfileMembers(PiActionProfileId actionProfileId) {
201 try {
202 requestMsg.addEntities(
203 P4RuntimeOuterClass.Entity.newBuilder()
204 .setActionProfileMember(
205 P4RuntimeOuterClass.ActionProfileMember.newBuilder()
206 .setActionProfileId(
207 p4ActionProfileId(actionProfileId))
208 .build())
209 .build());
210 } catch (InternalRequestException e) {
211 log.warn("Unable to read members for action profile '{}' from {}: {}",
212 actionProfileId, client.deviceId(), e.getMessage());
213 }
214 return this;
215 }
216
217 @Override
218 public P4RuntimeReadClient.ReadRequest counterCells(PiCounterId counterId) {
219 try {
220 requestMsg.addEntities(
221 P4RuntimeOuterClass.Entity.newBuilder()
222 .setCounterEntry(
223 P4RuntimeOuterClass.CounterEntry.newBuilder()
224 .setCounterId(p4CounterId(counterId))
225 .build())
226 .build());
227 } catch (InternalRequestException e) {
228 log.warn("Unable to read cells for counter '{}' from {}: {}",
229 counterId, client.deviceId(), e.getMessage());
230 }
231 return this;
232 }
233
234 @Override
235 public P4RuntimeReadClient.ReadRequest meterCells(PiMeterId meterId) {
236 try {
237 requestMsg.addEntities(
238 P4RuntimeOuterClass.Entity.newBuilder()
239 .setMeterEntry(
240 P4RuntimeOuterClass.MeterEntry.newBuilder()
241 .setMeterId(p4MeterId(meterId))
242 .build())
243 .build());
244 } catch (InternalRequestException e) {
245 log.warn("Unable to read cells for meter '{}' from {}: {}",
246 meterId, client.deviceId(), e.getMessage());
247 }
248 return this;
249 }
250
251 @Override
252 public P4RuntimeReadClient.ReadRequest directCounterCells(PiTableId tableId) {
253 try {
254 requestMsg.addEntities(
255 P4RuntimeOuterClass.Entity.newBuilder()
256 .setDirectCounterEntry(
257 P4RuntimeOuterClass.DirectCounterEntry.newBuilder()
258 .setTableEntry(
259 P4RuntimeOuterClass.TableEntry
260 .newBuilder()
261 .setTableId(p4TableId(tableId))
262 .build())
263 .build())
264 .build());
265 } catch (InternalRequestException e) {
266 log.warn("Unable to read direct counter cells for table '{}' from {}: {}",
267 tableId, client.deviceId(), e.getMessage());
268 }
269 return this;
270 }
271
272 @Override
273 public P4RuntimeReadClient.ReadRequest directMeterCells(PiTableId tableId) {
274 try {
275 requestMsg.addEntities(
276 P4RuntimeOuterClass.Entity.newBuilder()
277 .setDirectMeterEntry(
278 P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
279 .setTableEntry(
280 P4RuntimeOuterClass.TableEntry
281 .newBuilder()
282 .setTableId(p4TableId(tableId))
283 .build())
284 .build())
285 .build());
286 } catch (InternalRequestException e) {
287 log.warn("Unable to read direct meter cells for table '{}' from {}: {}",
288 tableId, client.deviceId(), e.getMessage());
289 }
290 return this;
291 }
292
293 private void doTableEntry(PiTableId piTableId, boolean defaultEntries)
294 throws InternalRequestException {
Daniele Moro46b27632021-02-01 17:12:16 +0100295
296 final var builder = P4RuntimeOuterClass.TableEntry.newBuilder();
297
298 builder.setIsDefaultAction(defaultEntries);
299 if (piTableId == null) {
300 builder.setCounterData(P4RuntimeOuterClass.CounterData.getDefaultInstance());
301 builder.setMeterConfig(P4RuntimeOuterClass.MeterConfig.getDefaultInstance());
302 } else {
303 builder.setTableId(p4TableId(piTableId));
Daniele Moro298d3262021-02-16 12:54:06 +0100304 if (tableHasCounters(piTableId)) {
305 builder.setCounterData(P4RuntimeOuterClass.CounterData.getDefaultInstance());
306 }
Carmelo Casconedf794592020-07-17 01:26:31 -0700307 }
308 final var entityMsg = P4RuntimeOuterClass.Entity
309 .newBuilder().setTableEntry(builder.build()).build();
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800310 requestMsg.addEntities(entityMsg);
311 }
312
313 @Override
314 public CompletableFuture<P4RuntimeReadClient.ReadResponse> submit() {
315 final P4RuntimeOuterClass.ReadRequest readRequest = requestMsg.build();
316 log.debug("Sending read request to {} for {} entities...",
317 client.deviceId(), readRequest.getEntitiesCount());
318 if (readRequest.getEntitiesCount() == 0) {
319 // No need to ask the server.
320 return completedFuture(ReadResponseImpl.EMPTY);
321 }
322 final CompletableFuture<P4RuntimeReadClient.ReadResponse> future =
323 new CompletableFuture<>();
324 // Instantiate response builder and let stream observer populate it.
325 final ReadResponseImpl.Builder responseBuilder =
326 ReadResponseImpl.builder(client.deviceId(), pipeconf);
327 final StreamObserver<P4RuntimeOuterClass.ReadResponse> observer =
328 new StreamObserver<P4RuntimeOuterClass.ReadResponse>() {
329 @Override
330 public void onNext(P4RuntimeOuterClass.ReadResponse value) {
331 log.debug("Received read response from {} with {} entities...",
332 client.deviceId(), value.getEntitiesCount());
333 value.getEntitiesList().forEach(responseBuilder::addEntity);
334 }
335 @Override
336 public void onError(Throwable t) {
337 client.handleRpcError(t, "READ");
338 // TODO: implement parsing of trailer errors
339 future.complete(responseBuilder.fail(t));
340 }
341 @Override
342 public void onCompleted() {
343 future.complete(responseBuilder.build());
344 }
345 };
346 client.execRpc(s -> s.read(readRequest, observer), SHORT_TIMEOUT_SECONDS);
347 return future;
348 }
349
350 @Override
351 public P4RuntimeReadClient.ReadResponse submitSync() {
352 return Futures.getUnchecked(submit());
353 }
354
355 private int p4TableId(PiTableId piTableId) throws InternalRequestException {
356 try {
357 return getBrowser().tables().getByName(piTableId.id())
358 .getPreamble().getId();
359 } catch (P4InfoBrowser.NotFoundException e) {
360 throw new InternalRequestException(e.getMessage());
361 }
362 }
363
Carmelo Casconedf794592020-07-17 01:26:31 -0700364 private boolean tableHasCounters(PiTableId piTableId) throws InternalRequestException {
365 return pipeconf.pipelineModel().table(piTableId).orElseThrow(
366 () -> new InternalRequestException(format(
367 "Not such a table in pipeline model: %s", piTableId)))
368 .counters().size() > 0;
369 }
370
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800371 private int p4ActionProfileId(PiActionProfileId piActionProfileId)
372 throws InternalRequestException {
373 try {
374 return getBrowser().actionProfiles().getByName(piActionProfileId.id())
375 .getPreamble().getId();
376 } catch (P4InfoBrowser.NotFoundException e) {
377 throw new InternalRequestException(e.getMessage());
378 }
379 }
380
381 private int p4CounterId(PiCounterId counterId)
382 throws InternalRequestException {
383 try {
384 return getBrowser().counters().getByName(counterId.id())
385 .getPreamble().getId();
386 } catch (P4InfoBrowser.NotFoundException e) {
387 throw new InternalRequestException(e.getMessage());
388 }
389 }
390
391 private int p4MeterId(PiMeterId meterId)
392 throws InternalRequestException {
393 try {
394 return getBrowser().meters().getByName(meterId.id())
395 .getPreamble().getId();
396 } catch (P4InfoBrowser.NotFoundException e) {
397 throw new InternalRequestException(e.getMessage());
398 }
399 }
400
401 private P4InfoBrowser getBrowser() throws InternalRequestException {
402 final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
403 if (browser == null) {
404 throw new InternalRequestException(
405 "Unable to get a P4Info browser for pipeconf " + pipeconf.id());
406 }
407 return browser;
408 }
409
410 /**
411 * Internal exception to signal that something went wrong when populating
412 * the request.
413 */
414 private final class InternalRequestException extends Exception {
415
416 private InternalRequestException(String message) {
417 super(message);
418 }
419 }
420}