[ONOS-6809] Support for Packets and packet Metadata

Change-Id: I53910cd8e2ce5845e301dc68bcbf3ae8f34a197b
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
index 8d06100..e71d910 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
@@ -21,10 +21,13 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.pi.runtime.PiHeaderFieldId;
+import org.onosproject.net.pi.runtime.PiPacketMetadata;
 import org.onosproject.net.pi.runtime.PiTableAction;
 import org.onosproject.net.pi.runtime.PiTableId;
 
+import java.util.Collection;
 import java.util.Optional;
 
 /**
@@ -76,6 +79,18 @@
             throws PiInterpreterException;
 
     /**
+     * Returns a collection of metadatas for a packet-out operation that are equivalent to
+     * the traffic treatment of the given OutboundPacket, for the given pipeline configuration.
+     *
+     * @param packet   a ONOS outbound packet
+     * @param pipeconf a pipeline configuration
+     * @return a collection of packet metadata
+     * @throws PiInterpreterException if the packet treatments cannot be mapped to any metadata
+     */
+    Collection<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
+            throws PiInterpreterException;
+
+    /**
      * Signals that an error was encountered while executing the interpreter.
      */
     @Beta
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java
new file mode 100644
index 0000000..0f707a3
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onlab.util.ImmutableByteSequence;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Instance of a metadata for a packet I/O operation, with id and value for a protocol-independent pipeline.
+ */
+@Beta
+public final class PiPacketMetadata {
+
+    private final PiPacketMetadataId id;
+    private final ImmutableByteSequence value;
+
+    /**
+     * Creates a new packet metadata instance for the given identifier and value.
+     *
+     * @param id    packet metadata identifier
+     * @param value value for this metadata
+     */
+    private PiPacketMetadata(PiPacketMetadataId id, ImmutableByteSequence value) {
+        this.id = id;
+        this.value = value;
+    }
+
+    /**
+     * Return the identifier of this packet metadata.
+     *
+     * @return packet metadata identifier
+     */
+    public PiPacketMetadataId id() {
+        return id;
+    }
+
+    /**
+     * Returns the value for the field in this metadata.
+     *
+     * @return value
+     */
+    public ImmutableByteSequence value() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiPacketMetadata piPacket = (PiPacketMetadata) o;
+        return Objects.equal(id, piPacket.id()) &&
+                Objects.equal(value, piPacket.value());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(id, value);
+    }
+
+    @Override
+    public String toString() {
+        return this.id().toString() + " = " + value.toString();
+    }
+
+    /**
+     * Returns a packet metadata builder.
+     *
+     * @return a new builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of protocol-independent packet metadatas.
+     */
+    public static final class Builder {
+
+        private PiPacketMetadataId id;
+        private ImmutableByteSequence value;
+
+        private Builder() {
+            // hides constructor.
+        }
+
+        /**
+         * Sets the identifier of this packet metadata.
+         *
+         * @param id packet metadata identifier
+         * @return this
+         */
+        public Builder withId(PiPacketMetadataId id) {
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * Sets the value of this metadata.
+         *
+         * @param value value of the metadata
+         * @return this
+         */
+        public Builder withValue(ImmutableByteSequence value) {
+            this.value = value;
+            return this;
+        }
+
+        /**
+         * Returns a new packet metadata instance.
+         *
+         * @return packet metadata
+         */
+        public PiPacketMetadata build() {
+            checkNotNull(id);
+            checkNotNull(value);
+            return new PiPacketMetadata(id, value);
+        }
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java
new file mode 100644
index 0000000..038e2a2
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import org.onlab.util.Identifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Identifier of a metadata for a packet I/O operation in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiPacketMetadataId extends Identifier<String> {
+
+    /**
+     * Creates a packet metadata identifier.
+     *
+     * @param name packet metadata name
+     */
+    private PiPacketMetadataId(String name) {
+        super(name);
+    }
+
+    /**
+     * Returns the name of the packet metadata.
+     *
+     * @return packet metadata name
+     */
+    public String name() {
+        return this.identifier;
+    }
+
+    /**
+     * Returns a identifier with the given name.
+     *
+     * @param name packet metadata name
+     * @return packet metadata identifier
+     */
+    public static PiPacketMetadataId of(String name) {
+        checkNotNull(name);
+        checkArgument(!name.isEmpty(), "Name can't be empty");
+        return new PiPacketMetadataId(name);
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java
new file mode 100644
index 0000000..a37a13f
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.util.ImmutableByteSequence;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Instance of a packet I/O operation, and its metadatas for a protocol-independent pipeline.
+ */
+@Beta
+public final class PiPacketOperation {
+
+    enum Type {
+        /**
+         * Represents a packet out.
+         */
+        PACKET_OUT,
+
+        /**
+         * Represents a packet in.
+         */
+        PACKET_IN,
+    }
+
+    private final ImmutableByteSequence data;
+    private final Set<PiPacketMetadata> packetMetadatas;
+    private final PiPacketOperation.Type type;
+
+    /**
+     * Creates a new packet I/O operation for the given data, metadatas and operation type.
+     *
+     * @param data            the packet raw data
+     * @param packetMetadatas set of packet metadata
+     * @param type            type of this packet operation
+     */
+    private PiPacketOperation(ImmutableByteSequence data, Collection<PiPacketMetadata> packetMetadatas, Type type) {
+        this.data = data;
+        this.packetMetadatas = ImmutableSet.copyOf(packetMetadatas);
+        this.type = type;
+    }
+
+    /**
+     * Return the type of this packet.
+     *
+     * @return packet type
+     */
+    public Type type() {
+        return type;
+    }
+
+    /**
+     * Returns the data of this packet.
+     *
+     * @return packet data
+     */
+    public ImmutableByteSequence data() {
+        return data;
+    }
+
+    /**
+     * Returns all metadatas of this packet.
+     * Returns an empty collection if the packet doesn't have any metadata.
+     *
+     * @return collection of metadatas
+     */
+    public Collection<PiPacketMetadata> metadatas() {
+        return packetMetadatas;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiPacketOperation that = (PiPacketOperation) o;
+        return Objects.equal(packetMetadatas, that.packetMetadatas) &&
+                Objects.equal(data, that.data()) &&
+                Objects.equal(type, that.type());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(data, packetMetadatas, type);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .addValue(packetMetadatas)
+                .addValue(type.toString())
+                .toString();
+    }
+
+    /**
+     * Returns an packet builder.
+     *
+     * @return a new builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of protocol-independent packets.
+     */
+    public static final class Builder {
+
+        private Map<PiPacketMetadataId, PiPacketMetadata> packetMetadatas = new HashMap<>();
+        private PiPacketOperation.Type type;
+        private ImmutableByteSequence data;
+
+        private Builder() {
+            // hides constructor.
+        }
+
+        /**
+         * Adds the raw packet data.
+         *
+         * @param data the packet raw data
+         * @return this
+         */
+        public Builder withData(ImmutableByteSequence data) {
+            checkNotNull(data);
+            this.data = data;
+            return this;
+        }
+
+        /**
+         * Adds a metadata.
+         * Only one metadata is allowed for a given metadata id.
+         * If a metadata with same id already exists it will be replaced by the given one.
+         *
+         * @param metadata packet metadata
+         * @return this
+         */
+        public Builder withMetadata(PiPacketMetadata metadata) {
+            checkNotNull(metadata);
+            packetMetadatas.put(metadata.id(), metadata);
+
+            return this;
+        }
+
+        /**
+         * Adds many packet metadatas.
+         *
+         * @param metadatas collection of metadata
+         * @return this
+         */
+        public Builder withMetadatas(Collection<PiPacketMetadata> metadatas) {
+            checkNotNull(metadatas);
+            metadatas.forEach(this::withMetadata);
+            return this;
+        }
+
+        /**
+         * Sets the type of this packet.
+         *
+         * @param type type of the packet
+         * @return this
+         */
+        public Builder withType(Type type) {
+            this.type = type;
+            return this;
+        }
+
+        /**
+         * Returns a new packet instance.
+         *
+         * @return packet
+         */
+        public PiPacketOperation build() {
+            checkNotNull(data);
+            checkNotNull(packetMetadatas);
+            checkNotNull(type);
+            return new PiPacketOperation(data, packetMetadatas.values(), type);
+        }
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java b/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
index ff8ac93..ddee2ca 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
@@ -23,6 +23,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiPipelineInterpreter;
 import org.onosproject.net.pi.runtime.PiAction;
@@ -30,9 +31,11 @@
 import org.onosproject.net.pi.runtime.PiActionParam;
 import org.onosproject.net.pi.runtime.PiActionParamId;
 import org.onosproject.net.pi.runtime.PiHeaderFieldId;
+import org.onosproject.net.pi.runtime.PiPacketMetadata;
 import org.onosproject.net.pi.runtime.PiTableAction;
 import org.onosproject.net.pi.runtime.PiTableId;
 
+import java.util.Collection;
 import java.util.Optional;
 
 import static org.onosproject.net.PortNumber.CONTROLLER;
@@ -98,6 +101,12 @@
         }
     }
 
+    @Override
+    public Collection<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
+            throws PiInterpreterException {
+        return null;
+    }
+
     /**
      * Returns an action instance with no runtime parameters.
      */
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java
index 59f0c8c..ee307b6 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java
@@ -24,6 +24,7 @@
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiPipelineInterpreter;
 import org.onosproject.net.pi.runtime.PiAction;
@@ -31,9 +32,11 @@
 import org.onosproject.net.pi.runtime.PiActionParam;
 import org.onosproject.net.pi.runtime.PiActionParamId;
 import org.onosproject.net.pi.runtime.PiHeaderFieldId;
+import org.onosproject.net.pi.runtime.PiPacketMetadata;
 import org.onosproject.net.pi.runtime.PiTableAction;
 import org.onosproject.net.pi.runtime.PiTableId;
 
+import java.util.Collection;
 import java.util.Optional;
 
 import static org.onosproject.net.PortNumber.CONTROLLER;
@@ -97,6 +100,12 @@
         }
     }
 
+    @Override
+    public Collection<PiPacketMetadata> mapOutboundPacket(OutboundPacket packet, PiPipeconf pipeconf)
+            throws PiInterpreterException {
+        throw new UnsupportedOperationException("Currently unsupported");
+    }
+
     /**
      * Returns an action instance with no runtime parameters.
      */
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
index ee622cb..03e28c2 100644
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
+++ b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
@@ -17,6 +17,8 @@
 package org.onosproject.p4runtime.api;
 
 import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPacketOperation;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.runtime.PiTableId;
 
@@ -75,6 +77,16 @@
     CompletableFuture<Collection<PiTableEntry>> dumpTable(PiTableId tableId);
 
     /**
+     * Executes a packet-out operation.
+     *
+     * @param packet   packet-out operation to be performed by the device
+     * @param pipeconf pipeconf currently deployed on the device
+     * @return a completable future of a boolean, true if the operations was successful, false otherwise.
+     */
+    CompletableFuture<Boolean> packetOut(PiPacketOperation packet, PiPipeconf pipeconf);
+
+
+    /**
      * Shutdown the client by terminating any active RPC such as the stream channel.
      */
     void shutdown();
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 35e9726..3762eb1 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
@@ -27,13 +27,14 @@
 import io.grpc.stub.StreamObserver;
 import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiPacketOperation;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.runtime.PiTableId;
 import org.onosproject.p4runtime.api.P4RuntimeClient;
 import org.onosproject.p4runtime.api.P4RuntimeEvent;
 import org.slf4j.Logger;
 import p4.P4RuntimeGrpc;
-import p4.config.P4InfoOuterClass;
 import p4.tmp.P4Config;
 
 import java.io.IOException;
@@ -46,8 +47,14 @@
 
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
 import static org.slf4j.LoggerFactory.getLogger;
-import static p4.P4RuntimeOuterClass.*;
+import static p4.P4RuntimeOuterClass.ForwardingPipelineConfig;
+import static p4.P4RuntimeOuterClass.MasterArbitrationUpdate;
+import static p4.P4RuntimeOuterClass.PacketIn;
+import static p4.P4RuntimeOuterClass.SetForwardingPipelineConfigRequest;
 import static p4.P4RuntimeOuterClass.SetForwardingPipelineConfigRequest.Action.VERIFY_AND_COMMIT;
+import static p4.P4RuntimeOuterClass.StreamMessageRequest;
+import static p4.P4RuntimeOuterClass.StreamMessageResponse;
+import static p4.config.P4InfoOuterClass.P4Info;
 
 /**
  * Implementation of a P4Runtime client.
@@ -112,9 +119,9 @@
             StreamMessageRequest initRequest = StreamMessageRequest
                     .newBuilder()
                     .setArbitration(MasterArbitrationUpdate
-                                            .newBuilder()
-                                            .setDeviceId(p4DeviceId)
-                                            .build())
+                            .newBuilder()
+                            .setDeviceId(p4DeviceId)
+                            .build())
                     .build();
             streamRequestObserver.onNext(initRequest);
             return true;
@@ -133,12 +140,12 @@
 
         log.debug("Setting pipeline config for {}", deviceId);
 
-        P4InfoOuterClass.P4Info.Builder p4iInfoBuilder = P4InfoOuterClass.P4Info.newBuilder();
+        P4Info.Builder p4iInfoBuilder = P4Info.newBuilder();
 
         try {
             TextFormat.getParser().merge(new InputStreamReader(p4info),
-                                         ExtensionRegistry.getEmptyRegistry(),
-                                         p4iInfoBuilder);
+                    ExtensionRegistry.getEmptyRegistry(),
+                    p4iInfoBuilder);
         } catch (IOException ex) {
             log.warn("Unable to load p4info for {}: {}", deviceId, ex.getMessage());
             return false;
@@ -161,11 +168,11 @@
                 .newBuilder()
                 .setAction(VERIFY_AND_COMMIT)
                 .addConfigs(ForwardingPipelineConfig
-                                    .newBuilder()
-                                    .setDeviceId(p4DeviceId)
-                                    .setP4Info(p4iInfoBuilder.build())
-                                    .setP4DeviceConfig(deviceIdConfig.toByteString())
-                                    .build())
+                        .newBuilder()
+                        .setDeviceId(p4DeviceId)
+                        .setP4Info(p4iInfoBuilder.build())
+                        .setP4DeviceConfig(deviceIdConfig.toByteString())
+                        .build())
                 .build();
         try {
             this.blockingStub.setForwardingPipelineConfig(request);
@@ -190,6 +197,37 @@
     }
 
     @Override
+    public CompletableFuture<Boolean> packetOut(PiPacketOperation packet, PiPipeconf pipeconf) {
+        CompletableFuture<Boolean> result = new CompletableFuture<>();
+//        P4InfoBrowser browser = null; //PipeconfHelper.getP4InfoBrowser(pipeconf);
+//        try {
+//            ControllerPacketMetadata controllerPacketMetadata =
+//                    browser.controllerPacketMetadatas().getByName("packet_out");
+//            PacketOut.Builder packetOutBuilder = PacketOut.newBuilder();
+//            packetOutBuilder.addAllMetadata(packet.metadatas().stream().map(metadata -> {
+//                //FIXME we are assuming that there is no more than one metadata per name.
+//                int metadataId = controllerPacketMetadata.getMetadataList().stream().filter(metadataInfo -> {
+//                   return  metadataInfo.getName().equals(metadata.id().name());
+//                }).findFirst().get().getId();
+//                return PacketMetadata.newBuilder()
+//                        .setMetadataId(metadataId)
+//                        .setValue(ByteString.copyFrom(metadata.value().asReadOnlyBuffer()))
+//                        .build();
+//            }).filter(Objects::nonNull).collect(Collectors.toList()));
+//            packetOutBuilder.setPayload(ByteString.copyFrom(packet.data().asReadOnlyBuffer()));
+//            PacketOut packetOut = packetOutBuilder.build();
+//            StreamMessageRequest packetOutRequest = StreamMessageRequest
+//                    .newBuilder().setPacket(packetOut).build();
+//            streamRequestObserver.onNext(packetOutRequest);
+//            result.complete(true);
+//        } catch (P4InfoBrowser.NotFoundException e) {
+//            log.error("Cant find metadata with name \"packet_out\" in p4Info file.");
+//            result.complete(false);
+//        }
+        return result;
+    }
+
+    @Override
     public void shutdown() {
 
         log.info("Shutting down client for {}...", deviceId);