Make PIM hello interval configurable on a per-interface basis.

Change-Id: I7a0788be4445c7befbd947a3df893bcce1118bf5
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterface.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterface.java
index 30d08c8..ce28908 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterface.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterface.java
@@ -40,6 +40,7 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -68,6 +69,10 @@
     // Neighbor priority
     private int priority   = PIMHelloOption.DEFAULT_PRIORITY;
 
+    private final int helloInterval;
+
+    private long lastHello;
+
     // Our current genid
     private final int generationId;
 
@@ -88,19 +93,23 @@
      * @param packetService reference to the packet service
      */
     private PIMInterface(Interface intf,
-                        short holdTime,
-                        int priority,
-                        short propagationDelay,
-                        short overrideInterval,
-                        PacketService packetService) {
+                         int helloInterval,
+                         short holdTime,
+                         int priority,
+                         short propagationDelay,
+                         short overrideInterval,
+                         PacketService packetService) {
 
         onosInterface = intf;
         outputTreatment = createOutputTreatment();
+        this.helloInterval = helloInterval;
         this.holdtime = holdTime;
         this.packetService = packetService;
         IpAddress ourIp = getIpAddress();
         MacAddress mac = intf.mac();
 
+        lastHello = 0;
+
         generationId = new Random().nextInt();
 
         // Create a PIM Neighbor to represent ourselves for DR election.
@@ -232,6 +241,13 @@
      * result of a newly created interface.
      */
     public void sendHello() {
+        if (lastHello + TimeUnit.SECONDS.toMillis(helloInterval) >
+                System.currentTimeMillis()) {
+            return;
+        }
+
+        lastHello = System.currentTimeMillis();
+
         // Create the base PIM Packet and mark it a hello packet
         PIMPacket pimPacket = new PIMPacket(PIM.TYPE_HELLO);
 
@@ -401,6 +417,7 @@
     public static class Builder {
         private Interface intf;
         private PacketService packetService;
+        private int helloInterval = PIMInterfaceManager.DEFAULT_HELLO_INTERVAL;
         private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
         private int priority   = PIMHelloOption.DEFAULT_PRIORITY;
         private short propagationDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
@@ -429,6 +446,17 @@
         }
 
         /**
+         * Users the specified hello interval.
+         *
+         * @param helloInterval hello interval in seconds
+         * @return this PIM interface builder
+         */
+        public Builder withHelloInterval(int helloInterval) {
+            this.helloInterval = helloInterval;
+            return this;
+        }
+
+        /**
          * Uses the specified hold time.
          *
          * @param holdTime hold time in seconds
@@ -481,8 +509,8 @@
             checkArgument(intf != null, "Must provide an interface");
             checkArgument(packetService != null, "Must provide a packet service");
 
-            return new PIMInterface(intf, holdtime, priority, propagationDelay,
-                    overrideInterval, packetService);
+            return new PIMInterface(intf, helloInterval, holdtime, priority,
+                    propagationDelay, overrideInterval, packetService);
         }
 
     }
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfaceManager.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfaceManager.java
index d6205e5..248a233 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfaceManager.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PIMInterfaceManager.java
@@ -59,20 +59,19 @@
     private static final Class<PimInterfaceConfig> PIM_INTERFACE_CONFIG_CLASS = PimInterfaceConfig.class;
     private static final String PIM_INTERFACE_CONFIG_KEY = "pimInterface";
 
-    private static final int DEFAULT_TIMEOUT_TASK_PERIOD_MS = 250;
+    public static final int DEFAULT_HELLO_INTERVAL = 30; // seconds
+
+    private static final int DEFAULT_TASK_PERIOD_MS = 250;
 
     // Create a Scheduled Executor service for recurring tasks
     private final ScheduledExecutorService scheduledExecutorService =
             Executors.newScheduledThreadPool(1);
 
-    // Wait for a bout 3 seconds before sending the initial hello messages.
-    // TODO: make this tunnable.
-    private final long initialHelloDelay = 3;
+    private final long initialHelloDelay = 1000;
 
-    // Send PIM hello packets: 30 seconds.
-    private final long pimHelloPeriod = 30;
+    private final long pimHelloPeriod = DEFAULT_TASK_PERIOD_MS;
 
-    private final int timeoutTaskPeriod = DEFAULT_TIMEOUT_TASK_PERIOD_MS;
+    private final int timeoutTaskPeriod = DEFAULT_TASK_PERIOD_MS;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
@@ -121,7 +120,7 @@
         scheduledExecutorService.scheduleAtFixedRate(
                 SafeRecurringTask.wrap(
                         () -> pimInterfaces.values().forEach(PIMInterface::sendHello)),
-                initialHelloDelay, pimHelloPeriod, TimeUnit.SECONDS);
+                initialHelloDelay, pimHelloPeriod, TimeUnit.MILLISECONDS);
 
         // Schedule task to periodically time out expired neighbors
         scheduledExecutorService.scheduleAtFixedRate(
@@ -194,6 +193,9 @@
                 .withPacketService(packetService)
                 .withInterface(intf);
 
+        if (config.getHelloInterval().isPresent()) {
+            builder.withHelloInterval(config.getHelloInterval().get());
+        }
         if (config.getHoldTime().isPresent()) {
             builder.withHoldTime(config.getHoldTime().get());
         }
diff --git a/apps/pim/src/main/java/org/onosproject/pim/impl/PimInterfaceConfig.java b/apps/pim/src/main/java/org/onosproject/pim/impl/PimInterfaceConfig.java
index 8d8ad78..48969c3 100644
--- a/apps/pim/src/main/java/org/onosproject/pim/impl/PimInterfaceConfig.java
+++ b/apps/pim/src/main/java/org/onosproject/pim/impl/PimInterfaceConfig.java
@@ -28,6 +28,7 @@
 
     private static final String INTERFACE_NAME = "interfaceName";
     private static final String ENABLED = "enabled";
+    private static final String HELLO_INTERVAL = "helloInterval";
     private static final String HOLD_TIME = "holdTime";
     private static final String PRIORITY = "priority";
     private static final String PROPAGATION_DELAY = "propagationDelay";
@@ -53,6 +54,18 @@
     }
 
     /**
+     * Gets the hello interval of the interface.
+     *
+     * @return hello interval
+     */
+    public Optional<Integer> getHelloInterval() {
+        if (node.path(HELLO_INTERVAL).isMissingNode()) {
+            return Optional.empty();
+        }
+        return Optional.of(Integer.parseInt(node.path(HELLO_INTERVAL).asText()));
+    }
+
+    /**
      * Gets the HELLO hold time of the interface.
      *
      * @return hold time