[FALCON] adding api to enable packet requests to a single device.

Change-Id: Id870c13ece658fe61f46194c1e795fb1d209a6a8
diff --git a/core/api/src/main/java/org/onosproject/net/packet/DefaultPacketRequest.java b/core/api/src/main/java/org/onosproject/net/packet/DefaultPacketRequest.java
index 605ab4b..3bddee4 100644
--- a/core/api/src/main/java/org/onosproject/net/packet/DefaultPacketRequest.java
+++ b/core/api/src/main/java/org/onosproject/net/packet/DefaultPacketRequest.java
@@ -19,9 +19,11 @@
 
 import org.onosproject.cluster.NodeId;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.TrafficSelector;
 
 import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Default implementation of a packet request.
@@ -31,22 +33,24 @@
     private final PacketPriority priority;
     private final ApplicationId appId;
     private final NodeId nodeId;
+    private final Optional<DeviceId> deviceId;
+
 
     /**
      * Creates a new packet request.
-     *
-     * @param selector  traffic selector
+     *  @param selector  traffic selector
      * @param priority  intercept priority
      * @param appId     application id
      * @param nodeId    identifier of node where request originated
+     * @param deviceId  device id
      */
     public DefaultPacketRequest(TrafficSelector selector, PacketPriority priority,
-                                ApplicationId appId,
-                                NodeId nodeId) {
+                                ApplicationId appId, NodeId nodeId, Optional<DeviceId> deviceId) {
         this.selector = selector;
         this.priority = priority;
         this.appId = appId;
         this.nodeId = nodeId;
+        this.deviceId = deviceId;
     }
 
     @Override
@@ -64,6 +68,10 @@
         return appId;
     }
 
+    public Optional<DeviceId> deviceId() {
+        return deviceId;
+    }
+
     @Override
     public NodeId nodeId() {
         return nodeId;
@@ -71,7 +79,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(selector, priority, appId, nodeId);
+        return Objects.hash(selector, priority, appId, nodeId, deviceId);
     }
 
     @Override
@@ -86,7 +94,8 @@
         return Objects.equals(this.selector, other.selector)
                 && Objects.equals(this.priority, other.priority)
                 && Objects.equals(this.appId, other.appId)
-                && Objects.equals(this.nodeId, other.nodeId);
+                && Objects.equals(this.nodeId, other.nodeId)
+                && Objects.equals(this.deviceId, other.deviceId);
     }
 
     @Override
@@ -95,6 +104,8 @@
                 .add("selector", selector)
                 .add("priority", priority)
                 .add("appId", appId)
-                .add("nodeId", nodeId).toString();
+                .add("nodeId", nodeId)
+                .add("applies to", deviceId.isPresent() ? deviceId.get() : "all")
+                .toString();
     }
 }
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/packet/PacketRequest.java b/core/api/src/main/java/org/onosproject/net/packet/PacketRequest.java
index 32f7cd5..78fe501 100644
--- a/core/api/src/main/java/org/onosproject/net/packet/PacketRequest.java
+++ b/core/api/src/main/java/org/onosproject/net/packet/PacketRequest.java
@@ -17,8 +17,11 @@
 
 import org.onosproject.cluster.NodeId;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.TrafficSelector;
 
+import java.util.Optional;
+
 /**
  * Represents a packet request made to devices.
  */
@@ -51,4 +54,12 @@
      * @return an node id
      */
     NodeId nodeId();
+
+    /**
+     * Obtains the optional device id.
+     *
+     * @return an optional containing a device id
+     */
+    Optional<DeviceId> deviceId();
+
 }
diff --git a/core/api/src/main/java/org/onosproject/net/packet/PacketService.java b/core/api/src/main/java/org/onosproject/net/packet/PacketService.java
index 2e7a1b9..d26956e 100644
--- a/core/api/src/main/java/org/onosproject/net/packet/PacketService.java
+++ b/core/api/src/main/java/org/onosproject/net/packet/PacketService.java
@@ -17,9 +17,11 @@
 
 import com.google.common.annotations.Beta;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.TrafficSelector;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Service for intercepting data plane packets and for emitting synthetic
@@ -66,9 +68,25 @@
      * @param priority the priority of the rule
      * @param appId    the application ID of the requester
      */
