Fixed P4RuntimeClient not parsing detailed write errors
Before the only error shown was UNKNOWN, now we can report errors
specif to each write request.
Change-Id: I5eeed890597b7273720414498b3abce8e9d62fa8
diff --git a/protocols/p4runtime/ctl/BUILD b/protocols/p4runtime/ctl/BUILD
index 9418b80..d6c0c7a 100644
--- a/protocols/p4runtime/ctl/BUILD
+++ b/protocols/p4runtime/ctl/BUILD
@@ -6,6 +6,7 @@
"@com_google_protobuf//:protobuf_java",
"//protocols/grpc:grpc-core-repkg",
"@io_grpc_grpc_java//netty",
+ "@io_grpc_grpc_java//protobuf-lite",
"@io_grpc_grpc_java//stub",
"@com_google_api_grpc_proto_google_common_protos//jar",
]
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
index 867bfa3..0bd45d3 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
@@ -24,8 +24,10 @@
import com.google.protobuf.InvalidProtocolBufferException;
import io.grpc.Context;
import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
+import io.grpc.protobuf.lite.ProtoLiteUtils;
import io.grpc.stub.ClientCallStreamObserver;
import io.grpc.stub.StreamObserver;
import org.apache.commons.lang3.tuple.ImmutablePair;
@@ -118,6 +120,11 @@
// Timeout in seconds to obtain the request lock.
private static final int LOCK_TIMEOUT = 60;
+ private static final Metadata.Key<com.google.rpc.Status> STATUS_DETAILS_KEY =
+ Metadata.Key.of("grpc-status-details-bin",
+ ProtoLiteUtils.metadataMarshaller(
+ com.google.rpc.Status.getDefaultInstance()));
+
private static final Map<WriteOperationType, Update.Type> UPDATE_TYPES = ImmutableMap.of(
WriteOperationType.UNSPECIFIED, Update.Type.UNSPECIFIED,
WriteOperationType.INSERT, Update.Type.INSERT,
@@ -1022,37 +1029,32 @@
checkGrpcException(ex);
- List<P4RuntimeOuterClass.Error> errors = null;
- String description = null;
- try {
- errors = extractWriteErrorDetails(ex);
- } catch (InvalidProtocolBufferException e) {
- description = ex.getStatus().getDescription();
- }
+ final List<P4RuntimeOuterClass.Error> errors = extractWriteErrorDetails(ex);
- log.warn("Unable to {} {} {}(s) on {}: {}{} (detailed errors might be logged below)",
+ if (errors.isEmpty()) {
+ final String description = ex.getStatus().getDescription();
+ log.warn("Unable to {} {} {}(s) on {}: {}",
opType.name(), writeEntities.size(), entryType, deviceId,
ex.getStatus().getCode().name(),
- description == null ? "" : " - " + description);
-
- if (errors == null || errors.isEmpty()) {
+ description == null ? "" : " - " + description);
return;
}
// FIXME: we are assuming entities is an ordered collection, e.g. a list,
// and that errors are reported in the same order as the corresponding
// written entity. Write RPC methods should be refactored to accept an
- // order list of entities, instead of a collection.
+ // ordered list of entities, instead of a collection.
if (errors.size() == writeEntities.size()) {
Iterator<E> entityIterator = writeEntities.iterator();
errors.stream()
.map(e -> ImmutablePair.of(e, entityIterator.next()))
.filter(p -> p.left.getCanonicalCode() != Status.OK.getCode().value())
- .forEach(p -> log.warn("Unable to {} {}: {} [{}]",
- opType.name(), entryType, parseP4Error(p.getLeft()),
+ .forEach(p -> log.warn("Unable to {} {} on {}: {} [{}]",
+ opType.name(), entryType, deviceId,
+ parseP4Error(p.getLeft()),
p.getRight().toString()));
} else {
- log.error("Unable to reconcile error details to updates " +
+ log.warn("Unable to reconcile error details to updates " +
"(sent {} updates, but device returned {} errors)",
entryType, writeEntities.size(), errors.size());
errors.stream()
@@ -1063,13 +1065,14 @@
}
private List<P4RuntimeOuterClass.Error> extractWriteErrorDetails(
- StatusRuntimeException ex) throws InvalidProtocolBufferException {
- String statusString = ex.getStatus().getDescription();
- if (statusString == null) {
+ StatusRuntimeException ex) {
+ if (!ex.getTrailers().containsKey(STATUS_DETAILS_KEY)) {
return Collections.emptyList();
}
- com.google.rpc.Status status = com.google.rpc.Status
- .parseFrom(statusString.getBytes());
+ com.google.rpc.Status status = ex.getTrailers().get(STATUS_DETAILS_KEY);
+ if (status == null) {
+ return Collections.emptyList();
+ }
return status.getDetailsList().stream()
.map(any -> {
try {
@@ -1086,12 +1089,12 @@
}
private String parseP4Error(P4RuntimeOuterClass.Error err) {
- return format("%s %s (%s code %d)%s",
- Status.fromCodeValue(err.getCanonicalCode()),
+ return format("%s %s%s (%s:%d)",
+ Status.fromCodeValue(err.getCanonicalCode()).getCode(),
err.getMessage(),
+ err.hasDetails() ? ", " + err.getDetails().toString() : "",
err.getSpace(),
- err.getCode(),
- err.hasDetails() ? "\n" + err.getDetails().toString() : "");
+ err.getCode());
}
private void checkGrpcException(StatusRuntimeException ex) {