NullPacketProvider that generates PacketEvents at a specified rate
and behaves as a sink for network-bound events.

Reference: ONOS-665

Change-Id: I4e84929028094213baf0e9c27cef3a0b62ca1afb
diff --git a/providers/null/link/src/main/java/org/onosproject/provider/nil/link/impl/NullLinkProvider.java b/providers/null/link/src/main/java/org/onosproject/provider/nil/link/impl/NullLinkProvider.java
index 947b620..8d9404e 100644
--- a/providers/null/link/src/main/java/org/onosproject/provider/nil/link/impl/NullLinkProvider.java
+++ b/providers/null/link/src/main/java/org/onosproject/provider/nil/link/impl/NullLinkProvider.java
@@ -30,6 +30,7 @@
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -108,9 +109,6 @@
         providerService = providerRegistry.register(this);
         deviceService.addListener(linkProvider);
         modified(context);
-        if (flicker) {
-            linkDriver.submit(new LinkDriver());
-        }
         log.info("started");
     }
 
@@ -131,6 +129,7 @@
         log.info("stopped");
     }
 
+    @Modified
     public void modified(ComponentContext context) {
         if (context == null) {
             log.info("No configs, using defaults: flicker={}, eventRate={}",
diff --git a/providers/null/packet/pom.xml b/providers/null/packet/pom.xml
index 5a271b9..fd7c776 100644
--- a/providers/null/packet/pom.xml
+++ b/providers/null/packet/pom.xml
@@ -31,4 +31,11 @@
 
     <description>ONOS Null packet provider</description>
 
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+    </dependencies>
+
 </project>
diff --git a/providers/null/packet/src/main/java/org/onosproject/provider/nil/packet/impl/NullPacketProvider.java b/providers/null/packet/src/main/java/org/onosproject/provider/nil/packet/impl/NullPacketProvider.java
new file mode 100644
index 0000000..e70076f
--- /dev/null
+++ b/providers/null/packet/src/main/java/org/onosproject/provider/nil/packet/impl/NullPacketProvider.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2015 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.provider.nil.packet.impl;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.delay;
+import static org.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.nio.ByteBuffer;
+import java.util.Dictionary;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProvider;
+import org.onosproject.net.packet.PacketProviderRegistry;
+import org.onosproject.net.packet.PacketProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+/**
+ * Provider which 1) intercepts network-bound messages from the core, and 2)
+ * generates PacketEvents at some tunable rate. To be used for benchmarking
+ * only.
+ */
+@Component(immediate = true)
+public class NullPacketProvider extends AbstractProvider implements
+        PacketProvider {
+
+    private final Logger log = getLogger(getClass());
+
+    // Default packetEvent generation rate (in packets/sec)
+    // If 0, we are just a sink for network-bound packets
+    private static final int DEFAULT_RATE = 5;
+    // arbitrary host "destination"
+    private static final int DESTHOST = 5;
+
+    private PacketProviderService providerService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketProviderRegistry providerRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    // Rate to generate PacketEvents, per second
+    @Property(name = "pktRate", intValue = DEFAULT_RATE,
+            label = "Rate of PacketEvent generation")
+    private int pktRate = DEFAULT_RATE;
+
+    private ExecutorService packetDriver = Executors.newFixedThreadPool(1,
+            namedThreads("null-packet-driver"));
+
+    public NullPacketProvider() {
+        super(new ProviderId("null", "org.onosproject.provider.nil"));
+    }
+
+    @Activate
+    public void activate(ComponentContext context) {
+        providerService = providerRegistry.register(this);
+        if (!modified(context)) {
+            packetDriver.submit(new PacketDriver());
+        }
+        log.info("started");
+    }
+
+    @Deactivate
+    public void deactivate(ComponentContext context) {
+        try {
+            packetDriver.awaitTermination(1000, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            log.error("PacketDriver did not terminate");
+        }
+        packetDriver.shutdownNow();
+        providerRegistry.unregister(this);
+        log.info("stopped");
+    }
+
+    @Modified
+    public boolean modified(ComponentContext context) {
+        if (context == null) {
+            log.info("No configuration change, using defaults: pktRate={}",
+                    DEFAULT_RATE);
+            return false;
+        }
+        Dictionary<?, ?> properties = context.getProperties();
+
+        int newRate;
+        try {
+            String s = String.valueOf(properties.get("pktRate"));
+            newRate = isNullOrEmpty(s) ? pktRate : Integer.valueOf(s);
+        } catch (Exception e) {
+            log.warn(e.getMessage());
+            newRate = pktRate;
+        }
+
+        if (newRate != pktRate) {
+            pktRate = newRate;
+            packetDriver.submit(new PacketDriver());
+            log.info("Using new settings: pktRate={}", pktRate);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void emit(OutboundPacket packet) {
+        // We don't have a network to emit to. Keep a counter here, maybe?
+    }
+
+    /**
+     * Generates packet events at a given rate.
+     */
+    private class PacketDriver implements Runnable {
+
+        // time between event firing, in milliseconds
+        int pktInterval;
+        // filler echo request
+        ICMP icmp;
+        Ethernet eth;
+
+        public PacketDriver() {
+            pktInterval = 1000 / pktRate;
+            icmp = new ICMP();
+            icmp.setIcmpType((byte) 8).setIcmpCode((byte) 0)
+                    .setChecksum((short) 0);
+            eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV4);
+            eth.setPayload(icmp);
+        }
+
+        @Override
+        public void run() {
+            log.info("PacketDriver started");
+            while (!packetDriver.isShutdown()) {
+                for (Device dev : deviceService.getDevices()) {
+                    sendEvents(dev);
+                }
+            }
+        }
+
+        private void sendEvents(Device dev) {
+            // make it look like things came from ports attached to hosts
+            for (int i = 0; i < 4; i++) {
+                eth.setSourceMACAddress("00:00:10:00:00:0" + i)
+                        .setDestinationMACAddress("00:00:10:00:00:0" + DESTHOST);
+                InboundPacket inPkt = new DefaultInboundPacket(
+                        new ConnectPoint(dev.id(), PortNumber.portNumber(i)),
+                        eth, ByteBuffer.wrap(eth.serialize()));
+                PacketContext pctx = new NullPacketContext(
+                        System.currentTimeMillis(), inPkt, null, false);
+                providerService.processPacket(pctx);
+                delay(pktInterval);
+            }
+        }
+
+    }
+
+    /**
+     * Minimal PacketContext to make core + applications happy.
+     */
+    private class NullPacketContext extends DefaultPacketContext {
+
+        public NullPacketContext(long time, InboundPacket inPkt,
+                OutboundPacket outPkt, boolean block) {
+            super(time, inPkt, outPkt, block);
+        }
+
+        @Override
+        public void send() {
+            // We don't send anything out.
+        }
+
+    }
+
+}
diff --git a/tools/package/etc/org.onosproject.provider.nil.packet.impl.NullPacketProvider.cfg b/tools/package/etc/org.onosproject.provider.nil.packet.impl.NullPacketProvider.cfg
new file mode 100644
index 0000000..db4342c
--- /dev/null
+++ b/tools/package/etc/org.onosproject.provider.nil.packet.impl.NullPacketProvider.cfg
@@ -0,0 +1,4 @@
+#
+# Uncomment and tweak to tune the rate of Packet events (per second)
+#
+# pktRate = 100