ONOS-4410 Implemented PacketProgrammable and Pipeliner behaviors in the
BMv2 driver

Also other minor fixes / refactorings

Change-Id: I2205890b76471e8e8490beccd6b36e5358f8d407
diff --git a/drivers/bmv2/pom.xml b/drivers/bmv2/pom.xml
index 680f25d..1722cc4 100644
--- a/drivers/bmv2/pom.xml
+++ b/drivers/bmv2/pom.xml
@@ -49,6 +49,11 @@
             <artifactId>onos-bmv2-protocol</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-drivers</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleDriver.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
similarity index 96%
rename from drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleDriver.java
rename to drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
index 2947b5f..082d1d6 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleDriver.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
@@ -42,11 +42,14 @@
 import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 
-public class Bmv2FlowRuleDriver extends AbstractHandlerBehaviour
+/**
+ * Flow rule programmable device behaviour implementation for BMv2.
+ */
+public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
         implements FlowRuleProgrammable {
 
     private static final Logger LOG =
-            LoggerFactory.getLogger(Bmv2FlowRuleDriver.class);
+            LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class);
     // There's no Bmv2 client method to poll flow entries from the device device. gitNeed a local store.
     private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
             ENTRIES_MAP = Maps.newConcurrentMap();
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java
new file mode 100644
index 0000000..5076786
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016-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.drivers.bmv2;
+
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.bmv2.api.runtime.Bmv2Client;
+import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
+import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketProgrammable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.lang.Math.toIntExact;
+import static org.onosproject.net.PortNumber.FLOOD;
+import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
+
+/**
+ * Packet programmable device behaviour implementation for BMv2.
+ */
+public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable {
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class);
+
+    @Override
+    public void emit(OutboundPacket packet) {
+
+        TrafficTreatment treatment = packet.treatment();
+
+        treatment.allInstructions().forEach(inst -> {
+            if (inst.type().equals(OUTPUT)) {
+                Instructions.OutputInstruction outInst = (Instructions.OutputInstruction) inst;
+                if (outInst.port().isLogical()) {
+                    if (outInst.port() == FLOOD) {
+                        // TODO: implement flood
+                        LOG.info("Flood not implemented", outInst);
+                    }
+                    LOG.info("Output on logical port not supported: {}", outInst);
+                } else {
+                    try {
+                        long longPort = outInst.port().toLong();
+                        int portNumber = toIntExact(longPort);
+                        send(portNumber, packet);
+                    } catch (ArithmeticException e) {
+                        LOG.error("Port number overflow! Cannot send packet on port {} (long), as the bmv2" +
+                                          " device only accepts int port values.");
+                    }
+                }
+            } else {
+                LOG.info("Instruction type not supported: {}", inst.type().name());
+            }
+        });
+    }
+
+    private void send(int port, OutboundPacket packet) {
+
+        DeviceId deviceId = handler().data().deviceId();
+
+        Bmv2Client deviceClient;
+        try {
+            deviceClient = Bmv2ThriftClient.of(deviceId);
+        } catch (Bmv2RuntimeException e) {
+            LOG.error("Failed to connect to Bmv2 device", e);
+            return;
+        }
+
+        ImmutableByteSequence bs = ImmutableByteSequence.copyFrom(packet.data());
+        try {
+            deviceClient.transmitPacket(port, bs);
+        } catch (Bmv2RuntimeException e) {
+            LOG.info("Unable to push packet to device: deviceId={}, packet={}", deviceId, bs);
+        }
+    }
+}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Pipeliner.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Pipeliner.java
new file mode 100644
index 0000000..b8e4205
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Pipeliner.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016-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.drivers.bmv2;
+
+import org.onosproject.driver.pipeline.DefaultSingleTablePipeline;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+
+import java.util.List;
+
+/**
+ * Pipeliner device behaviour implementation for BMv2.
+ */
+public class Bmv2Pipeliner extends AbstractHandlerBehaviour implements Pipeliner {
+
+    private Pipeliner pipeliner;
+
+    @Override
+    public void init(DeviceId deviceId, PipelinerContext context) {
+        // TODO: get multi-table pipeliner dynamically based on BMv2 device running model
+        // Right now we only support single table pipelines
+        pipeliner = new DefaultSingleTablePipeline();
+        pipeliner.init(deviceId, context);
+    }
+
+    @Override
+    public void filter(FilteringObjective filterObjective) {
+        pipeliner.filter(filterObjective);
+    }
+
+    @Override
+    public void forward(ForwardingObjective forwardObjective) {
+        pipeliner.forward(forwardObjective);
+    }
+
+    @Override
+    public void next(NextObjective nextObjective) {
+        pipeliner.next(nextObjective);
+    }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        return pipeliner.getNextMappings(nextGroup);
+    }
+}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortGetterDriver.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortDiscovery.java
similarity index 97%
rename from drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortGetterDriver.java
rename to drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortDiscovery.java
index e1da42b..0825883 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortGetterDriver.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortDiscovery.java
@@ -34,7 +34,7 @@
 import java.util.Collections;
 import java.util.List;
 