+    @Deprecated
     void requestPackets(TrafficSelector selector, PacketPriority priority,
                         ApplicationId appId);
 
+
+    /**
+     * Requests that packets matching the given selector are punted from the
+     * dataplane to the controller. If a deviceId is specified then the
+     * packet request is only installed at the device represented by that
+     * deviceId.
+     *
+     * @param selector the traffic selector used to match packets
+     * @param priority the priority of the rule
+     * @param appId    the application ID of the requester
+     * @param deviceId an optional deviceId
+     */
+    void requestPackets(TrafficSelector selector, PacketPriority priority,
+                        ApplicationId appId, Optional<DeviceId> deviceId);
+
     /**
      * Cancels previous packet requests for packets matching the given
      * selector to be punted from the dataplane to the controller.
@@ -77,10 +95,26 @@
      * @param priority the priority of the rule
      * @param appId    the application ID of the requester
      */
+    @Deprecated
     void cancelPackets(TrafficSelector selector, PacketPriority priority,
                        ApplicationId appId);
 
     /**
+     * Cancels previous packet requests for packets matching the given
+     * selector to be punted from the dataplane to the controller. If a
+     * deviceId is specified then the packet request is only withdrawn from
+     * the device represented by that deviceId.
+     *
+     * @param selector the traffic selector used to match packets
+     * @param priority the priority of the rule
+     * @param appId    the application ID of the requester
+     * @param deviceId an optional deviceId
+     */
+    void cancelPackets(TrafficSelector selector, PacketPriority priority,
+                       ApplicationId appId, Optional<DeviceId> deviceId);
+
+
+    /**
      * Returns list of all existing requests ordered by priority.
      *
      * @return list of existing packet requests
diff --git a/core/api/src/test/java/org/onosproject/net/packet/DefaultPacketRequestTest.java b/core/api/src/test/java/org/onosproject/net/packet/DefaultPacketRequestTest.java
index a7d07e8..9b4df54 100644
--- a/core/api/src/test/java/org/onosproject/net/packet/DefaultPacketRequestTest.java
+++ b/core/api/src/test/java/org/onosproject/net/packet/DefaultPacketRequestTest.java
@@ -24,6 +24,8 @@
 
 import com.google.common.testing.EqualsTester;
 
+import java.util.Optional;
+
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
@@ -42,27 +44,27 @@
             new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(),
                                      PacketPriority.CONTROL,
                                      NetTestTools.APP_ID,
-                                     NetTestTools.NODE_ID);
+                                     NetTestTools.NODE_ID, Optional.empty());
     private final DefaultPacketRequest sameAsacketRequest1 =
             new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(),
                                      PacketPriority.CONTROL,
                                      NetTestTools.APP_ID,
-                                     NetTestTools.NODE_ID);
+                                     NetTestTools.NODE_ID, Optional.empty());
     private final DefaultPacketRequest packetRequest2 =
             new DefaultPacketRequest(selector,
                                      PacketPriority.CONTROL,
                                      NetTestTools.APP_ID,
-                                     NetTestTools.NODE_ID);
+                                     NetTestTools.NODE_ID, Optional.empty());
     private final DefaultPacketRequest packetRequest3 =
             new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(),
                                      PacketPriority.REACTIVE,
                                      NetTestTools.APP_ID,
-                                     NetTestTools.NODE_ID);
+                                     NetTestTools.NODE_ID, Optional.empty());
     private final DefaultPacketRequest packetRequest4 =
             new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(),
                                      PacketPriority.CONTROL,
                                      new DefaultApplicationId(1, "foo"),
-                                     new NodeId("node1"));
+                                     new NodeId("node1"), Optional.empty());
 
     /**
      * Tests the operation of the equals(), toAstring() and hashCode() methods.
diff --git a/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java
index 2993ce6..c01dcc0 100644
--- a/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java
@@ -16,9 +16,11 @@
 package org.onosproject.net.packet;
 
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.TrafficSelector;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Test adapter for packet service.
@@ -43,11 +45,25 @@
     }
 
     @Override
-    public void requestPackets(TrafficSelector selector, PacketPriority priority, ApplicationId appId) {
+    public void requestPackets(TrafficSelector selector, PacketPriority priority,
+                               ApplicationId appId) {
     }
 
     @Override
-    public void cancelPackets(TrafficSelector selector, PacketPriority priority, ApplicationId appId) {
+    public void requestPackets(TrafficSelector selector, PacketPriority priority,
+                               ApplicationId appId, Optional<DeviceId> deviceId) {
+
+    }
+
+    @Override
+    public void cancelPackets(TrafficSelector selector, PacketPriority priority,
+                              ApplicationId appId) {
+    }
+
+    @Override
+    public void cancelPackets(TrafficSelector selector, PacketPriority priority,
+                              ApplicationId appId, Optional<DeviceId> deviceId) {
+
     }
 
     @Override
diff --git a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
index 79ac4b0..c0a2286 100644
--- a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
+++ b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
@@ -29,6 +29,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
@@ -60,6 +61,7 @@
 import org.slf4j.Logger;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -175,18 +177,49 @@
         checkNotNull(selector, "Selector cannot be null");
         checkNotNull(appId, "Application ID cannot be null");
 
-        PacketRequest request = new DefaultPacketRequest(selector, priority, appId, localNodeId);
+        PacketRequest request = new DefaultPacketRequest(selector, priority, appId,
+                                                         localNodeId, Optional.empty());
         store.requestPackets(request);
     }
 
     @Override
+    public void requestPackets(TrafficSelector selector, PacketPriority priority,
+                               ApplicationId appId, Optional<DeviceId> deviceId) {
+        checkPermission(PACKET_READ);
+        checkNotNull(selector, "Selector cannot be null");
+        checkNotNull(appId, "Application ID cannot be null");
+
+        PacketRequest request =
+                new DefaultPacketRequest(selector, priority, appId,
+                                         localNodeId, deviceId);
+
+        store.requestPackets(request);
+
+    }
+
+    @Override
     public void cancelPackets(TrafficSelector selector, PacketPriority priority,
                               ApplicationId appId) {
         checkPermission(PACKET_READ);
         checkNotNull(selector, "Selector cannot be null");
         checkNotNull(appId, "Application ID cannot be null");
 
-        PacketRequest request = new DefaultPacketRequest(selector, priority, appId, localNodeId);
+
+        PacketRequest request = new DefaultPacketRequest(selector, priority, appId,
+                                                         localNodeId, Optional.empty());
+        store.cancelPackets(request);
+    }
+
+    @Override
+    public void cancelPackets(TrafficSelector selector, PacketPriority priority,
+                              ApplicationId appId, Optional<DeviceId> deviceId) {
+        checkPermission(PACKET_READ);
+        checkNotNull(selector, "Selector cannot be null");
+        checkNotNull(appId, "Application ID cannot be null");
+
+        PacketRequest request = new DefaultPacketRequest(selector, priority,
+                                                         appId, localNodeId,
+                                                         deviceId);
         store.cancelPackets(request);
     }
 
@@ -203,7 +236,12 @@
     private void pushRulesToDevice(Device device) {
         log.debug("Pushing packet requests to device {}", device.id());
         for (PacketRequest request : store.existingRequests()) {
-            pushRule(device, request);
+            if (!request.deviceId().isPresent()) {
+                pushRule(device, request);
+            } else if (request.deviceId().get().equals(device.id())) {
+                pushRule(device, request);
+            }
+
         }
     }
 
@@ -332,6 +370,7 @@
 
     }
 
+
     /**
      * Internal callback from the packet store.
      */
@@ -343,12 +382,24 @@
 
         @Override
         public void requestPackets(PacketRequest request) {
-            pushToAllDevices(request);
+            DeviceId deviceid = request.deviceId().orElse(null);
+
+            if (deviceid != null) {
+                pushRule(deviceService.getDevice(deviceid), request);
+            } else {
+                pushToAllDevices(request);
+            }
         }
 
         @Override
         public void cancelPackets(PacketRequest request) {
-            removeFromAllDevices(request);
+            DeviceId deviceid = request.deviceId().orElse(null);
+
+            if (deviceid != null) {
+                removeRule(deviceService.getDevice(deviceid), request);
+            } else {
+                removeFromAllDevices(request);
+            }
         }
     }