blob: 29a7169ba60274b06e6028ccf8e78c48516dcecb [file] [log] [blame]
/*
* Copyright 2019-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.p4runtime.ctl.client;
import com.google.common.util.concurrent.Futures;
import io.grpc.stub.StreamObserver;
import org.onosproject.net.pi.model.PiActionProfileId;
import org.onosproject.net.pi.model.PiCounterId;
import org.onosproject.net.pi.model.PiMeterId;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiTableId;
import org.onosproject.net.pi.runtime.PiHandle;
import org.onosproject.p4runtime.api.P4RuntimeReadClient;
import org.onosproject.p4runtime.ctl.codec.CodecException;
import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
import org.onosproject.p4runtime.ctl.utils.PipeconfHelper;
import org.slf4j.Logger;
import p4.v1.P4RuntimeOuterClass;
import java.util.concurrent.CompletableFuture;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.onosproject.p4runtime.ctl.client.P4RuntimeClientImpl.SHORT_TIMEOUT_SECONDS;
import static org.onosproject.p4runtime.ctl.codec.Codecs.CODECS;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Handles the creation of P4Runtime ReadRequest and execution of the Read RPC
* on the server.
*/
public final class ReadRequestImpl implements P4RuntimeReadClient.ReadRequest {
private static final Logger log = getLogger(ReadRequestImpl.class);
private final P4RuntimeClientImpl client;
private final PiPipeconf pipeconf;
private final P4RuntimeOuterClass.ReadRequest.Builder requestMsg;
ReadRequestImpl(P4RuntimeClientImpl client, long p4DeviceId, PiPipeconf pipeconf) {
this.client = client;
this.pipeconf = pipeconf;
this.requestMsg = P4RuntimeOuterClass.ReadRequest.newBuilder()
.setDeviceId(p4DeviceId);
}
@Override
public P4RuntimeReadClient.ReadRequest handles(Iterable<? extends PiHandle> handles) {
checkNotNull(handles);
handles.forEach(this::handle);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest tableEntries(Iterable<PiTableId> tableIds) {
checkNotNull(tableIds);
tableIds.forEach(this::tableEntries);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest defaultTableEntry(Iterable<PiTableId> tableIds) {
checkNotNull(tableIds);
tableIds.forEach(this::defaultTableEntry);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest actionProfileGroups(Iterable<PiActionProfileId> actionProfileIds) {
checkNotNull(actionProfileIds);
actionProfileIds.forEach(this::actionProfileGroups);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest actionProfileMembers(Iterable<PiActionProfileId> actionProfileIds) {
checkNotNull(actionProfileIds);
actionProfileIds.forEach(this::actionProfileMembers);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest counterCells(Iterable<PiCounterId> counterIds) {
checkNotNull(counterIds);
counterIds.forEach(this::counterCells);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest directCounterCells(Iterable<PiTableId> tableIds) {
checkNotNull(tableIds);
tableIds.forEach(this::directCounterCells);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest meterCells(Iterable<PiMeterId> meterIds) {
checkNotNull(meterIds);
meterIds.forEach(this::meterCells);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest directMeterCells(Iterable<PiTableId> tableIds) {
checkNotNull(tableIds);
tableIds.forEach(this::directMeterCells);
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest handle(PiHandle handle) {
checkNotNull(handle);
try {
requestMsg.addEntities(CODECS.handle().encode(handle, null, pipeconf));
} catch (CodecException e) {
log.warn("Unable to read {} from {}: {} [{}]",
handle.entityType(), client.deviceId(), e.getMessage(), handle);
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest tableEntries(PiTableId tableId) {
try {
doTableEntry(tableId, false);
} catch (InternalRequestException e) {
log.warn("Unable to read entries for table '{}' from {}: {}",
tableId, client.deviceId(), e.getMessage());
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest defaultTableEntry(PiTableId tableId) {
try {
doTableEntry(tableId, true);
} catch (InternalRequestException e) {
log.warn("Unable to read default entry for table '{}' from {}: {}",
tableId, client.deviceId(), e.getMessage());
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest actionProfileGroups(PiActionProfileId actionProfileId) {
try {
requestMsg.addEntities(
P4RuntimeOuterClass.Entity.newBuilder()
.setActionProfileGroup(
P4RuntimeOuterClass.ActionProfileGroup.newBuilder()
.setActionProfileId(
p4ActionProfileId(actionProfileId))
.build())
.build());
} catch (InternalRequestException e) {
log.warn("Unable to read groups for action profile '{}' from {}: {}",
actionProfileId, client.deviceId(), e.getMessage());
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest actionProfileMembers(PiActionProfileId actionProfileId) {
try {
requestMsg.addEntities(
P4RuntimeOuterClass.Entity.newBuilder()
.setActionProfileMember(
P4RuntimeOuterClass.ActionProfileMember.newBuilder()
.setActionProfileId(
p4ActionProfileId(actionProfileId))
.build())
.build());
} catch (InternalRequestException e) {
log.warn("Unable to read members for action profile '{}' from {}: {}",
actionProfileId, client.deviceId(), e.getMessage());
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest counterCells(PiCounterId counterId) {
try {
requestMsg.addEntities(
P4RuntimeOuterClass.Entity.newBuilder()
.setCounterEntry(
P4RuntimeOuterClass.CounterEntry.newBuilder()
.setCounterId(p4CounterId(counterId))
.build())
.build());
} catch (InternalRequestException e) {
log.warn("Unable to read cells for counter '{}' from {}: {}",
counterId, client.deviceId(), e.getMessage());
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest meterCells(PiMeterId meterId) {
try {
requestMsg.addEntities(
P4RuntimeOuterClass.Entity.newBuilder()
.setMeterEntry(
P4RuntimeOuterClass.MeterEntry.newBuilder()
.setMeterId(p4MeterId(meterId))
.build())
.build());
} catch (InternalRequestException e) {
log.warn("Unable to read cells for meter '{}' from {}: {}",
meterId, client.deviceId(), e.getMessage());
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest directCounterCells(PiTableId tableId) {
try {
requestMsg.addEntities(
P4RuntimeOuterClass.Entity.newBuilder()
.setDirectCounterEntry(
P4RuntimeOuterClass.DirectCounterEntry.newBuilder()
.setTableEntry(
P4RuntimeOuterClass.TableEntry
.newBuilder()
.setTableId(p4TableId(tableId))
.build())
.build())
.build());
} catch (InternalRequestException e) {
log.warn("Unable to read direct counter cells for table '{}' from {}: {}",
tableId, client.deviceId(), e.getMessage());
}
return this;
}
@Override
public P4RuntimeReadClient.ReadRequest directMeterCells(PiTableId tableId) {
try {
requestMsg.addEntities(
P4RuntimeOuterClass.Entity.newBuilder()
.setDirectMeterEntry(
P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
.setTableEntry(
P4RuntimeOuterClass.TableEntry
.newBuilder()
.setTableId(p4TableId(tableId))
.build())
.build())
.build());
} catch (InternalRequestException e) {
log.warn("Unable to read direct meter cells for table '{}' from {}: {}",
tableId, client.deviceId(), e.getMessage());
}
return this;
}
private void doTableEntry(PiTableId piTableId, boolean defaultEntries)
throws InternalRequestException {
checkNotNull(piTableId);
final P4RuntimeOuterClass.Entity entityMsg = P4RuntimeOuterClass.Entity
.newBuilder()
.setTableEntry(
P4RuntimeOuterClass.TableEntry.newBuilder()
.setTableId(p4TableId(piTableId))
.setIsDefaultAction(defaultEntries)
.setCounterData(P4RuntimeOuterClass.CounterData
.getDefaultInstance())
.build())
.build();
requestMsg.addEntities(entityMsg);
}
@Override
public CompletableFuture<P4RuntimeReadClient.ReadResponse> submit() {
final P4RuntimeOuterClass.ReadRequest readRequest = requestMsg.build();
log.debug("Sending read request to {} for {} entities...",
client.deviceId(), readRequest.getEntitiesCount());
if (readRequest.getEntitiesCount() == 0) {
// No need to ask the server.
return completedFuture(ReadResponseImpl.EMPTY);
}
final CompletableFuture<P4RuntimeReadClient.ReadResponse> future =
new CompletableFuture<>();
// Instantiate response builder and let stream observer populate it.
final ReadResponseImpl.Builder responseBuilder =
ReadResponseImpl.builder(client.deviceId(), pipeconf);
final StreamObserver<P4RuntimeOuterClass.ReadResponse> observer =
new StreamObserver<P4RuntimeOuterClass.ReadResponse>() {
@Override
public void onNext(P4RuntimeOuterClass.ReadResponse value) {
log.debug("Received read response from {} with {} entities...",
client.deviceId(), value.getEntitiesCount());
value.getEntitiesList().forEach(responseBuilder::addEntity);
}
@Override
public void onError(Throwable t) {
client.handleRpcError(t, "READ");
// TODO: implement parsing of trailer errors
future.complete(responseBuilder.fail(t));
}
@Override
public void onCompleted() {
future.complete(responseBuilder.build());
}
};
client.execRpc(s -> s.read(readRequest, observer), SHORT_TIMEOUT_SECONDS);
return future;
}
@Override
public P4RuntimeReadClient.ReadResponse submitSync() {
return Futures.getUnchecked(submit());
}
private int p4TableId(PiTableId piTableId) throws InternalRequestException {
try {
return getBrowser().tables().getByName(piTableId.id())
.getPreamble().getId();
} catch (P4InfoBrowser.NotFoundException e) {
throw new InternalRequestException(e.getMessage());
}
}
private int p4ActionProfileId(PiActionProfileId piActionProfileId)
throws InternalRequestException {
try {
return getBrowser().actionProfiles().getByName(piActionProfileId.id())
.getPreamble().getId();
} catch (P4InfoBrowser.NotFoundException e) {
throw new InternalRequestException(e.getMessage());
}
}
private int p4CounterId(PiCounterId counterId)
throws InternalRequestException {
try {
return getBrowser().counters().getByName(counterId.id())
.getPreamble().getId();
} catch (P4InfoBrowser.NotFoundException e) {
throw new InternalRequestException(e.getMessage());
}
}
private int p4MeterId(PiMeterId meterId)
throws InternalRequestException {
try {
return getBrowser().meters().getByName(meterId.id())
.getPreamble().getId();
} catch (P4InfoBrowser.NotFoundException e) {
throw new InternalRequestException(e.getMessage());
}
}
private P4InfoBrowser getBrowser() throws InternalRequestException {
final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
throw new InternalRequestException(
"Unable to get a P4Info browser for pipeconf " + pipeconf.id());
}
return browser;
}
/**
* Internal exception to signal that something went wrong when populating
* the request.
*/
private final class InternalRequestException extends Exception {
private InternalRequestException(String message) {
super(message);
}
}
}