[AETHER-76] Complete T3 offline mode

- For the performance improvement, T3 offline mode uses snapshots of the network states
called Network Information Base (NIB) instead of runtime interactions with ONOS core
during troubleshooting a Trellis system.
- Enables NIB to be manually filled with the followings via T3-load commands.
  - Static dump files of onos-diagnostics (t3-load-file).
  - Returns of API calls to live ONOS stores (t3-load-snapshot).
- Enables NIB to be auto-filled with live ONOS stores.
  - When T3-execution commands (e.g. pingall) found NIB is invalid.
- Partially tested with some mininet topos for Trellis
(https://github.com/opennetworkinglab/routing/tree/master/trellis).
- Usage instruction docs (https://docs.trellisfabric.org/troubleshooting.html).

Change-Id: I2bb546bdde454a034338cd896388fa0b37d868be
(cherry picked from commit c3803e7fad5fb28ecf3e83253f183a34936be4a0)
diff --git a/app/src/main/java/org/onosproject/t3/api/AbstractNib.java b/app/src/main/java/org/onosproject/t3/api/AbstractNib.java
new file mode 100644
index 0000000..652c974
--- /dev/null
+++ b/app/src/main/java/org/onosproject/t3/api/AbstractNib.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * 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.t3.api;
+
+/**
+ * Base abstraction of Network Information Base (NIB).
+ */
+public abstract class AbstractNib {
+
+    protected NibProfile nibProfile;
+
+    /**
+     * Sets a profile describing this NIB.
+     *
+     * @param nibProfile NIB profile
+     */
+    public void setProfile(NibProfile nibProfile) {
+        this.nibProfile = nibProfile;
+    }
+
+    /**
+     * Returns the current profile of this NIB.
+     *
+     * @return NIB profile
+     */
+    public NibProfile getProfile() {
+        return nibProfile;
+    }
+
+    /**
+     * Checks this NIB is valid.
+     *
+     * @return true (valid) only if this NIB is already filled with any source. false means invalid
+     */
+    public boolean isValid() {
+        if (nibProfile != null) {
+            return nibProfile.isValid();
+        } else {
+            return false;
+        }
+    }
+
+}
diff --git a/app/src/main/java/org/onosproject/t3/api/DeviceNib.java b/app/src/main/java/org/onosproject/t3/api/DeviceNib.java
index 618724a..aca8418 100644
--- a/app/src/main/java/org/onosproject/t3/api/DeviceNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/DeviceNib.java
@@ -32,7 +32,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.device.DeviceService} for offline data.
  */
-public class DeviceNib {
+public class DeviceNib extends AbstractNib {
 
     private Map<Device, Set<Port>> devicePortMap;
 
diff --git a/app/src/main/java/org/onosproject/t3/api/DriverNib.java b/app/src/main/java/org/onosproject/t3/api/DriverNib.java
index 4c88a20..edee901 100644
--- a/app/src/main/java/org/onosproject/t3/api/DriverNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/DriverNib.java
@@ -26,7 +26,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.driver.DriverService} for offline data.
  */
-public class DriverNib {
+public class DriverNib extends AbstractNib {
 
     private Map<DeviceId, String> deviceDriverMap;
 
diff --git a/app/src/main/java/org/onosproject/t3/api/EdgePortNib.java b/app/src/main/java/org/onosproject/t3/api/EdgePortNib.java
index f24668f..060f9c4 100644
--- a/app/src/main/java/org/onosproject/t3/api/EdgePortNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/EdgePortNib.java
@@ -28,7 +28,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.edge.EdgePortService} for offline data.
  */
-public class EdgePortNib {
+public class EdgePortNib extends AbstractNib {
 
     private Map<DeviceId, Set<ConnectPoint>> edgePorts;
 
diff --git a/app/src/main/java/org/onosproject/t3/api/FlowNib.java b/app/src/main/java/org/onosproject/t3/api/FlowNib.java
index aecb57c..b9f2226 100644
--- a/app/src/main/java/org/onosproject/t3/api/FlowNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/FlowNib.java
@@ -28,7 +28,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.flow.FlowRuleService} for offline data.
  */
-public class FlowNib {
+public class FlowNib extends AbstractNib {
 
     // TODO with method optimization, store into subdivided structures at the first load
     private Set<FlowEntry> flows;
diff --git a/app/src/main/java/org/onosproject/t3/api/GroupNib.java b/app/src/main/java/org/onosproject/t3/api/GroupNib.java
index 64d6575..b1fb563 100644
--- a/app/src/main/java/org/onosproject/t3/api/GroupNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/GroupNib.java
@@ -28,7 +28,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.group.GroupService} for offline data.
  */
-public class GroupNib {
+public class GroupNib extends AbstractNib {
 
     // TODO with method optimization, store into subdivided structures at the first load
     private Set<Group> groups;
diff --git a/app/src/main/java/org/onosproject/t3/api/HostNib.java b/app/src/main/java/org/onosproject/t3/api/HostNib.java
index 5b5eab2..2ba68d1 100644
--- a/app/src/main/java/org/onosproject/t3/api/HostNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/HostNib.java
@@ -31,7 +31,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.host.HostService} for offline data.
  */
-public class HostNib {
+public class HostNib extends AbstractNib {
 
     // TODO with method optimization, store into subdivided structures at the first load
     private Set<Host> hosts;
diff --git a/app/src/main/java/org/onosproject/t3/api/LinkNib.java b/app/src/main/java/org/onosproject/t3/api/LinkNib.java
index 02acb54..622a187 100644
--- a/app/src/main/java/org/onosproject/t3/api/LinkNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/LinkNib.java
@@ -28,7 +28,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.link.LinkService} for offline data.
  */
-public class LinkNib {
+public class LinkNib extends AbstractNib {
 
     // TODO with method optimization, store into subdivided structures at the first load
     private Set<Link> links;
diff --git a/app/src/main/java/org/onosproject/t3/api/MastershipNib.java b/app/src/main/java/org/onosproject/t3/api/MastershipNib.java
index 5101d52..733c590 100644
--- a/app/src/main/java/org/onosproject/t3/api/MastershipNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/MastershipNib.java
@@ -27,7 +27,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.mastership.MastershipService} for offline data.
  */
-public class MastershipNib {
+public class MastershipNib extends AbstractNib {
 
     private Map<DeviceId, NodeId> deviceMasterMap;
 
diff --git a/app/src/main/java/org/onosproject/t3/api/MulticastRouteNib.java b/app/src/main/java/org/onosproject/t3/api/MulticastRouteNib.java
index b9d106d..0bcdc1f 100644
--- a/app/src/main/java/org/onosproject/t3/api/MulticastRouteNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/MulticastRouteNib.java
@@ -29,7 +29,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.mcast.api.MulticastRouteService} for offline data.
  */
-public class MulticastRouteNib {
+public class MulticastRouteNib extends AbstractNib {
 
     private Map<McastRoute, McastRouteData> mcastRoutes;
 
diff --git a/app/src/main/java/org/onosproject/t3/api/NetworkConfigNib.java b/app/src/main/java/org/onosproject/t3/api/NetworkConfigNib.java
index 9624fb7..f3de961 100644
--- a/app/src/main/java/org/onosproject/t3/api/NetworkConfigNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/NetworkConfigNib.java
@@ -31,7 +31,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.net.config.NetworkConfigService} for offline data.
  */
-public class NetworkConfigNib {
+public class NetworkConfigNib extends AbstractNib {
 
     private static final Logger log = getLogger(NetworkConfigNib.class);
 
diff --git a/app/src/main/java/org/onosproject/t3/api/NibProfile.java b/app/src/main/java/org/onosproject/t3/api/NibProfile.java
new file mode 100644
index 0000000..146d98a
--- /dev/null
+++ b/app/src/main/java/org/onosproject/t3/api/NibProfile.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * 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.t3.api;
+
+import java.text.SimpleDateFormat;
+
+/**
+ * Basic descriptions about a single Network information Base (NIB) instance.
+ */
+public class NibProfile {
+    // default false means the contents of this NIB are empty at the instantiation
+    private boolean valid = false;
+    private String date;
+    private SourceType sourceType;
+
+    public enum SourceType {
+        /**
+         * Provided by dump files.
+         */
+        FILE,
+        /**
+         * Provided by a running system.
+         */
+        SNAPSHOT
+    }
+
+    public NibProfile(long date, SourceType sourceType) {
+        this.valid = true;
+        this.date = new SimpleDateFormat("dd-MM-yyyy hh:mm").format(date);
+        this.sourceType = sourceType;
+    }
+
+    /**
+     * Returns the validity state of this NIB.
+     *
+     * @return true once this profile is initialized
+     */
+    public boolean isValid() {
+        return valid;
+    }
+
+    /**
+     * Returns the time this NIB has been filled.
+     *
+     * @return string representation for the time
+     */
+    public String date() {
+        return date;
+    }
+
+    /**
+     * Returns the type of the source used to fill this NIB.
+     *
+     * @return source type
+     */
+    public SourceType sourceType() {
+        return sourceType;
+    }
+
+}
diff --git a/app/src/main/java/org/onosproject/t3/api/RouteNib.java b/app/src/main/java/org/onosproject/t3/api/RouteNib.java
index 3edabd3..01e6e61 100644
--- a/app/src/main/java/org/onosproject/t3/api/RouteNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/RouteNib.java
@@ -32,7 +32,7 @@
  * and supports alternative functions to
  * {@link org.onosproject.routeservice.RouteService} for offline data.
  */
-public class RouteNib {
+public class RouteNib extends AbstractNib {
 
     // TODO with method optimization, store into subdivided structures at the first load
     // unresolved Route is treated as ResolvedRoute with nextHopMac null
diff --git a/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java b/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
index 9838e04..3ab6052 100644
--- a/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
+++ b/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
@@ -36,7 +36,7 @@
      * Requests a static trace be performed between all hosts in the network, given a type of traffic.
      *
      * @param type the etherType of the traffic we want to trace.
-     * @return a trace result
+     * @return trace result
      */
     List<StaticPacketTrace> pingAll(EthType.EtherType type);
 
@@ -44,7 +44,7 @@
      * Requests a static trace be performed between all hosts in the network, given a type of traffic.
      *
      * @param type the etherType of the traffic we want to trace.
-     * @return a trace result
+     * @return trace result
      */
     Generator<Set<StaticPacketTrace>> pingAllGenerator(EthType.EtherType type);
 
@@ -52,7 +52,7 @@
      * Requests a static trace be performed for all mcast Routes in the network.
      *
      * @param vlanId the vlanId configured for multicast.
-     * @return a set of trace result yielded one by one.
+     * @return set of trace result yielded one by one.
      */
     Generator<Set<StaticPacketTrace>> traceMcast(VlanId vlanId);
 
@@ -62,7 +62,7 @@
      * @param sourceHost      source host
      * @param destinationHost destination host
      * @param type            the etherType of the traffic we want to trace.
-     * @return a trace result
+     * @return trace result
      */
     Set<StaticPacketTrace> trace(HostId sourceHost, HostId destinationHost, EthType.EtherType type);
 
@@ -72,7 +72,7 @@
      *
      * @param packet description of packet
      * @param in     point at which packet starts
-     * @return a trace result
+     * @return trace result
      */
     StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in);
 
@@ -80,20 +80,22 @@
      * Requests list of static trace to be performed for all mcast routes in the network.
      *
      * @param vlanId the vlan id configured for multicast
-     * @return a list of trace result
+     * @return list of trace result
      */
     List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId);
 
     /**
-     * Checks the availability of all NIBs of the manager.
+     * Checks the validity of NIBs applied to the manager.
      *
-     * @return true if any NIB objects is unavailable
+     * @return true only if all NIBs are in the valid state.
      */
-    boolean checkNibsUnavailable();
+    boolean checkNibValidity();
 
     /**
-     * Applies created NIBs to the manager.
+     * Returns a summary describing all the NIBs applied to the manager.
+     *
+     * @return string for the summary
      */
-    void applyNibs();
+    String printNibSummary();
 
 }
diff --git a/app/src/main/java/org/onosproject/t3/cli/NibLoader.java b/app/src/main/java/org/onosproject/t3/cli/NibLoader.java
new file mode 100644
index 0000000..40690ca
--- /dev/null
+++ b/app/src/main/java/org/onosproject/t3/cli/NibLoader.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * 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.t3.cli;
+
+import java.io.IOException;
+
+/**
+ * Common APIs for T3 CLI commands to load snapshots of the network states
+ * from {@link org.onosproject.t3.api.NibProfile.SourceType} and fill the NIB.
+ */
+public interface NibLoader {
+
+    /**
+     * Extracts flow-related information and fills the flow NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadFlowNib() throws IOException;
+
+    /**
+     * Extracts group-related information and fills the group NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadGroupNib() throws IOException;
+
+    /**
+     * Extracts host-related information and fills the host NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadHostNib() throws IOException;
+
+    /**
+     * Extracts link-related information and fills the link NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadLinkNib() throws IOException;
+
+    /**
+     * Extracts device-related information and fills the device NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadDeviceNib() throws IOException;
+
+    /**
+     * Extracts driver-related information and fills the driver NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadDriverNib() throws IOException;
+
+    /**
+     * Extracts mastership-related information and fills the mastership NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadMastershipNib() throws IOException;
+
+    /**
+     * Extracts edge port-related information and fills the edge port NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadEdgePortNib() throws IOException;
+
+    /**
+     * Extracts route-related information and fills the route NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadRouteNib() throws IOException;
+
+    /**
+     * Extracts network config-related information and fills the network configuration NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadNetworkConfigNib() throws IOException;
+
+    /**
+     * Extracts multicast route-related information and fills the multicast route NIB.
+     *
+     * @throws IOException  exception during possible file I/O
+     */
+    void loadMulticastRouteNib() throws IOException;
+
+}
diff --git a/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java b/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
index 26c814e..ddbdb38 100644
--- a/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
+++ b/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
@@ -42,6 +42,11 @@
     private static final String GROUP_BUCKET_FORMAT =
             "       id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
 
+    public static final String NIB_AUTOFILLED =
+            "*** NIB is invalid. Snapshots for the NIB have been auto-filled: ***";
+    public static final String NIB_TERMINATE =
+            "*** NIB is still invalid. You can manually load it via CLI commands for T3 load and try again ***";
+
     /**
      * Builds a string output for the given trace for a specific level of verbosity.
      *
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadFileCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadFileCommand.java
index 36c22d3..58b99d6 100644
--- a/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadFileCommand.java
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadFileCommand.java
@@ -58,8 +58,8 @@
 import org.onosproject.t3.api.MastershipNib;
 import org.onosproject.t3.api.MulticastRouteNib;
 import org.onosproject.t3.api.NetworkConfigNib;
+import org.onosproject.t3.api.NibProfile;
 import org.onosproject.t3.api.RouteNib;
-import org.onosproject.t3.api.TroubleshootService;
 import org.slf4j.Logger;
 
 import java.io.File;
@@ -76,20 +76,16 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Reads network states from JSON files of onos-diagnostics
- * and sets them to corresponding Network Information Bases (NIBs).
+ * T3 CLI command to load the NIB with dump files that represent snapshots of the network states.
  */
 @Service
 @Command(scope = "onos", name = "t3-load-file",
-        description = "Command to create a snapshot (cache) of network states called Network Information Bases (NIBs) "
-                + "from onos-diagnostics dump files")
-public class TroubleshootLoadFileCommand extends AbstractShellCommand {
+        description = "Load the NIB with onos-diagnostics dump files")
+public class TroubleshootLoadFileCommand
+        extends AbstractShellCommand implements NibLoader {
 
     private static final Logger log = getLogger(TroubleshootLoadFileCommand.class);
 
-    public static final String ERROR_NULL = "Some NIBs are not ready to trace. " +
-            "Make sure t3-troubleshoot-load-file is done correctly";
-
     @Argument(index = 0, name = "rootDir", description = "Specify the location of the directory " +
             "where the dump files of a given instance have been extracted (e.g. /tmp/onos-diags/127.0.0.1)",
             required = true, multiValued = false)
@@ -105,85 +101,254 @@
         print("Load target files in: %s", rootDir);
 
         try {
-            // fills each NIB (singleton) instance with the contents of the corresponding dump file
-            // the file names are defined in the onos-diagnostics script
-            createFlowNib(rootDir + "flows.json");
-            createGroupNib(rootDir + "groups.json");
-            createLinkNib(rootDir + "links.json");
-            createHostNib(rootDir + "hosts.json");
-            createDeviceNib(rootDir + "ports.json");
-            createDriverNib(rootDir + "device-drivers.json");
-            createMastershipNib(rootDir + "masters.json");
-            createEdgePortNib(rootDir + "edge-ports.json");
-            createRouteNib(rootDir + "routes.json");
-            createNetworkConfigNib(rootDir + "netcfg.json");
-            createMulticastRouteNib(rootDir + "mcast-host-show.json");
+            // names of files to read are defined in the onos-diagnostics script
+            loadFlowNib();
+            loadGroupNib();
+            loadLinkNib();
+            loadHostNib();
+            loadDeviceNib();
+            loadDriverNib();
+            loadMastershipNib();
+            loadEdgePortNib();
+            loadRouteNib();
+            loadNetworkConfigNib();
+            loadMulticastRouteNib();
+
         } catch (IOException e) {
             print("Error in creating NIB: %s", e.getMessage());
             log.error("Nib creation error", e);
             return;
         }
 
-        TroubleshootService service = get(TroubleshootService.class);
-        service.applyNibs();
-        if (service.checkNibsUnavailable()) {
-            print(ERROR_NULL);
-            return;
-        }
+        // ensured no errors in file loading. so make them available officially
+        Lists.newArrayList(FlowNib.getInstance(), GroupNib.getInstance(), LinkNib.getInstance(),
+                HostNib.getInstance(), DeviceNib.getInstance(), DriverNib.getInstance(),
+                MastershipNib.getInstance(), EdgePortNib.getInstance(), RouteNib.getInstance(),
+                NetworkConfigNib.getInstance(), MulticastRouteNib.getInstance())
+                .forEach(nib -> {
+                    // specify creation time and source which the NIB is filled with
+                    nib.setProfile(new NibProfile(System.currentTimeMillis(), NibProfile.SourceType.FILE));
+                    NibProfile profile = nib.getProfile();
+                    print(String.format(
+                            nib.getClass().getSimpleName() + " created %s from %s",
+                            profile.date(), profile.sourceType()));
+                });
     }
 
-    /**
-     * Fetches multicast route-related information and creates the multicast route NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createMulticastRouteNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
+    @Override
+    public void loadFlowNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "flows.json"));
         JsonNode jsonTree = mapper().readTree(stream);
-        Map<McastRoute, McastRouteData> mcastRoutes = new HashMap<>();
+        Set<FlowEntry> flows = new HashSet<>();
 
-        // note: the parsing structure depends on McastShowHostCommand
-        jsonTree.forEach(mcastRouteNode -> {
-            // use McastHostRouteCodec to decode McastRoute
-            McastRoute mcastRoute = codec(McastRoute.class)
-                    .decode((ObjectNode) mcastRouteNode, this);
-            // create McastRouteData that stores sources and sinks of McastRoute
-            McastRouteData mcastRouteData = McastRouteData.empty();
-            if (mcastRouteNode.get("sources") != null) {
-                JsonNode sourcesNode = mcastRouteNode.get("sources");
-                sourcesNode.fields().forEachRemaining(sourceEntry -> {
-                    HostId hostId = HostId.hostId(sourceEntry.getKey());
-                    Set<ConnectPoint> sources = mapper().convertValue(
-                            sourceEntry.getValue(), new TypeReference<Set<ConnectPoint>>() { });
-                    mcastRouteData.addSources(hostId, sources);
-                });
-            }
-            if (mcastRouteNode.get("sinks") != null) {
-                JsonNode sinksNode = mcastRouteNode.get("sinks");
-                sinksNode.fields().forEachRemaining(sinkEntry -> {
-                    HostId hostId = HostId.hostId(sinkEntry.getKey());
-                    Set<ConnectPoint> sinks = mapper().convertValue(
-                            sinkEntry.getValue(), new TypeReference<Set<ConnectPoint>>() { });
-                    mcastRouteData.addSinks(hostId, sinks);
-                });
-            }
-            mcastRoutes.put(mcastRoute, mcastRouteData);
+        List<ObjectNode> flowNodeList = new ArrayList<>();
+        jsonTree.forEach(jsonNode -> {
+            ArrayNode flowArrayNode = (ArrayNode) jsonNode.get("flows");
+            Lists.newArrayList(flowArrayNode.iterator())
+                    .forEach(flowNode -> flowNodeList.add((ObjectNode) flowNode));
         });
 
-        MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
-        mcastRouteNib.setMcastRoutes(mcastRoutes);
-        print("the number of mcast routes: %d", mcastRouteNib.getMcastRoutes().size());
+        // TODO: future plan for the new APIs of the flow rule service that returns raw flows or normalized flows
+        flowNodeList.forEach(flowNode -> {
+            FlowEntry flow;
+            try {
+                flow = codec(FlowEntry.class).decode(flowNode, this);
+            } catch (IllegalArgumentException e) {
+                log.warn("T3 in offline mode ignores reading extension fields of this flow to avoid decoding error");
+                ObjectNode extensionRemoved = removeExtension(flowNode);
+                flow = codec(FlowEntry.class).decode(extensionRemoved, this);
+            }
+            flows.add(flow);
+        });
+
+        FlowNib flowNib = FlowNib.getInstance();
+        flowNib.setFlows(flows);
 
         stream.close();
     }
 
     /**
-     * Fetches network config-related information and creates the network config NIB.
+     * Remove JSON nodes for extension instructions of a flow.
+     * This effectively allows T3 in offline mode to ignore extension fields of flows to avoid "device not found" error.
+     * See decodeExtension() in {@link org.onosproject.codec.impl.DecodeInstructionCodecHelper}.
      *
-     * @param fileName absolute path of JSON file to read
+     * @param flowNode  the json node representing a flow
+     * @return          json node with removed extensions
      */
-    private void createNetworkConfigNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
+    private ObjectNode removeExtension(ObjectNode flowNode) {
+
+        // TODO: decoding extension instructions of offline (dumped) flows is not supported by T3 for now
+        ArrayNode extensionRemoved = mapper().createArrayNode();
+        ArrayNode instructionArrayNode = (ArrayNode) flowNode.get("treatment").get("instructions");
+        instructionArrayNode.forEach(instrNode -> {
+            String instrType = instrNode.get("type").asText();
+            if (!instrType.equals(Instruction.Type.EXTENSION.name())) {
+                extensionRemoved.add(instrNode);
+            }
+        });
+        ((ObjectNode) flowNode.get("treatment")).replace("instructions", extensionRemoved);
+
+        return flowNode;
+    }
+
+    @Override
+    public void loadGroupNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "groups.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Set<Group> groups = new HashSet<>();
+
+        // note: the parsing structure depends on GroupsListCommand
+        groups.addAll(codec(Group.class).decode((ArrayNode) jsonTree, this));
+
+        GroupNib groupNib = GroupNib.getInstance();
+        groupNib.setGroups(groups);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadLinkNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "links.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Set<Link> links = new HashSet<>();
+
+        // note: the parsing structure depends on LinksListCommand
+        links.addAll(codec(Link.class).decode((ArrayNode) jsonTree, this));
+
+        LinkNib linkNib = LinkNib.getInstance();
+        linkNib.setLinks(links);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadHostNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "hosts.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Set<Host> hosts = new HashSet<>();
+
+        // note: the parsing structure depends on HostsListCommand
+        hosts.addAll(codec(Host.class).decode((ArrayNode) jsonTree, this));
+
+        HostNib hostNib = HostNib.getInstance();
+        hostNib.setHosts(hosts);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadDeviceNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "ports.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Map<Device, Set<Port>> devicePortMap = new HashMap<>();
+
+        // note: the parsing structure depends on DevicePortsListCommand
+        jsonTree.forEach(jsonNode -> {
+            Device device = codec(Device.class).decode(
+                    (ObjectNode) jsonNode.get("device"), this);
+            Set<Port> ports = new HashSet<>(codec(Port.class).decode(
+                    (ArrayNode) jsonNode.get("ports"), this));
+            devicePortMap.put(device, ports);
+        });
+
+        DeviceNib deviceNib = DeviceNib.getInstance();
+        deviceNib.setDevicePortMap(devicePortMap);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadDriverNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "device-drivers.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Map<DeviceId, String> deviceDriverMap = new HashMap<>();
+
+        // note: the parsing structure depends on DeviceDriversCommand
+        jsonTree.fields().forEachRemaining(e -> {
+            deviceDriverMap.put(DeviceId.deviceId(e.getKey()), e.getValue().asText());
+        });
+
+        DriverNib driverNib = DriverNib.getInstance();
+        driverNib.setDeviceDriverMap(deviceDriverMap);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadMastershipNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "masters.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Map<DeviceId, NodeId> deviceMasterMap = new HashMap<>();
+
+        // note: the parsing structure depends on MastersListCommand
+        jsonTree.forEach(jsonNode -> {
+            ArrayNode devicesNode = ((ArrayNode) jsonNode.get("devices"));
+            devicesNode.forEach(deviceNode -> {
+                // a device is connected to only one master node at a time
+                deviceMasterMap.put(
+                        DeviceId.deviceId(deviceNode.asText()),
+                        NodeId.nodeId(jsonNode.get("id").asText()));
+            });
+        });
+
+        MastershipNib mastershipNib = MastershipNib.getInstance();
+        mastershipNib.setDeviceMasterMap(deviceMasterMap);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadEdgePortNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "edge-ports.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Map<DeviceId, Set<ConnectPoint>> edgePorts = new HashMap<>();
+
+        // note: the parsing structure depends on EdgePortsListCommand
+        jsonTree.forEach(jsonNode -> {
+            DeviceId deviceId = DeviceId.deviceId(jsonNode.fieldNames().next());
+            PortNumber portNumber = PortNumber.portNumber(
+                    jsonNode.get(deviceId.toString()).asText());
+            if (!edgePorts.containsKey(deviceId)) {
+                edgePorts.put(deviceId, new HashSet<>());
+            }
+            edgePorts.get(deviceId).add(new ConnectPoint(deviceId, portNumber));
+        });
+
+        EdgePortNib edgePortNib = EdgePortNib.getInstance();
+        edgePortNib.setEdgePorts(edgePorts);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadRouteNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "routes.json"));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Set<ResolvedRoute> routes = new HashSet<>();
+
+        // note: the parsing structure depends on RoutesListCommand
+        jsonTree.fields().forEachRemaining(e -> {
+            ArrayNode routesNode = (ArrayNode) e.getValue();
+            routesNode.forEach(routeNode -> {
+                Route route = codec(Route.class).decode((ObjectNode) routeNode, this);
+                // parse optional fields needed for ResolvedRoute
+                MacAddress nextHopMac = (null == routeNode.get("nextHopMac")) ?
+                        null : MacAddress.valueOf(routeNode.get("nextHopMac").asText());
+                VlanId nextHopVlan = (null == routeNode.get("nextHopVlan")) ?
+                        null : VlanId.vlanId(routeNode.get("nextHopVlan").asText());
+                routes.add(new ResolvedRoute(route, nextHopMac, nextHopVlan));
+            });
+        });
+
+        RouteNib routeNib = RouteNib.getInstance();
+        routeNib.setRoutes(routes);
+
+        stream.close();
+    }
+
+    @Override
+    public void loadNetworkConfigNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "netcfg.json"));
         JsonNode jsonTree = mapper().readTree(stream);
         Map<String, Config> portConfigMap = new HashMap<>();
         Map<String, Config> deviceConfigMap = new HashMap<>();
@@ -217,266 +382,48 @@
         NetworkConfigNib networkConfigNib = NetworkConfigNib.getInstance();
         networkConfigNib.setPortConfigMap(portConfigMap);
         networkConfigNib.setDeviceConfigMap(deviceConfigMap);
-        print("the number of network configurations: %d",
-                networkConfigNib.getPortConfigMap().size() + networkConfigNib.getDeviceConfigMap().size());
 
         stream.close();
     }
 
-    /**
-     * Fetches route-related information and creates the route NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createRouteNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
+    @Override
+    public void loadMulticastRouteNib() throws IOException {
+        InputStream stream = new FileInputStream(new File(rootDir + "mcast-host-show.json"));
         JsonNode jsonTree = mapper().readTree(stream);
-        Set<ResolvedRoute> routes = new HashSet<>();
+        Map<McastRoute, McastRouteData> mcastRoutes = new HashMap<>();
 
-        // note: the parsing structure depends on RoutesListCommand
-        jsonTree.fields().forEachRemaining(e -> {
-            ArrayNode routesNode = (ArrayNode) e.getValue();
-            routesNode.forEach(routeNode -> {
-                Route route = codec(Route.class).decode((ObjectNode) routeNode, this);
-                // parse optional fields needed for ResolvedRoute
-                MacAddress nextHopMac = (null == routeNode.get("nextHopMac")) ?
-                        null : MacAddress.valueOf(routeNode.get("nextHopMac").asText());
-                VlanId nextHopVlan = (null == routeNode.get("nextHopVlan")) ?
-                        null : VlanId.vlanId(routeNode.get("nextHopVlan").asText());
-                routes.add(new ResolvedRoute(route, nextHopMac, nextHopVlan));
-            });
-        });
-
-        RouteNib routeNib = RouteNib.getInstance();
-        routeNib.setRoutes(routes);
-        print("the number of routes: %d", routeNib.getRoutes().size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches edge port-related information and creates the edge port NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createEdgePortNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Map<DeviceId, Set<ConnectPoint>> edgePorts = new HashMap<>();
-
-        // note: the parsing structure depends on EdgePortsListCommand
-        jsonTree.forEach(jsonNode -> {
-            DeviceId deviceId = DeviceId.deviceId(jsonNode.fieldNames().next());
-            PortNumber portNumber = PortNumber.portNumber(
-                    jsonNode.get(deviceId.toString()).asText());
-            if (!edgePorts.containsKey(deviceId)) {
-                edgePorts.put(deviceId, new HashSet<>());
+        // note: the parsing structure depends on McastShowHostCommand
+        jsonTree.forEach(mcastRouteNode -> {
+            // use McastHostRouteCodec to decode McastRoute
+            McastRoute mcastRoute = codec(McastRoute.class)
+                    .decode((ObjectNode) mcastRouteNode, this);
+            // create McastRouteData that stores sources and sinks of McastRoute
+            McastRouteData mcastRouteData = McastRouteData.empty();
+            if (mcastRouteNode.get("sources") != null) {
+                JsonNode sourcesNode = mcastRouteNode.get("sources");
+                sourcesNode.fields().forEachRemaining(sourceEntry -> {
+                    HostId hostId = HostId.hostId(sourceEntry.getKey());
+                    Set<ConnectPoint> sources = mapper().convertValue(
+                            sourceEntry.getValue(), new TypeReference<Set<ConnectPoint>>() { });
+                    mcastRouteData.addSources(hostId, sources);
+                });
             }
-            edgePorts.get(deviceId).add(new ConnectPoint(deviceId, portNumber));
-        });
-
-        EdgePortNib edgePortNib = EdgePortNib.getInstance();
-        edgePortNib.setEdgePorts(edgePorts);
-        print("the number of edge ports: %d", edgePortNib.getEdgePorts().size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches mastership-related information and creates the mastership NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createMastershipNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Map<DeviceId, NodeId> deviceMasterMap = new HashMap<>();
-
-        // note: the parsing structure depends on MastersListCommand
-        jsonTree.forEach(jsonNode -> {
-            ArrayNode devicesNode = ((ArrayNode) jsonNode.get("devices"));
-            devicesNode.forEach(deviceNode -> {
-                // a device is connected to only one master node at a time
-                deviceMasterMap.put(
-                        DeviceId.deviceId(deviceNode.asText()),
-                        NodeId.nodeId(jsonNode.get("id").asText()));
-            });
-        });
-
-        MastershipNib mastershipNib = MastershipNib.getInstance();
-        mastershipNib.setDeviceMasterMap(deviceMasterMap);
-        print("the number of device-node mappings: %d", mastershipNib.getDeviceMasterMap().size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches driver-related information and creates the driver NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createDriverNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Map<DeviceId, String> deviceDriverMap = new HashMap<>();
-
-        // note: the parsing structure depends on DeviceDriversCommand
-        jsonTree.fields().forEachRemaining(e -> {
-            deviceDriverMap.put(DeviceId.deviceId(e.getKey()), e.getValue().asText());
-        });
-
-        DriverNib driverNib = DriverNib.getInstance();
-        driverNib.setDeviceDriverMap(deviceDriverMap);
-        print("the number of device-driver mappings: %d", deviceDriverMap.size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches device-related information and creates the device NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createDeviceNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Map<Device, Set<Port>> devicePortMap = new HashMap<>();
-
-        // note: the parsing structure depends on DevicePortsListCommand
-        jsonTree.forEach(jsonNode -> {
-            Device device = codec(Device.class).decode(
-                    (ObjectNode) jsonNode.get("device"), this);
-            Set<Port> ports = new HashSet<>(codec(Port.class).decode(
-                    (ArrayNode) jsonNode.get("ports"), this));
-            devicePortMap.put(device, ports);
-        });
-
-        DeviceNib deviceNib = DeviceNib.getInstance();
-        deviceNib.setDevicePortMap(devicePortMap);
-        print("the number of devices: %d", deviceNib.getDevicePortMap().size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches host-related information and creates the host NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createHostNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Set<Host> hosts = new HashSet<>();
-
-        // note: the parsing structure depends on HostsListCommand
-        hosts.addAll(codec(Host.class).decode((ArrayNode) jsonTree, this));
-
-        HostNib hostNib = HostNib.getInstance();
-        hostNib.setHosts(hosts);
-        print("the number of hosts: %d", hostNib.getHosts().size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches link-related information and creates the link NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createLinkNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Set<Link> links = new HashSet<>();
-
-        // note: the parsing structure depends on LinksListCommand
-        links.addAll(codec(Link.class).decode((ArrayNode) jsonTree, this));
-
-        LinkNib linkNib = LinkNib.getInstance();
-        linkNib.setLinks(links);
-        print("the number of links: %d", linkNib.getLinks().size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches group-related information and creates the group NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createGroupNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Set<Group> groups = new HashSet<>();
-
-        // note: the parsing structure depends on GroupsListCommand
-        groups.addAll(codec(Group.class).decode((ArrayNode) jsonTree, this));
-
-        GroupNib groupNib = GroupNib.getInstance();
-        groupNib.setGroups(groups);
-        print("the number of groups: %d", groupNib.getGroups().size());
-
-        stream.close();
-    }
-
-    /**
-     * Fetches flow-related information and creates the flow NIB.
-     *
-     * @param fileName absolute path of JSON file to read
-     */
-    private void createFlowNib(String fileName) throws IOException {
-        InputStream stream = new FileInputStream(new File(fileName));
-        JsonNode jsonTree = mapper().readTree(stream);
-        Set<FlowEntry> flows = new HashSet<>();
-
-        List<ObjectNode> flowNodeList = new ArrayList<>();
-        jsonTree.forEach(jsonNode -> {
-            ArrayNode flowArrayNode = (ArrayNode) jsonNode.get("flows");
-            Lists.newArrayList(flowArrayNode.iterator())
-                    .forEach(flowNode -> flowNodeList.add((ObjectNode) flowNode));
-        });
-
-        // TODO: future plan for the new APIs of the flow rule service that returns raw flows or normalized flows
-        flowNodeList.forEach(flowNode -> {
-            FlowEntry flow;
-            try {
-                flow = codec(FlowEntry.class).decode(flowNode, this);
-            } catch (IllegalArgumentException e) {
-                log.warn("T3 in offline mode ignores reading extension fields of this flow to avoid decoding error");
-                ObjectNode extensionRemoved = removeExtension(flowNode);
-                flow = codec(FlowEntry.class).decode(extensionRemoved, this);
+            if (mcastRouteNode.get("sinks") != null) {
+                JsonNode sinksNode = mcastRouteNode.get("sinks");
+                sinksNode.fields().forEachRemaining(sinkEntry -> {
+                    HostId hostId = HostId.hostId(sinkEntry.getKey());
+                    Set<ConnectPoint> sinks = mapper().convertValue(
+                            sinkEntry.getValue(), new TypeReference<Set<ConnectPoint>>() { });
+                    mcastRouteData.addSinks(hostId, sinks);
+                });
             }
-            flows.add(flow);
+            mcastRoutes.put(mcastRoute, mcastRouteData);
         });
 
-        FlowNib flowNib = FlowNib.getInstance();
-        flowNib.setFlows(flows);
-        print("the number of flows: %d", flowNib.getFlows().size());
+        MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
+        mcastRouteNib.setMcastRoutes(mcastRoutes);
 
         stream.close();
     }
 
-    /**
-     * Remove JSON nodes for extension instructions of a flow.
-     * This effectively allows T3 in offline mode to ignore extension fields of flows to avoid "device not found" error.
-     * See decodeExtension() in {@link org.onosproject.codec.impl.DecodeInstructionCodecHelper}.
-     *
-     * @param flowNode  the json node representing a flow
-     * @return          json node with removed extensions
-     */
-    private ObjectNode removeExtension(ObjectNode flowNode) {
-
-        // TODO: decoding extension instructions of offline (dumped) flows is not supported by T3 for now
-        ArrayNode extensionRemoved = mapper().createArrayNode();
-        ArrayNode instructionArrayNode = (ArrayNode) flowNode.get("treatment").get("instructions");
-        instructionArrayNode.forEach(instrNode -> {
-            String instrType = instrNode.get("type").asText();
-            if (!instrType.equals(Instruction.Type.EXTENSION.name())) {
-                extensionRemoved.add(instrNode);
-            }
-        });
-        ((ObjectNode) flowNode.get("treatment")).replace("instructions", extensionRemoved);
-
-        return flowNode;
-    }
-
 }
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadSnapshotCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadSnapshotCommand.java
new file mode 100644
index 0000000..9a6b20b
--- /dev/null
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadSnapshotCommand.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * 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.t3.cli;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.mcast.api.McastRoute;
+import org.onosproject.mcast.api.McastRouteData;
+import org.onosproject.mcast.api.MulticastRouteService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Link;
+import org.onosproject.net.Port;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.InterfaceConfig;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.edge.EdgePortService;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.routeservice.ResolvedRoute;
+import org.onosproject.routeservice.RouteService;
+import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
+import org.onosproject.t3.api.DeviceNib;
+import org.onosproject.t3.api.DriverNib;
+import org.onosproject.t3.api.EdgePortNib;
+import org.onosproject.t3.api.FlowNib;
+import org.onosproject.t3.api.GroupNib;
+import org.onosproject.t3.api.HostNib;
+import org.onosproject.t3.api.LinkNib;
+import org.onosproject.t3.api.MastershipNib;
+import org.onosproject.t3.api.MulticastRouteNib;
+import org.onosproject.t3.api.NetworkConfigNib;
+import org.onosproject.t3.api.NibProfile;
+import org.onosproject.t3.api.RouteNib;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * T3 CLI command to load the NIB with snapshots of the network states that are fetched from ONOS stores.
+ */
+@Service
+@Command(scope = "onos", name = "t3-load-snapshot",
+        description = "Load the NIB with the network states stored in the ONOS instance where the T3 is running")
+public class TroubleshootLoadSnapshotCommand
+        extends AbstractShellCommand implements NibLoader {
+
+    @Override
+    protected void doExecute() {
+
+        print("Load current network states from ONOS stores");
+
+        loadFlowNib();
+        loadGroupNib();
+        loadLinkNib();
+        loadHostNib();
+        loadDeviceNib();
+        loadDriverNib();
+        loadMastershipNib();
+        loadEdgePortNib();
+        loadRouteNib();
+        loadNetworkConfigNib();
+        loadMulticastRouteNib();
+
+        Lists.newArrayList(FlowNib.getInstance(), GroupNib.getInstance(), LinkNib.getInstance(),
+                HostNib.getInstance(), DeviceNib.getInstance(), DriverNib.getInstance(),
+                MastershipNib.getInstance(), EdgePortNib.getInstance(), RouteNib.getInstance(),
+                NetworkConfigNib.getInstance(), MulticastRouteNib.getInstance())
+                .forEach(nib -> {
+                    // specify creation time and source which the NIB is filled with
+                    nib.setProfile(new NibProfile(System.currentTimeMillis(), NibProfile.SourceType.SNAPSHOT));
+                    NibProfile profile = nib.getProfile();
+                    print(String.format(
+                            nib.getClass().getSimpleName() + " created %s from %s",
+                            profile.date(), profile.sourceType()));
+                });
+    }
+
+    @Override
+    public void loadFlowNib() {
+        FlowRuleService flowRuleService = get(FlowRuleService.class);
+        DeviceService deviceService = get(DeviceService.class);
+        Set<FlowEntry> flows = new HashSet<>();
+
+        Lists.newArrayList(deviceService.getDevices().iterator())
+                .forEach(device -> flows.addAll(Lists.newArrayList(
+                        flowRuleService.getFlowEntries(device.id()))));
+
+        FlowNib flowNib = FlowNib.getInstance();
+        flowNib.setFlows(flows);
+    }
+
+    @Override
+    public void loadGroupNib() {
+        GroupService groupService = get(GroupService.class);
+        DeviceService deviceService = get(DeviceService.class);
+        Set<Group> groups = new HashSet<>();
+
+        Lists.newArrayList(deviceService.getDevices().iterator())
+                .forEach(device -> groups.addAll(Lists.newArrayList(
+                        groupService.getGroups(device.id()))));
+
+        GroupNib groupNib = GroupNib.getInstance();
+        groupNib.setGroups(groups);
+    }
+
+    @Override
+    public void loadLinkNib() {
+        LinkService linkService = get(LinkService.class);
+        Set<Link> links = new HashSet<>();
+
+        links.addAll(Lists.newArrayList(linkService.getLinks()));
+
+        LinkNib linkNib = LinkNib.getInstance();
+        linkNib.setLinks(links);
+    }
+
+    @Override
+    public void loadHostNib() {
+        HostService hostService = get(HostService.class);
+        Set<Host> hosts = new HashSet<>();
+
+        hosts.addAll(Lists.newArrayList(hostService.getHosts()));
+
+        HostNib hostNib = HostNib.getInstance();
+        hostNib.setHosts(hosts);
+    }
+
+    @Override
+    public void loadDeviceNib() {
+        DeviceService deviceService = get(DeviceService.class);
+        Map<Device, Set<Port>> devicePortMap = new HashMap<>();
+
+        Lists.newArrayList(deviceService.getDevices().iterator())
+                .forEach(device -> {
+                    // current DeviceNib impl. checks the availability of devices from their annotations
+                    DefaultAnnotations annotations = DefaultAnnotations.builder()
+                            .set("available", String.valueOf(deviceService.isAvailable(device.id()))).build();
+                    DefaultDevice annotated = new DefaultDevice(device.providerId(), device.id(), device.type(),
+                            device.manufacturer(), device.hwVersion(), device.swVersion(), device.serialNumber(),
+                            device.chassisId(), annotations);
+                    devicePortMap.put(annotated, Sets.newHashSet(deviceService.getPorts(device.id())));
+                });
+
+        DeviceNib deviceNib = DeviceNib.getInstance();
+        deviceNib.setDevicePortMap(devicePortMap);
+    }
+
+    @Override
+    public void loadDriverNib() {
+        DriverService driverService = get(DriverService.class);
+        Map<DeviceId, String> deviceDriverMap = driverService.getDeviceDrivers();
+
+        DriverNib driverNib = DriverNib.getInstance();
+        driverNib.setDeviceDriverMap(deviceDriverMap);
+    }
+
+    @Override
+    public void loadMastershipNib() {
+        MastershipService mastershipService = get(MastershipService.class);
+        DeviceService deviceService = get(DeviceService.class);
+        Map<DeviceId, NodeId> deviceMasterMap = new HashMap<>();
+
+        Lists.newArrayList(deviceService.getDevices().iterator())
+                .forEach(device -> deviceMasterMap.put(device.id(), mastershipService.getMasterFor(device.id())));
+
+        MastershipNib mastershipNib = MastershipNib.getInstance();
+        mastershipNib.setDeviceMasterMap(deviceMasterMap);
+    }
+
+    @Override
+    public void loadEdgePortNib() {
+        EdgePortService edgePortService = get(EdgePortService.class);
+        DeviceService deviceService = get(DeviceService.class);
+        Map<DeviceId, Set<ConnectPoint>> edgePorts = new HashMap<>();
+
+        Lists.newArrayList(deviceService.getDevices().iterator())
+                .forEach(device -> edgePorts.put(device.id(), Sets.newHashSet(edgePortService.getEdgePoints())));
+
+        EdgePortNib edgePortNib = EdgePortNib.getInstance();
+        edgePortNib.setEdgePorts(edgePorts);
+    }
+
+    @Override
+    public void loadRouteNib() {
+        RouteService routeService = get(RouteService.class);
+        Set<ResolvedRoute> routes = new HashSet<>();
+
+        Lists.newArrayList(routeService.getRouteTables())
+                .forEach(routeTableId -> routes.addAll(routeService.getResolvedRoutes(routeTableId)));
+
+        RouteNib routeNib = RouteNib.getInstance();
+        routeNib.setRoutes(routes);
+    }
+
+    @Override
+    public void loadNetworkConfigNib() {
+        NetworkConfigService networkConfigService = get(NetworkConfigService.class);
+        DeviceService deviceService = get(DeviceService.class);
+
+        // Map of str ConnectPoint : InterfaceConfig
+        Map<String, Config> portConfigMap = new HashMap<>();
+        Lists.newArrayList(deviceService.getDevices().iterator())
+                .forEach(device -> deviceService.getPorts(device.id())
+                        .forEach(port -> {
+                            ConnectPoint cp = new ConnectPoint(device.id(), port.number());
+                            portConfigMap.put(cp.toString(), networkConfigService.getConfig(cp, InterfaceConfig.class));
+                        }));
+
+        // Map of str DeviceId : SegmentRoutingDeviceConfig
+        Map<String, Config> deviceConfigMap = new HashMap<>();
+        Lists.newArrayList(deviceService.getDevices().iterator())
+                .forEach(device -> deviceConfigMap.put(device.id().toString(),
+                        networkConfigService.getConfig(device.id(), SegmentRoutingDeviceConfig.class)));
+
+        NetworkConfigNib networkConfigNib = NetworkConfigNib.getInstance();
+        networkConfigNib.setPortConfigMap(portConfigMap);
+        networkConfigNib.setDeviceConfigMap(deviceConfigMap);
+    }
+
+    @Override
+    public void loadMulticastRouteNib() {
+        MulticastRouteService mcastRouteService = get(MulticastRouteService.class);
+        Map<McastRoute, McastRouteData> mcastRoutes = new HashMap<>();
+
+        Lists.newArrayList(mcastRouteService.getRoutes())
+                .forEach(mcastRoute -> mcastRoutes.put(mcastRoute, mcastRouteService.routeData(mcastRoute)));
+
+        MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
+        mcastRouteNib.setMcastRoutes(mcastRoutes);
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
index 3df110f..d635c23 100644
--- a/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
@@ -67,9 +67,18 @@
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
-        if (service.checkNibsUnavailable()) {
-            print(TroubleshootLoadFileCommand.ERROR_NULL);
-            return;
+        if (!service.checkNibValidity()) {
+            // if the NIB is found invalid, fill it with the current network states so that this command can proceed
+            print(T3CliUtils.NIB_AUTOFILLED);
+            TroubleshootLoadSnapshotCommand cmd = new TroubleshootLoadSnapshotCommand();
+            cmd.doExecute();
+            if (!service.checkNibValidity()) {
+                // if the NIB is still invalid even after auto-filled snapshots, stop and warn
+                print(T3CliUtils.NIB_TERMINATE);
+                return;
+            }
+        } else {
+            print(service.printNibSummary());
         }
 
         print("Tracing all Multicast routes in the System");
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
index 3e7003f..e61a533 100644
--- a/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
@@ -69,9 +69,18 @@
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
-        if (service.checkNibsUnavailable()) {
-            print(TroubleshootLoadFileCommand.ERROR_NULL);
-            return;
+        if (!service.checkNibValidity()) {
+            // if the NIB is found invalid, fill it with the current network states so that this command can proceed
+            print(T3CliUtils.NIB_AUTOFILLED);
+            TroubleshootLoadSnapshotCommand cmd = new TroubleshootLoadSnapshotCommand();
+            cmd.doExecute();
+            if (!service.checkNibValidity()) {
+                // if the NIB is still invalid even after auto-filled snapshots, stop and warn
+                print(T3CliUtils.NIB_TERMINATE);
+                return;
+            }
+        } else {
+            print(service.printNibSummary());
         }
 
         EtherType type = EtherType.valueOf(ethType.toUpperCase());
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
index 7a5ecc7..66f5749 100644
--- a/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
@@ -68,10 +68,20 @@
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
-        if (service.checkNibsUnavailable()) {
-            print(TroubleshootLoadFileCommand.ERROR_NULL);
-            return;
+        if (!service.checkNibValidity()) {
+            // if the NIB is found invalid, fill it with the current network states so that this command can proceed
+            print(T3CliUtils.NIB_AUTOFILLED);
+            TroubleshootLoadSnapshotCommand cmd = new TroubleshootLoadSnapshotCommand();
+            cmd.doExecute();
+            if (!service.checkNibValidity()) {
+                // if the NIB is still invalid even after auto-filled snapshots, stop and warn
+                print(T3CliUtils.NIB_TERMINATE);
+                return;
+            }
+        } else {
+            print(service.printNibSummary());
         }
+
         if (srcHost.equals(dstHost)) {
             print("Source and destination are same. Use different hosts");
             return;
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
index 5da3f8c..db7d69f 100644
--- a/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
@@ -127,9 +127,18 @@
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
-        if (service.checkNibsUnavailable()) {
-            print(TroubleshootLoadFileCommand.ERROR_NULL);
-            return;
+        if (!service.checkNibValidity()) {
+            // if the NIB is found invalid, fill it with the current network states so that this command can proceed
+            print(T3CliUtils.NIB_AUTOFILLED);
+            TroubleshootLoadSnapshotCommand cmd = new TroubleshootLoadSnapshotCommand();
+            cmd.doExecute();
+            if (!service.checkNibValidity()) {
+                // if the NIB is still invalid even after auto-filled snapshots, stop and warn
+                print(T3CliUtils.NIB_TERMINATE);
+                return;
+            }
+        } else {
+            print(service.printNibSummary());
         }
 
         String[] cpInfo = srcPort.split("/");
diff --git a/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index b1359bc..bc04136 100644
--- a/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.VlanId;
@@ -67,6 +68,7 @@
 import org.onosproject.t3.api.MastershipNib;
 import org.onosproject.t3.api.MulticastRouteNib;
 import org.onosproject.t3.api.NetworkConfigNib;
+import org.onosproject.t3.api.NibProfile;
 import org.onosproject.t3.api.RouteNib;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.onosproject.t3.api.TroubleshootService;
@@ -109,38 +111,38 @@
     static final String PACKET_TO_CONTROLLER = "Packet goes to the controller";
 
     // uses a snapshot (cache) of NIBs instead of interacting with ONOS core in runtime
-    protected FlowNib flowNib;
-    protected GroupNib groupNib;
-    protected LinkNib linkNib;
-    protected HostNib hostNib;
-    protected DeviceNib deviceNib;
-    protected DriverNib driverNib;
-    protected MastershipNib mastershipNib;
-    protected EdgePortNib edgePortNib;
-    protected RouteNib routeNib;
-    protected NetworkConfigNib networkConfigNib;
-    protected MulticastRouteNib mcastRouteNib;
+    protected FlowNib flowNib = FlowNib.getInstance();
+    protected GroupNib groupNib = GroupNib.getInstance();
+    protected LinkNib linkNib = LinkNib.getInstance();
+    protected HostNib hostNib = HostNib.getInstance();
+    protected DeviceNib deviceNib = DeviceNib.getInstance();
+    protected DriverNib driverNib = DriverNib.getInstance();
+    protected MastershipNib mastershipNib = MastershipNib.getInstance();
+    protected EdgePortNib edgePortNib = EdgePortNib.getInstance();
+    protected RouteNib routeNib = RouteNib.getInstance();
+    protected NetworkConfigNib networkConfigNib = NetworkConfigNib.getInstance();
+    protected MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
 
     @Override
-    public boolean checkNibsUnavailable() {
+    public boolean checkNibValidity() {
         return Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
                 mastershipNib, edgePortNib, routeNib, networkConfigNib, mcastRouteNib)
-                .anyMatch(x -> x == null);
+                .allMatch(nib -> nib != null && nib.isValid());
     }
 
     @Override
-    public void applyNibs() {
-        flowNib = FlowNib.getInstance();
-        groupNib = GroupNib.getInstance();
-        linkNib = LinkNib.getInstance();
-        hostNib = HostNib.getInstance();
-        deviceNib = DeviceNib.getInstance();
-        driverNib = DriverNib.getInstance();
-        mastershipNib = MastershipNib.getInstance();
-        edgePortNib = EdgePortNib.getInstance();
-        routeNib = RouteNib.getInstance();
-        networkConfigNib = NetworkConfigNib.getInstance();
-        mcastRouteNib = MulticastRouteNib.getInstance();
+    public String printNibSummary() {
+        StringBuilder summary = new StringBuilder().append("*** Current NIB in valid: ***\n");
+        Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
+                mastershipNib, edgePortNib, routeNib, networkConfigNib, mcastRouteNib)
+                .forEach(nib -> {
+                    NibProfile profile = nib.getProfile();
+                    summary.append(String.format(
+                            nib.getClass().getName() + " created %s from %s\n",
+                            profile.date(), profile.sourceType()));
+                });
+
+        return summary.append(StringUtils.rightPad("", 125, '-')).toString();
     }
 
     @Override