Set all write responses as failed when P4Runtime server returns error

Change-Id: I1e3e1cd214b71b96c4abd10d8700a1fa11f73f0e
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/client/WriteResponseImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/client/WriteResponseImpl.java
index 404ab80..f24c717 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/client/WriteResponseImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/client/WriteResponseImpl.java
@@ -156,8 +156,8 @@
         WriteResponseImpl buildAsIs() {
             synchronized (this) {
                 if (!pendingResponses.isEmpty()) {
-                    log.warn("Detected partial response from {}, " +
-                                     "{} of {} total entities are in status PENDING",
+                    log.warn("Partial response from {}, {} of {} total " +
+                                     "entities are in status PENDING",
                              deviceId, pendingResponses.size(), allResponses.size());
                 }
                 return new WriteResponseImpl(
@@ -174,6 +174,14 @@
             }
         }
 
+        WriteResponseImpl setFailAllAndBuild(Throwable throwable) {
+            synchronized (this) {
+                pendingResponses.values().forEach(r -> r.setFailure(throwable));
+                pendingResponses.clear();
+                return buildAsIs();
+            }
+        }
+
         WriteResponseImpl setErrorsAndBuild(Throwable throwable) {
             synchronized (this) {
                 return doSetErrorsAndBuild(throwable);
@@ -219,26 +227,26 @@
         private WriteResponseImpl doSetErrorsAndBuild(Throwable throwable) {
             if (!(throwable instanceof StatusRuntimeException)) {
                 // Leave all entity responses in pending state.
-                return buildAsIs();
+                return setFailAllAndBuild(throwable);
             }
             final StatusRuntimeException sre = (StatusRuntimeException) throwable;
             if (!sre.getStatus().equals(Status.UNKNOWN)) {
                 // Error trailers expected only if status is UNKNOWN.
-                return buildAsIs();
+                return setFailAllAndBuild(throwable);
             }
             // Extract error details.
             if (!sre.getTrailers().containsKey(STATUS_DETAILS_KEY)) {
                 log.warn("Cannot parse write error details from {}, " +
                                  "missing status trailers in StatusRuntimeException",
                          deviceId);
-                return buildAsIs();
+                return setFailAllAndBuild(throwable);
             }
             com.google.rpc.Status status = sre.getTrailers().get(STATUS_DETAILS_KEY);
             if (status == null) {
                 log.warn("Cannot parse write error details from {}, " +
                                  "found NULL status trailers in StatusRuntimeException",
                          deviceId);
-                return buildAsIs();
+                return setFailAllAndBuild(throwable);
             }
             final boolean reconcilable = status.getDetailsList().size() == pendingResponses.size();
             // We expect one error for each entity...
@@ -339,6 +347,12 @@
             this.status = WriteResponseStatus.OK;
         }
 
+        private void setFailure(Throwable throwable) {
+            this.status = WriteResponseStatus.OTHER_ERROR;
+            this.explanation = throwable.toString();
+            this.throwable = throwable;
+        }
+
         @Override
         public PiHandle handle() {
             return handle;