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

Also other minor fixes / refactorings

Change-Id: I2205890b76471e8e8490beccd6b36e5358f8d407
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);