[VOL-3922] Dynamic configuration of the required drivers property to ensure correct driver is assigned to the device at all times, both instance restart and application update

Change-Id: I1c54d2e260aecd1e33b15c06bcb1f6c857603355
diff --git a/impl/src/main/java/org/opencord/olt/driver/OltDriversLoader.java b/impl/src/main/java/org/opencord/olt/driver/OltDriversLoader.java
index a91621f..f6714d4 100644
--- a/impl/src/main/java/org/opencord/olt/driver/OltDriversLoader.java
+++ b/impl/src/main/java/org/opencord/olt/driver/OltDriversLoader.java
@@ -16,18 +16,57 @@
 
 package org.opencord.olt.driver;
 
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
 import org.onosproject.net.driver.AbstractDriverLoader;
+import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
+import java.util.Dictionary;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.opencord.olt.impl.OsgiPropertyConstants.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Loader for olt device drivers.
  */
-@Component(immediate = true)
+@Component(immediate = true, property = {
+        REQUIRED_DRIVERS_PROPERTY_DELAY + ":Integer=" + REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT
+})
 public class OltDriversLoader extends AbstractDriverLoader {
 
+    public static final String DRIVER_REGISTRY_MANAGER =
+            "org.onosproject.net.driver.impl.DriverRegistryManager";
+    public static final String REQUIRED_DRIVERS = "requiredDrivers";
+    public static final String VOLTHA_DRIVER_NAME = "voltha";
+    public static final String DRIVER_UPDATE_LEADSHIP_TOPIC = "driver-update";
+    public static final String OLT_DRIVERS_LOADER = "org.opencord.olt.driver.OltDriversLoader";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    private ComponentConfigService compCfgService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ClusterService clusterService;
+
+    /**
+     * Default amounts of eapol retry.
+     **/
+    protected int requiredDriversPropertyDelay = REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT;
+
     private final Logger log = getLogger(getClass());
 
     public OltDriversLoader() {
@@ -36,11 +75,102 @@
 
     @Override
     public void activate() {
+        log.info("Activating OLT Driver loader");
+        compCfgService.registerProperties(getClass());
+        //The AbstractDriversLoader does not pass the context, to avoid changes to all
+        // inheriting classes getting the property differently
+        ConfigProperty requiredDriversPropertyDelayNew =
+                compCfgService.getProperty(OLT_DRIVERS_LOADER, REQUIRED_DRIVERS_PROPERTY_DELAY);
+        requiredDriversPropertyDelay = requiredDriversPropertyDelayNew == null ?
+                REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT : requiredDriversPropertyDelayNew.asInteger();
+        log.info("OLT Driver loader requiredDriversPropertyDelay: {}", requiredDriversPropertyDelay);
         super.activate();
+        // Verify if this node is the leader
+        NodeId leader = leadershipService.runForLeadership(DRIVER_UPDATE_LEADSHIP_TOPIC).leaderNodeId();
+        if (clusterService.getLocalNode().id().equals(leader)) {
+            Thread thread = new Thread(() -> {
+                // Sleep is needed to allow driver correct initialization and
+                // flow objective cache invalidation.
+                try {
+                    TimeUnit.SECONDS.sleep(requiredDriversPropertyDelay);
+                } catch (InterruptedException e) {
+                    log.error("Interrupted thread while activating", e);
+                }
+                String currentRequiredDrivers =
+                        compCfgService.getProperty(DRIVER_REGISTRY_MANAGER, REQUIRED_DRIVERS)
+                                .asString();
+                //insertion of voltha in the required drivers
+                if (!currentRequiredDrivers.contains(VOLTHA_DRIVER_NAME)) {
+                    String updatedRequiredDrivers = currentRequiredDrivers;
+                    if (!updatedRequiredDrivers.endsWith(",")) {
+                        updatedRequiredDrivers = updatedRequiredDrivers + ",";
+                    }
+                    updatedRequiredDrivers = updatedRequiredDrivers + VOLTHA_DRIVER_NAME;
+                    compCfgService.setProperty(DRIVER_REGISTRY_MANAGER,
+                                                  REQUIRED_DRIVERS, updatedRequiredDrivers);
+                    log.debug("Added voltha driver to required drivers {}",
+                              updatedRequiredDrivers);
+                }
+            });
+            thread.start();
+        }
+    }
+
+    @Modified
+    public void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+        try {
+            String requiredDriversPropertyDelayNew = get(properties, REQUIRED_DRIVERS_PROPERTY_DELAY);
+            requiredDriversPropertyDelay = isNullOrEmpty(requiredDriversPropertyDelayNew) ?
+                    REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT :
+                    Integer.parseInt(requiredDriversPropertyDelayNew.trim());
+
+            log.info("OLT Driver loader requiredDriversPropertyDelay: {}", requiredDriversPropertyDelay);
+
+        } catch (Exception e) {
+            log.error("Error while modifying the properties", e);
+            requiredDriversPropertyDelay = REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT;
+        }
     }
 
     @Override
     public void deactivate() {
+        log.info("Deactivating OLT Driver loader");
+        //The AbstractDriversLoader does not pass the context, to avoid changes to all
+        // inheriting classes getting the property differently
+        ConfigProperty requiredDriversPropertyDelayNew =
+                compCfgService.getProperty(OLT_DRIVERS_LOADER, REQUIRED_DRIVERS_PROPERTY_DELAY);
+        requiredDriversPropertyDelay = requiredDriversPropertyDelayNew == null ?
+                REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT : requiredDriversPropertyDelayNew.asInteger();
+        log.info("OLT Driver loader requiredDriversPropertyDelay: {}", requiredDriversPropertyDelay);
+        NodeId leader = leadershipService.runForLeadership(DRIVER_UPDATE_LEADSHIP_TOPIC).leaderNodeId();
+        if (clusterService.getLocalNode().id().equals(leader)) {
+            String currentRequiredDrivers =
+                    compCfgService.getProperty(DRIVER_REGISTRY_MANAGER, REQUIRED_DRIVERS)
+                            .asString();
+            //removal of voltha from the required driver
+            if (currentRequiredDrivers.contains(VOLTHA_DRIVER_NAME)) {
+                String updatedRequiredDrivers = currentRequiredDrivers.replace(VOLTHA_DRIVER_NAME, "");
+                //handling the case where `voltha` was not the last required driver in the list
+                if (updatedRequiredDrivers.contains(",,")) {
+                    updatedRequiredDrivers = updatedRequiredDrivers.replace(",,", ",");
+                }
+                if (updatedRequiredDrivers.endsWith(",")) {
+                    updatedRequiredDrivers = updatedRequiredDrivers.substring(0, updatedRequiredDrivers.length() - 1);
+                }
+                compCfgService.setProperty(DRIVER_REGISTRY_MANAGER,
+                                           REQUIRED_DRIVERS, updatedRequiredDrivers);
+                log.debug("Removed voltha from required drivers {}", updatedRequiredDrivers);
+            }
+        }
+        // Sleep is needed to allow proper property sharing across the instances through accumulator
+        // of component config manager
+        try {
+            TimeUnit.SECONDS.sleep(requiredDriversPropertyDelay);
+        } catch (InterruptedException e) {
+            log.error("Interrupted while de-activating", e);
+        }
+        compCfgService.unregisterProperties(getClass(), false);
         super.deactivate();
     }
 }
diff --git a/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java b/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
index 84a56ca..5232eb8 100644
--- a/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
+++ b/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
@@ -161,6 +161,8 @@
             accumulator = new ObjectiveAccumulator(context.accumulatorMaxObjectives(),
                                                    context.accumulatorMaxBatchMillis(),
                                                    context.accumulatorMaxIdleMillis());
+        } else {
+            log.debug("Olt Pipeliner accumulator is disabled, processing immediately");
         }
 
 
@@ -1213,10 +1215,12 @@
             GroupKey key = event.subject().appCookie();
             NextObjective obj = pendingGroups.getIfPresent(key);
             if (obj == null) {
-                log.debug("No pending group for {}, moving on", key);
+                if (log.isTraceEnabled()) {
+                    log.trace("No pending group for {}, moving on", key);
+                }
                 return;
             }
-            log.trace("Event {} for group {}, handling pending" +
+            log.debug("Event {} for group {}, handling pending" +
                               "NextGroup {}", event.type(), key, obj.id());
             if (event.type() == GroupEvent.Type.GROUP_ADDED ||
                     event.type() == GroupEvent.Type.GROUP_UPDATED) {
diff --git a/impl/src/main/java/org/opencord/olt/impl/Olt.java b/impl/src/main/java/org/opencord/olt/impl/Olt.java
index f822ae3..76f3013 100644
--- a/impl/src/main/java/org/opencord/olt/impl/Olt.java
+++ b/impl/src/main/java/org/opencord/olt/impl/Olt.java
@@ -41,6 +41,7 @@
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
 import org.onosproject.net.flowobjective.ForwardingObjective;
@@ -134,9 +135,14 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected DeviceService deviceService;
 
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
 
+    //Dependency on driver service is to ensure correct startup order
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected DriverService driverService;
+
     @Reference(cardinality = ReferenceCardinality.OPTIONAL,
             bind = "bindSadisService",
             unbind = "unbindSadisService",
@@ -1236,7 +1242,8 @@
         log.info("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
 
         if (deviceInfo != null) {
-            // This is an OLT device as per Sadis, we create flows for UNI and NNI ports
+            log.debug("Driver for device {} is {}", dev.id(),
+                     driverService.getDriver(dev.id()));
             for (Port p : deviceService.getPorts(dev.id())) {
                 if (PortNumber.LOCAL.equals(p.number()) || !p.isEnabled()) {
                     continue;
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java b/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
index caeab28..fed9546 100644
--- a/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
+++ b/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
@@ -246,7 +246,7 @@
 
         log.info("modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " +
                          "enableDhcpV6:{}, enableIgmpOnNni:{}, " +
-                         "enableEapol{}, enablePppoe{}, defaultTechProfileId: {}",
+                         "enableEapol:{}, enablePppoe:{}, defaultTechProfileId:{}",
                  enableDhcpOnNni, enableDhcpV4, enableDhcpV6,
                  enableIgmpOnNni, enableEapol,  enablePppoe,
                  defaultTechProfileId);
@@ -681,8 +681,8 @@
                     @Override
                     public void onSuccess(Objective objective) {
                         log.info("Eapol filter {} for {} on {}/{} with meter {}.",
-                                 objective.id(), fi.getDevId(), fi.getUniPort(),
-                                 (install) ? INSTALLED : REMOVED, mId);
+                                 objective.id(), (install) ? INSTALLED : REMOVED,
+                                 fi.getDevId(), fi.getUniPort(), mId);
                         if (filterFuture != null) {
                             filterFuture.complete(null);
                         }
diff --git a/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java b/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java
index 3d76ef6..1db37e5 100644
--- a/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java
+++ b/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java
@@ -59,4 +59,7 @@
 
     public static final String PROVISION_DELAY = "provisionDelay";
     public static final int PROVISION_DELAY_DEFAULT = 100;
+
+    public static final String REQUIRED_DRIVERS_PROPERTY_DELAY = "requiredDriversPropertyDelay";
+    public static final int REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT = 5;
 }