emit + tests for OFPacketProvider

Change-Id: I9d6a2a641f857742fa68597189a9047dd16989d1
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java
index 9d07c2f..85ea70a 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/OpenFlowPacketContext.java
@@ -5,8 +5,9 @@
 
 /**
  * A representation of a packet context which allows any provider
- * to view the packet in event but may block the response to the
- * event if blocked has been called.
+ * to view a packet in event, but may block the response to the
+ * event if blocked has been called. This packet context can be used
+ * to react to the packet in event with a packet out.
  */
 public interface OpenFlowPacketContext {
 
diff --git a/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java b/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
index bc5b892..2403a72 100644
--- a/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
+++ b/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
@@ -3,6 +3,7 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.nio.ByteBuffer;
+import java.util.Collections;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -12,6 +13,8 @@
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.instructions.Instruction;
+import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction;
 import org.onlab.onos.net.packet.DefaultInboundPacket;
 import org.onlab.onos.net.packet.OutboundPacket;
 import org.onlab.onos.net.packet.PacketProvider;
@@ -19,12 +22,24 @@
 import org.onlab.onos.net.packet.PacketProviderService;
 import org.onlab.onos.net.provider.AbstractProvider;
 import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.openflow.controller.DefaultOpenFlowPacketContext;
 import org.onlab.onos.openflow.controller.Dpid;
 import org.onlab.onos.openflow.controller.OpenFlowController;
 import org.onlab.onos.openflow.controller.OpenFlowPacketContext;
+import org.onlab.onos.openflow.controller.OpenFlowSwitch;
 import org.onlab.onos.openflow.controller.PacketListener;
+import org.onlab.packet.Ethernet;
+import org.projectfloodlight.openflow.protocol.OFPacketOut;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
 import org.slf4j.Logger;
 
+import static org.onlab.onos.openflow.controller.RoleState.*;
+
+
 /**
  * Provider which uses an OpenFlow controller to detect network
  * infrastructure links.
@@ -68,9 +83,61 @@
 
     @Override
     public void emit(OutboundPacket packet) {
+        DeviceId devId = packet.sendThrough();
+        String scheme = devId.toString().split(":")[0];
+
+        if (!scheme.equals(this.id().scheme())) {
+            throw new IllegalArgumentException(
+                    "Don't know how to handle Device with scheme " + scheme);
+        }
+
+        Dpid dpid = Dpid.dpid(devId.uri());
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
+        if (sw == null) {
+            log.warn("Device {} isn't available?", devId);
+            return;
+        } else if (sw.getRole().equals(SLAVE)) {
+            log.warn("Can't write to Device {} as slave", devId);
+            return;
+        }
+
+        Ethernet eth = new Ethernet();
+        eth.deserialize(packet.data().array(), 0, packet.data().array().length);
+        OFPortDesc p = null;
+        for (Instruction inst : packet.treatment().instructions()) {
+            if (inst.type().equals(Instruction.Type.OUTPUT)) {
+                p = portDesc(((OutputInstruction) inst).port());
+                if (!sw.getPorts().contains(p)) {
+                    log.warn("Tried to write out non-existint port {}", p.getPortNo());
+                    continue;
+                }
+                OFPacketOut po = packetOut(sw, eth, p.getPortNo());
+                sw.sendMsg(po);
+            }
+        }
 
     }
 
+    private OFPortDesc portDesc(PortNumber port) {
+        OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc();
+        builder.setPortNo(OFPort.of((int) port.toLong()));
+
+        return builder.build();
+    }
+
+    private OFPacketOut packetOut(OpenFlowSwitch sw, Ethernet eth, OFPort out) {
+        OFPacketOut.Builder builder = sw.factory().buildPacketOut();
+        OFAction act = sw.factory().actions()
+                .buildOutput()
+                .setPort(out)
+                .build();
+        return builder
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setInPort(OFPort.NO_MASK)
+                .setActions(Collections.singletonList(act))
+                .setData(eth.serialize())
+                .build();
+    }
 
     /**
      * Internal Packet Provider implementation.