Implement OFActionEnqueue (OpenFlow 1.0 only)

Realize the QUEUE Treatment using OpenFlow 1.0 Enqueue action.

The ConnectivityIntentCommand's --setQueue parameter is extended by a notion of
<port>/<queue> to support the difference from OpenFlow 1.3 Set-Queue, which is
not bound to a port.

Change-Id: I28cf70a7c004a1a3a14361e5186cfe42f09f1356
diff --git a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
index 62cf042..a33af76 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
@@ -33,6 +33,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -166,7 +167,8 @@
             required = false, multiValued = false)
     private String pushVlan = null;
 
-    @Option(name = "--setQueue", description = "Set Queue ID",
+    @Option(name = "--setQueue", description = "Set Queue ID (for OpenFlow 1.0, " +
+            "also the port has to be specified, i.e., <port>/<queue>",
             required = false, multiValued = false)
     private String setQueue = null;
 
@@ -332,7 +334,15 @@
             emptyTreatment = false;
         }
         if (!isNullOrEmpty(setQueue)) {
-            treatmentBuilder.setQueue(Long.parseLong(setQueue));
+            // OpenFlow 1.0 notation (for ENQUEUE): <port>/<queue>
+            if (setQueue.contains("/")) {
+                String[] queueConfig = setQueue.split("/");
+                PortNumber port = PortNumber.portNumber(Long.parseLong(queueConfig[0]));
+                long queueId = Long.parseLong(queueConfig[1]);
+                treatmentBuilder.setQueue(queueId, port);
+            } else {
+                treatmentBuilder.setQueue(Long.parseLong(setQueue));
+            }
             emptyTreatment = false;
         }
 
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
index 6beeecc..4615a82 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
@@ -388,7 +388,12 @@
 
         @Override
         public Builder setQueue(long queueId) {
-            return add(Instructions.setQueue(queueId));
+            return add(Instructions.setQueue(queueId, null));
+        }
+
+        @Override
+        public Builder setQueue(long queueId, PortNumber port) {
+            return add(Instructions.setQueue(queueId, port));
         }
 
         @Override
diff --git a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
index b14ab99..f1a676a 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
@@ -271,6 +271,15 @@
         Builder setQueue(long queueId);
 
         /**
+         * Sets the Queue ID for a specific port.
+         *
+         * @param queueId a queue ID
+         * @param port a port number
+         * @return a treatment builder
+         */
+        Builder setQueue(long queueId, PortNumber port);
+
+        /**
          * Sets a meter to be used by this flow.
          *
          * @param meterId a meter id
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
index aad407c..bcc8bd8 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.net.flow.instructions;
 
+import com.google.common.base.MoreObjects;
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
@@ -99,11 +100,12 @@
      * Creates a set-queue instruction.
      *
      * @param queueId Queue Id
+     * @param port Port number
      * @return set-queue instruction
      */
-    public static SetQueueInstruction setQueue(final long queueId) {
+    public static SetQueueInstruction setQueue(final long queueId, final PortNumber port) {
         checkNotNull(queueId, "queue ID cannot be null");
-        return new SetQueueInstruction(queueId);
+        return new SetQueueInstruction(queueId, port);
     }
 
     public static MeterInstruction meterTraffic(final MeterId meterId) {
@@ -655,15 +657,26 @@
      */
     public static final class SetQueueInstruction implements Instruction {
         private final long queueId;
+        private final PortNumber port;
 
         private SetQueueInstruction(long queueId) {
             this.queueId = queueId;
+            this.port = null;
+        }
+
+        private SetQueueInstruction(long queueId, PortNumber port) {
+            this.queueId = queueId;
+            this.port = port;
         }
 
         public long queueId() {
             return queueId;
         }
 
+        public PortNumber port() {
+            return port;
+        }
+
         @Override
         public Type type() {
             return Type.QUEUE;
@@ -671,13 +684,18 @@
 
         @Override
         public String toString() {
-            return toStringHelper(type().toString())
-                    .add("queueId", queueId).toString();
+            MoreObjects.ToStringHelper toStringHelper = toStringHelper(type().toString());
+            toStringHelper.add("queueId", queueId);
+
+            if (port() != null) {
+                toStringHelper.add("port", port);
+            }
+            return toStringHelper.toString();
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(type().ordinal(), queueId);
+            return Objects.hash(type().ordinal(), queueId, port);
         }
 
         @Override
@@ -687,7 +705,7 @@
             }
             if (obj instanceof SetQueueInstruction) {
                 SetQueueInstruction that = (SetQueueInstruction) obj;
-                return Objects.equals(queueId, that.queueId);
+                return Objects.equals(queueId, that.queueId) && Objects.equals(port, that.port);
 
             }
             return false;
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index 4d5b6b2..c591f47 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -50,6 +50,7 @@
 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
+import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue;
 import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
 import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
@@ -346,6 +347,10 @@
                     OFActionSetQueue setQueue = (OFActionSetQueue) act;
                     builder.setQueue(setQueue.getQueueId());
                     break;
+                case ENQUEUE:
+                    OFActionEnqueue enqueue = (OFActionEnqueue) act;
+                    builder.setQueue(enqueue.getQueueId(), PortNumber.portNumber(enqueue.getPort().getPortNumber()));
+                    break;
                 case STRIP_VLAN:
                 case POP_VLAN:
                     builder.popVlan();
@@ -364,7 +369,6 @@
                 case SET_NW_TOS:
                 case SET_NW_TTL:
 
-                case ENQUEUE:
                 default:
                     log.warn("Action type {} not yet implemented.", act.getType());
             }
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
index c789841..6c4ee4d 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
@@ -22,6 +22,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.flow.instructions.Instructions.SetQueueInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
@@ -35,6 +36,7 @@
 import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.MacAddress;
@@ -167,6 +169,16 @@
                 }
                 acts.add(action.build());
                 break;
+            case QUEUE:
+                SetQueueInstruction queue = (SetQueueInstruction) i;
+                if (queue.port() == null) {
+                    log.warn("Required argument 'port' undefined for OFActionEnqueue");
+                }
+                OFActionEnqueue.Builder queueBuilder = factory().actions().buildEnqueue()
+                        .setQueueId(queue.queueId())
+                        .setPort(OFPort.ofInt((int) queue.port().toLong()));
+                acts.add(queueBuilder.build());
+                break;
             case L0MODIFICATION:
             case GROUP:
             case TABLE: