Ensures internal link state is up to date on a restarted ONOS instance. Also fixes an NPE in FpmManager.

Change-Id: Idb0df0ef961c6bb2f3b2c3dc72957af8a4987e1b
diff --git a/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java b/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java
index e290aea..455e546 100644
--- a/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java
+++ b/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java
@@ -226,8 +226,8 @@
 
         appId = coreService.registerApplication(APP_NAME, peers::destroy);
 
-        clusterService.addListener(clusterListener);
         asyncLock = storageService.lockBuilder().withName(LOCK_NAME).build();
+        clusterService.addListener(clusterListener);
 
         log.info("Started");
     }
@@ -760,6 +760,7 @@
         }
     }
 
+    @Override
     public void pushFpmRoutes() {
         Set<Route> routes = fpmRoutes.values().stream()
                 .map(Map::entrySet).flatMap(Set::stream).map(Map.Entry::getValue)
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index b22a89a..7e887eb 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -268,9 +268,9 @@
             }
             currentEcmpSpgMap.entrySet().forEach(entry -> {
                 updatedEcmpSpgMap.put(entry.getKey(), entry.getValue());
-                if (log.isDebugEnabled()) {
-                    log.debug("Root switch: {}", entry.getKey());
-                    log.debug("  Current/Existing SPG: {}", entry.getValue());
+                if (log.isTraceEnabled()) {
+                    log.trace("Root switch: {}", entry.getKey());
+                    log.trace("  Current/Existing SPG: {}", entry.getValue());
                 }
             });
             Set<EdgePair> edgePairs = new HashSet<>();
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/LinkHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/LinkHandler.java
index 2961e50..c643991 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/LinkHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/LinkHandler.java
@@ -71,6 +71,7 @@
                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
                 .build();
         log.trace("Current size {}", downedPortStore.size());
+        init();
     }
 
     /**
@@ -85,6 +86,15 @@
     }
 
     /**
+     * Initialize LinkHandler.
+     */
+    private void init() {
+        log.info("Loading stored links");
+        srManager.linkService.getActiveLinks()
+                .forEach(link -> processLinkAdded(link));
+    }
+
+    /**
      * Preprocessing of added link before being sent for route-path handling.
      * Also performs post processing of link.
      *
@@ -95,8 +105,8 @@
         if (!isLinkValid(link)) {
             return;
         }
-        if (!srManager.deviceConfiguration
-                .isConfigured(link.src().deviceId())) {
+        if (srManager.deviceConfiguration == null ||
+                !srManager.deviceConfiguration.isConfigured(link.src().deviceId())) {
             updateSeenLink(link, true);
             // XXX revisit - what about devicePortMap
             log.warn("Source device of this link is not configured.. "
@@ -262,6 +272,10 @@
             return false;
         }
         DeviceConfiguration devConfig = srManager.deviceConfiguration;
+        if (devConfig == null) {
+            log.warn("Cannot check validity of link without device config");
+            return true;
+        }
         try {
             if (!devConfig.isEdgeDevice(srcId)
                     && !devConfig.isEdgeDevice(dstId)) {
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 7bfa29b..f661add 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -136,6 +136,8 @@
 import static com.google.common.base.Preconditions.checkState;
 import static org.onlab.packet.Ethernet.TYPE_ARP;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REGISTERED;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UNREGISTERED;
 
 /**
  * Segment routing manager.
@@ -425,7 +427,7 @@
         cfgService.registerConfigFactory(xConnectConfigFactory);
         cfgService.registerConfigFactory(mcastConfigFactory);
         cfgService.registerConfigFactory(pwaasConfigFactory);
-
+        log.info("Configuring network before adding listeners");
         cfgListener.configureNetwork();
 
         hostService.addListener(hostListener);
@@ -1055,9 +1057,6 @@
                     }
                     if (event.type() == LinkEvent.Type.LINK_ADDED ||
                             event.type() == LinkEvent.Type.LINK_UPDATED) {
-                        // Note: do not update seenLinks here, otherwise every
-                        // link, even one seen for the first time, will be appear
-                        // to be a previously seen link
                         linkHandler.processLinkAdded((Link) event.subject());
                     } else if (event.type() == LinkEvent.Type.LINK_REMOVED) {
                         linkHandler.processLinkRemoved((Link) event.subject());
@@ -1258,8 +1257,10 @@
 
     private void createOrUpdateDeviceConfiguration() {
         if (deviceConfiguration == null) {
+            log.info("Creating new DeviceConfiguration");
             deviceConfiguration = new DeviceConfiguration(this);
         } else {
+            log.info("Updating DeviceConfiguration");
             deviceConfiguration.updateConfig();
         }
     }
@@ -1295,6 +1296,7 @@
          * Reads network config and initializes related data structure accordingly.
          */
         void configureNetwork() {
+            log.info("Configuring network ...");
             createOrUpdateDeviceConfiguration();
 
             arpHandler = new ArpHandler(srManager);
@@ -1310,6 +1312,7 @@
                                               tunnelHandler, policyStore);
             // add a small delay to absorb multiple network config added notifications
             if (!programmingScheduled.get()) {
+                log.info("Buffering config calls for {} secs", PROGRAM_DELAY);
                 programmingScheduled.set(true);
                 executorService.schedule(new ConfigChange(), PROGRAM_DELAY,
                                          TimeUnit.SECONDS);
@@ -1368,6 +1371,7 @@
                     default:
                         break;
                 }
+                log.info("App config event .. configuring network");
                 configureNetwork();
             } else if (event.configClass().equals(XConnectConfig.class)) {
                 checkState(xConnectHandler != null, "XConnectHandler is not initialized");
@@ -1404,21 +1408,29 @@
 
         @Override
         public boolean isRelevant(NetworkConfigEvent event) {
-            if (event.configClass().equals(SegmentRoutingDeviceConfig.class) ||
-                    event.configClass().equals(SegmentRoutingAppConfig.class) ||
-                    event.configClass().equals(InterfaceConfig.class) ||
-                    event.configClass().equals(XConnectConfig.class) ||
-                    event.configClass().equals(PwaasConfig.class)) {
-                return true;
+            if (event.type() == CONFIG_REGISTERED ||
+                    event.type() == CONFIG_UNREGISTERED) {
+                log.debug("Ignore event {} due to type mismatch", event);
+                return false;
             }
-            log.debug("Ignore irrelevant event class {}", event.configClass().getName());
-            return false;
+
+            if (!event.configClass().equals(SegmentRoutingDeviceConfig.class) &&
+                    !event.configClass().equals(SegmentRoutingAppConfig.class) &&
+                    !event.configClass().equals(InterfaceConfig.class) &&
+                    !event.configClass().equals(XConnectConfig.class) &&
+                    !event.configClass().equals(PwaasConfig.class)) {
+                log.debug("Ignore event {} due to class mismatch", event);
+                return false;
+            }
+
+            return true;
         }
 
         private final class ConfigChange implements Runnable {
             @Override
             public void run() {
                 programmingScheduled.set(false);
+                log.info("Reacting to config changes after buffer delay");
                 for (Device device : deviceService.getDevices()) {
                     processDeviceAdded(device);
                 }