-public class Bmv2PortGetterDriver extends AbstractHandlerBehaviour
+public class Bmv2PortDiscovery extends AbstractHandlerBehaviour
         implements PortDiscovery {
 
     private final Logger log =
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultFlowRuleTranslator.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultFlowRuleTranslator.java
index 1d93499..a54a3c8 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultFlowRuleTranslator.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultFlowRuleTranslator.java
@@ -67,7 +67,7 @@
 public class Bmv2DefaultFlowRuleTranslator implements Bmv2FlowRuleTranslator {
 
     // TODO: config is harcoded now, instead it should be selected based on device model
-    private final TranslatorConfig config = new Bmv2SimplePipelineTranslatorConfig();
+    private final TranslatorConfig config = new Bmv2SimpleTranslatorConfig();
     private final Bmv2Model model = config.model();
 
     private static Bmv2TernaryMatchParam buildTernaryParam(Bmv2ModelField field, Criterion criterion, int byteWidth)
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimplePipelineTranslatorConfig.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimpleTranslatorConfig.java
similarity index 85%
rename from drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimplePipelineTranslatorConfig.java
rename to drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimpleTranslatorConfig.java
index 13f27e6..565c1e4 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimplePipelineTranslatorConfig.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimpleTranslatorConfig.java
@@ -23,6 +23,7 @@
 import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.bmv2.api.model.Bmv2Model;
 import org.onosproject.bmv2.api.runtime.Bmv2Action;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
@@ -36,19 +37,19 @@
 
 /**
  * Implementation of a Bmv2 flow rule translator configuration for the
- * simple_pipeline.p4 model.
+ * simple.p4 model.
  */
 @Beta
-public class Bmv2SimplePipelineTranslatorConfig implements Bmv2FlowRuleTranslator.TranslatorConfig {
+public class Bmv2SimpleTranslatorConfig implements Bmv2FlowRuleTranslator.TranslatorConfig {
 
-    private static final String JSON_CONFIG_PATH = "/simple_pipeline.json";
+    private static final String JSON_CONFIG_PATH = "/simple.json";
     private final Map<String, Criterion.Type> fieldMap = Maps.newHashMap();
     private final Bmv2Model model;
 
     /**
      * Creates a new simple pipeline translator configuration.
      */
-    public Bmv2SimplePipelineTranslatorConfig() {
+    public Bmv2SimpleTranslatorConfig() {
 
         this.model = getModel();
 
@@ -65,6 +66,12 @@
                 .build();
     }
 
+    private static Bmv2Action buildPushToCpAction() {
+        return Bmv2Action.builder()
+                .withName("send_to_cpu")
+                .build();
+    }
+
     private static Bmv2Action buildFwdAction(Instructions.OutputInstruction inst)
             throws Bmv2FlowRuleTranslatorException {
 
@@ -73,8 +80,12 @@
         actionBuilder.withName("fwd");
 
         if (inst.port().isLogical()) {
-            throw new Bmv2FlowRuleTranslatorException(
-                    "Output logic port numbers not supported: " + inst);
+            if (inst.port() == PortNumber.CONTROLLER) {
+                return buildPushToCpAction();
+            } else {
+                throw new Bmv2FlowRuleTranslatorException(
+                        "Output logic port number not supported: " + inst);
+            }
         }
 
         actionBuilder.addParameter(
@@ -84,7 +95,7 @@
     }
 
     private static Bmv2Model getModel() {
-        InputStream inputStream = Bmv2SimplePipelineTranslatorConfig.class
+        InputStream inputStream = Bmv2SimpleTranslatorConfig.class
                 .getResourceAsStream(JSON_CONFIG_PATH);
         InputStreamReader reader = new InputStreamReader(inputStream);
         BufferedReader bufReader = new BufferedReader(reader);
diff --git a/drivers/bmv2/src/main/resources/bmv2-drivers.xml b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
index 279d470..bf1baac 100644
--- a/drivers/bmv2/src/main/resources/bmv2-drivers.xml
+++ b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
@@ -18,8 +18,12 @@
 <drivers>
     <driver name="bmv2-thrift" manufacturer="p4.org" hwVersion="bmv2" swVersion="unknown">
         <behaviour api="org.onosproject.net.behaviour.PortDiscovery"
-                   impl="org.onosproject.drivers.bmv2.Bmv2PortGetterDriver"/>
+                   impl="org.onosproject.drivers.bmv2.Bmv2PortDiscovery"/>
         <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
-                   impl="org.onosproject.drivers.bmv2.Bmv2FlowRuleDriver"/>
+                   impl="org.onosproject.drivers.bmv2.Bmv2FlowRuleProgrammable"/>
+        <behaviour api="org.onosproject.net.behaviour.Pipeliner"
+                   impl="org.onosproject.drivers.bmv2.Bmv2Pipeliner"/>
+        <behaviour api="org.onosproject.net.packet.PacketProgrammable"
+                   impl="org.onosproject.drivers.bmv2.Bmv2PacketProgrammable"/>
     </driver>
 </drivers>
diff --git a/drivers/bmv2/src/main/resources/simple_pipeline.json b/drivers/bmv2/src/main/resources/simple.json
similarity index 75%
rename from drivers/bmv2/src/main/resources/simple_pipeline.json
rename to drivers/bmv2/src/main/resources/simple.json
index 372bdcd..f6e6752 100644
--- a/drivers/bmv2/src/main/resources/simple_pipeline.json
+++ b/drivers/bmv2/src/main/resources/simple.json
@@ -83,22 +83,6 @@
       ],
       "length_exp": null,
       "max_length": null
-    },
-    {
-      "name": "cpu_header_t",
-      "id": 3,
-      "fields": [
-        [
-          "device",
-          8
-        ],
-        [
-          "reason",
-          8
-        ]
-      ],
-      "length_exp": null,
-      "max_length": null
     }
   ],
   "headers": [
@@ -119,12 +103,6 @@
       "id": 2,
       "header_type": "intrinsic_metadata_t",
       "metadata": true
-    },
-    {
-      "name": "cpu_header",
-      "id": 3,
-      "header_type": "cpu_header_t",
-      "metadata": false
     }
   ],
   "header_stacks": [],
@@ -138,42 +116,6 @@
           "name": "start",
           "id": 0,
           "parser_ops": [],
-          "transition_key": [
-            {
-              "type": "lookahead",
-              "value": [
-                0,
-                64
-              ]
-            }
-          ],
-          "transitions": [
-            {
-              "value": "0x0000000000000000",
-              "mask": null,
-              "next_state": "parse_cpu_header"
-            },
-            {
-              "value": "default",
-              "mask": null,
-              "next_state": "parse_ethernet"
-            }
-          ]
-        },
-        {
-          "name": "parse_cpu_header",
-          "id": 1,
-          "parser_ops": [
-            {
-              "op": "extract",
-              "parameters": [
-                {
-                  "type": "regular",
-                  "value": "cpu_header"
-                }
-              ]
-            }
-          ],
           "transition_key": [],
           "transitions": [
             {
@@ -185,7 +127,7 @@
         },
         {
           "name": "parse_ethernet",
-          "id": 2,
+          "id": 1,
           "parser_ops": [
             {
               "op": "extract",
@@ -214,7 +156,6 @@
       "name": "deparser",
       "id": 0,
       "order": [
-        "cpu_header",
         "ethernet"
       ]
     }
@@ -301,59 +242,9 @@
     {
       "name": "send_to_cpu",
       "id": 3,
-      "runtime_data": [
-        {
-          "name": "device",
-          "bitwidth": 8
-        },
-        {
-          "name": "reason",
-          "bitwidth": 8
-        }
-      ],
+      "runtime_data": [],
       "primitives": [
         {
-          "op": "add_header",
-          "parameters": [
-            {
-              "type": "header",
-              "value": "cpu_header"
-            }
-          ]
-        },
-        {
-          "op": "modify_field",
-          "parameters": [
-            {
-              "type": "field",
-              "value": [
-                "cpu_header",
-                "device"
-              ]
-            },
-            {
-              "type": "runtime_data",
-              "value": 0
-            }
-          ]
-        },
-        {
-          "op": "modify_field",
-          "parameters": [
-            {
-              "type": "field",
-              "value": [
-                "cpu_header",
-                "reason"
-              ]
-            },
-            {
-              "type": "runtime_data",
-              "value": 1
-            }
-          ]
-        },
-        {
           "op": "modify_field",
           "parameters": [
             {
@@ -365,7 +256,7 @@
             },
             {
               "type": "hexstr",
-              "value": "0xfa"
+              "value": "0xff"
             }
           ]
         }
@@ -433,7 +324,8 @@
             "send_to_cpu": null,
             "_drop": null
           },
-          "default_action": null
+          "default_action": null,
+          "base_default_next": null
         }
       ],
       "conditionals": []