[AETHER-76] Impelentation of a new Trellis Troubleshoot Tool (T3) for 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.
- 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: Ice608f77aa96bfbcadfff34991c4a1b6d93125b6
(cherry picked from commit eaa6329aba67c2577fdca7d3ddf230611e82f9f7)
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/DeviceNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/DeviceNib.java
new file mode 100644
index 0000000..618724a
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/DeviceNib.java
@@ -0,0 +1,124 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents Network Information Base (NIB) for devices
+ * and supports alternative functions to
+ * {@link org.onosproject.net.device.DeviceService} for offline data.
+ */
+public class DeviceNib {
+
+    private Map<Device, Set<Port>> devicePortMap;
+
+    // use the singleton helper to create the instance
+    protected DeviceNib() {
+    }
+
+    /**
+     * Sets a map of device : ports of the device.
+     *
+     * @param devicePortMap device-ports map
+     */
+    public void setDevicePortMap(Map<Device, Set<Port>> devicePortMap) {
+        this.devicePortMap = devicePortMap;
+    }
+
+    /**
+     * Returns the device-ports map.
+     *
+     * @return device-ports map
+     */
+    public Map<Device, Set<Port>> getDevicePortMap() {
+        return ImmutableMap.copyOf(devicePortMap);
+    }
+
+    /**
+     * Returns the device with the specified identifier.
+     *
+     * @param deviceId device identifier
+     * @return device or null if one with the given identifier is not known
+     */
+    public Device getDevice(DeviceId deviceId) {
+        return devicePortMap.keySet().stream()
+                .filter(device -> device.id().equals(deviceId))
+                .findFirst().orElse(null);
+    }
+
+    /**
+     * Returns the port with the specified connect point.
+     *
+     * @param cp connect point
+     * @return device port
+     */
+    public Port getPort(ConnectPoint cp) {
+        return devicePortMap.get(getDevice(cp.deviceId())).stream()
+                .filter(port -> port.number().equals(cp.port()))
+                .findFirst().orElse(null);
+    }
+
+    /**
+     * Returns the list of ports associated with the device.
+     *
+     * @param deviceId device identifier
+     * @return list of ports
+     */
+    public List<Port> getPorts(DeviceId deviceId) {
+        return ImmutableList.copyOf(devicePortMap.get(getDevice(deviceId)));
+    }
+
+    /**
+     * Indicates whether or not the device is presently online and available.
+     * Availability, unlike reachability, denotes whether ANY node in the
+     * cluster can discover that this device is in an operational state,
+     * this does not necessarily mean that there exists a node that can
+     * control this device.
+     *
+     * @param deviceId device identifier
+     * @return true if the device is available
+     */
+    public boolean isAvailable(DeviceId deviceId) {
+        Device device = getDevice(deviceId);
+        // TODO: may need an extra REST API to get availableDevices from DeviceService, not from device annotations
+        return device.annotations().value("available").equals("true") ? true : false;
+    }
+
+    /**
+     * Returns the singleton instance of devices NIB.
+     *
+     * @return instance of devices NIB
+     */
+    public static DeviceNib getInstance() {
+        return DeviceNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final DeviceNib INSTANCE = new DeviceNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/DriverNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/DriverNib.java
new file mode 100644
index 0000000..4c88a20
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/DriverNib.java
@@ -0,0 +1,78 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import org.onosproject.net.DeviceId;
+
+import java.util.Map;
+
+/**
+ * Represents Network Information Base (NIB) for drivers
+ * and supports alternative functions to
+ * {@link org.onosproject.net.driver.DriverService} for offline data.
+ */
+public class DriverNib {
+
+    private Map<DeviceId, String> deviceDriverMap;
+
+    // use the singleton helper to create the instance
+    protected DriverNib() {
+    }
+
+    /**
+     * Sets a map of device id : driver name.
+     *
+     * @param deviceDriverMap device-driver map
+     */
+    public void setDeviceDriverMap(Map<DeviceId, String> deviceDriverMap) {
+        this.deviceDriverMap = deviceDriverMap;
+    }
+
+    /**
+     * Returns the device-driver map.
+     *
+     * @return device-driver map
+     */
+    public Map<DeviceId, String> getDeviceDriverMap() {
+        return ImmutableMap.copyOf(deviceDriverMap);
+    }
+
+    /**
+     * Returns a driver name of the given device.
+     *
+     * @param deviceId the device id
+     * @return the driver name
+     */
+    public String getDriverName(DeviceId deviceId) {
+        return deviceDriverMap.get(deviceId);
+    }
+
+    /**
+     * Returns the singleton instance of drivers NIB.
+     *
+     * @return instance of drivers NIB
+     */
+    public static DriverNib getInstance() {
+        return DriverNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final DriverNib INSTANCE = new DriverNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/EdgePortNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/EdgePortNib.java
new file mode 100644
index 0000000..f24668f
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/EdgePortNib.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents Network Information Base (NIB) for edge ports
+ * and supports alternative functions to
+ * {@link org.onosproject.net.edge.EdgePortService} for offline data.
+ */
+public class EdgePortNib {
+
+    private Map<DeviceId, Set<ConnectPoint>> edgePorts;
+
+    // use the singleton helper to create the instance
+    protected EdgePortNib() {
+    }
+
+    /**
+     * Sets a map of device id : edge ports of the device.
+     *
+     * @param edgePorts device-edge ports map
+     */
+    public void setEdgePorts(Map<DeviceId, Set<ConnectPoint>> edgePorts) {
+        this.edgePorts = edgePorts;
+    }
+
+    /**
+     * Returns the device-edge ports map.
+     * @return device-edge ports map
+     */
+    public Map<DeviceId, Set<ConnectPoint>> getEdgePorts() {
+        return ImmutableMap.copyOf(edgePorts);
+    }
+
+    /**
+     * Indicates whether or not the specified connection point is an edge point.
+     *
+     * @param point connection point
+     * @return true if edge point
+     */
+    public boolean isEdgePoint(ConnectPoint point) {
+        Set<ConnectPoint> connectPoints = edgePorts.get(point.deviceId());
+        return connectPoints != null && connectPoints.contains(point);
+    }
+
+    /**
+     * Returns the singleton instance of edge ports NIB.
+     *
+     * @return instance of edge ports NIB
+     */
+    public static EdgePortNib getInstance() {
+        return EdgePortNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final EdgePortNib INSTANCE = new EdgePortNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/FlowNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/FlowNib.java
new file mode 100644
index 0000000..aecb57c
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/FlowNib.java
@@ -0,0 +1,86 @@
+/*
+ * 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 com.google.common.collect.ImmutableSet;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowEntry;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents Network Information Base (NIB) for flows
+ * and supports alternative functions to
+ * {@link org.onosproject.net.flow.FlowRuleService} for offline data.
+ */
+public class FlowNib {
+
+    // TODO with method optimization, store into subdivided structures at the first load
+    private Set<FlowEntry> flows;
+
+    // use the singleton helper to create the instance
+    protected FlowNib() {
+    }
+
+    /**
+     * Sets a set of flows.
+     *
+     * @param flows flow set
+     */
+    public void setFlows(Set<FlowEntry> flows) {
+        this.flows = flows;
+    }
+
+    /**
+     * Returns the set of flows.
+     *
+     * @return flow set
+     */
+    public Set<FlowEntry> getFlows() {
+        return ImmutableSet.copyOf(flows);
+    }
+
+    /**
+     * Returns a list of rules filtered by device id and flow state.
+     *
+     * @param deviceId the device id to lookup
+     * @param flowState the flow state to lookup
+     * @return collection of flow entries
+     */
+    public Iterable<FlowEntry> getFlowEntriesByState(DeviceId deviceId, FlowEntry.FlowEntryState flowState) {
+        Set<FlowEntry> flowsFiltered = flows.stream()
+                .filter(flow -> flow.state() == flowState
+                        && flow.deviceId().equals(deviceId))
+                .collect(Collectors.toSet());
+        return flowsFiltered != null ? ImmutableSet.copyOf(flowsFiltered) : ImmutableSet.of();
+    }
+
+    /**
+     * Returns the singleton instance of flows NIB.
+     *
+     * @return instance of flows NIB
+     */
+    public static FlowNib getInstance() {
+        return SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final FlowNib INSTANCE = new FlowNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/GroupNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/GroupNib.java
new file mode 100644
index 0000000..64d6575
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/GroupNib.java
@@ -0,0 +1,84 @@
+/*
+ * 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 com.google.common.collect.ImmutableSet;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.Group;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents Network Information Base (NIB) for groups
+ * and supports alternative functions to
+ * {@link org.onosproject.net.group.GroupService} for offline data.
+ */
+public class GroupNib {
+
+    // TODO with method optimization, store into subdivided structures at the first load
+    private Set<Group> groups;
+
+    // use the singleton helper to create the instance
+    protected GroupNib() {
+    }
+
+    /**
+     * Sets a set of groups.
+     *
+     * @param groups group set
+     */
+    public void setGroups(Set<Group> groups) {
+        this.groups = groups;
+    }
+
+    /**
+     * Returns the set of groups.
+     *
+     * @return group set
+     */
+    public Set<Group> getGroups() {
+        return ImmutableSet.copyOf(groups);
+    }
+
+    /**
+     * Returns all groups associated with the given device.
+     *
+     * @param deviceId device ID to get groups for
+     * @return iterable of device's groups
+     */
+    public Iterable<Group> getGroups(DeviceId deviceId) {
+        Set<Group> groupsFiltered = groups.stream()
+                .filter(g -> g.deviceId().equals(deviceId))
+                .collect(Collectors.toSet());
+        return groupsFiltered != null ? ImmutableSet.copyOf(groupsFiltered) : ImmutableSet.of();
+    }
+
+    /**
+     * Returns the singleton instance of groups NIB.
+     *
+     * @return instance of groups NIB
+     */
+    public static GroupNib getInstance() {
+        return GroupNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final GroupNib INSTANCE = new GroupNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/HostNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/HostNib.java
new file mode 100644
index 0000000..5b5eab2
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/HostNib.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents Network Information Base (NIB) for hosts
+ * and supports alternative functions to
+ * {@link org.onosproject.net.host.HostService} for offline data.
+ */
+public class HostNib {
+
+    // TODO with method optimization, store into subdivided structures at the first load
+    private Set<Host> hosts;
+
+    // use the singleton helper to create the instance
+    protected HostNib() {
+    }
+
+    /**
+     * Sets a set of hosts.
+     *
+     * @param hosts host set
+     */
+    public void setHosts(Set<Host> hosts) {
+        this.hosts = hosts;
+    }
+
+    /**
+     * Returns the set of hosts.
+     *
+     * @return host set
+     */
+    public Set<Host> getHosts() {
+        return ImmutableSet.copyOf(hosts);
+    }
+
+    /**
+     * Returns the host with the specified identifier.
+     *
+     * @param hostId host identifier
+     * @return host or null if one with the given identifier is not known
+     */
+    public Host getHost(HostId hostId) {
+        return hosts.stream()
+                .filter(host -> host.id().equals(hostId))
+                .findFirst().orElse(null);
+    }
+
+    /**
+     * Returns the set of hosts whose most recent location is the specified
+     * connection point.
+     *
+     * @param connectPoint connection point
+     * @return set of hosts connected to the connection point
+     */
+    public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+        // TODO extend this method to support matching on auxLocations as well
+        Set<Host> connectedHosts = hosts.stream()
+                .filter(host -> host.locations().contains(connectPoint))
+                .collect(Collectors.toSet());
+        return connectedHosts != null ? ImmutableSet.copyOf(connectedHosts) : ImmutableSet.of();
+    }
+
+    /**
+     * Returns the set of hosts that have the specified IP address.
+     *
+     * @param ip ip address
+     * @return set of hosts with the given IP
+     */
+    public Set<Host> getHostsByIp(IpAddress ip) {
+        Set<Host> hostsByIp = hosts.stream()
+                .filter(host -> host.ipAddresses().contains(ip))
+                .collect(Collectors.toSet());
+        return hostsByIp != null ? ImmutableSet.copyOf(hostsByIp) : ImmutableSet.of();
+    }
+
+    /**
+     * Returns the set of hosts that have the specified MAC address.
+     *
+     * @param mac mac address
+     * @return set of hosts with the given mac
+     */
+    public Set<Host> getHostsByMac(MacAddress mac) {
+        Set<Host> hostsByMac = hosts.stream()
+                .filter(host -> host.mac().equals(mac))
+                .collect(Collectors.toSet());
+        return hostsByMac != null ? ImmutableSet.copyOf(hostsByMac) : ImmutableSet.of();
+    }
+
+    /**
+     * Returns the singleton instance of hosts NIB.
+     *
+     * @return instance of hosts NIB
+     */
+    public static HostNib getInstance() {
+        return HostNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final HostNib INSTANCE = new HostNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/LinkNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/LinkNib.java
new file mode 100644
index 0000000..02acb54
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/LinkNib.java
@@ -0,0 +1,85 @@
+/*
+ * 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 com.google.common.collect.ImmutableSet;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents Network Information Base (NIB) for links
+ * and supports alternative functions to
+ * {@link org.onosproject.net.link.LinkService} for offline data.
+ */
+public class LinkNib {
+
+    // TODO with method optimization, store into subdivided structures at the first load
+    private Set<Link> links;
+
+    // use the singleton helper to create the instance
+    protected LinkNib() {
+    }
+
+    /**
+     * Sets a set of links.
+     *
+     * @param links link set
+     */
+    public void setLinks(Set<Link> links) {
+        this.links = links;
+    }
+
+    /**
+     * Returns the set of links.
+     *
+     * @return link set
+     */
+    public Set<Link> getLinks() {
+        return ImmutableSet.copyOf(links);
+    }
+
+    /**
+     * Returns set of all infrastructure links leading from the specified
+     * connection point.
+     *
+     * @param connectPoint connection point
+     * @return set of device egress links
+     */
+    public Set<Link> getEgressLinks(ConnectPoint connectPoint) {
+        Set<Link> egressLinks = links.stream()
+                .filter(link -> connectPoint.equals(link.src()))
+                .collect(Collectors.toSet());
+        return egressLinks != null ? ImmutableSet.copyOf(egressLinks) : ImmutableSet.of();
+    }
+
+    /**
+     * Returns the singleton instance of links NIB.
+     *
+     * @return instance of links NIB
+     */
+    public static LinkNib getInstance() {
+        return LinkNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final LinkNib INSTANCE = new LinkNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/MastershipNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/MastershipNib.java
new file mode 100644
index 0000000..5101d52
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/MastershipNib.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.net.DeviceId;
+
+import java.util.Map;
+
+/**
+ * Represents Network Information Base (NIB) for mastership
+ * and supports alternative functions to
+ * {@link org.onosproject.mastership.MastershipService} for offline data.
+ */
+public class MastershipNib {
+
+    private Map<DeviceId, NodeId> deviceMasterMap;
+
+    // use the singleton helper to create the instance
+    protected MastershipNib() {
+    }
+
+    /**
+     * Sets a map of device id : master node id.
+     *
+     * @param deviceMasterMap device-master map
+     */
+    public void setDeviceMasterMap(Map<DeviceId, NodeId> deviceMasterMap) {
+        this.deviceMasterMap = deviceMasterMap;
+    }
+
+    /**
+     * Returns the device-master map.
+     *
+     * @return device-master map
+     */
+    public Map<DeviceId, NodeId> getDeviceMasterMap() {
+        return ImmutableMap.copyOf(deviceMasterMap);
+    }
+
+    /**
+     * Returns the current master for a given device.
+     *
+     * @param deviceId the identifier of the device
+     * @return the ID of the master controller for the device
+     */
+    public NodeId getMasterFor(DeviceId deviceId) {
+        return deviceMasterMap.get(deviceId);
+    }
+
+    /**
+     * Returns the singleton instance of mastership NIB.
+     *
+     * @return instance of mastership NIB
+     */
+    public static MastershipNib getInstance() {
+        return MastershipNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final MastershipNib INSTANCE = new MastershipNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/MulticastRouteNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/MulticastRouteNib.java
new file mode 100644
index 0000000..b9d106d
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/MulticastRouteNib.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.mcast.api.McastRoute;
+import org.onosproject.mcast.api.McastRouteData;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents Network Information Base (NIB) for multicast routes
+ * and supports alternative functions to
+ * {@link org.onosproject.mcast.api.MulticastRouteService} for offline data.
+ */
+public class MulticastRouteNib {
+
+    private Map<McastRoute, McastRouteData> mcastRoutes;
+
+    // use the singleton helper to create the instance
+    protected MulticastRouteNib() {
+    }
+
+    public void setMcastRoutes(Map<McastRoute, McastRouteData> mcastRoutes) {
+        this.mcastRoutes = mcastRoutes;
+    }
+
+    public Map<McastRoute, McastRouteData> getMcastRoutes() {
+        return ImmutableMap.copyOf(mcastRoutes);
+    }
+
+    /**
+     * Gets all Multicast routes in the system.
+     *
+     * @return set of Multicast routes
+     */
+    public Set<McastRoute> getRoutes() {
+        return ImmutableSet.copyOf(mcastRoutes.keySet());
+    }
+
+    /**
+     * Return the Multicast data for this route.
+     *
+     * @param route route
+     * @return the mcast route data
+     */
+    public McastRouteData routeData(McastRoute route) {
+        return mcastRoutes.get(route);
+    }
+
+    /**
+     * Returns the singleton instance of multicast routes NIB.
+     *
+     * @return instance of multicast routes NIB
+     */
+    public static MulticastRouteNib getInstance() {
+        return MulticastRouteNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final MulticastRouteNib INSTANCE = new MulticastRouteNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/NetworkConfigNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/NetworkConfigNib.java
new file mode 100644
index 0000000..9624fb7
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/NetworkConfigNib.java
@@ -0,0 +1,117 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.basics.InterfaceConfig;
+import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
+import org.slf4j.Logger;
+
+import java.util.Map;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Represents Network Information Base (NIB) for network configurations
+ * and supports alternative functions to
+ * {@link org.onosproject.net.config.NetworkConfigService} for offline data.
+ */
+public class NetworkConfigNib {
+
+    private static final Logger log = getLogger(NetworkConfigNib.class);
+
+    // Map of str ConnectPoint : InterfaceConfig
+    private Map<String, Config> portConfigMap;
+    // Map of str DeviceId : SegmentRoutingDeviceConfig
+    private Map<String, Config> deviceConfigMap;
+
+    // use the singleton helper to create the instance
+    protected NetworkConfigNib() {
+    }
+
+    /**
+     * Sets a map of port : configuration to the port.
+     *
+     * @param portConfigMap port-config map
+     */
+    public void setPortConfigMap(Map<String, Config> portConfigMap) {
+         this.portConfigMap = portConfigMap;
+    }
+
+    /**
+     * Sets a map of device : configuration to the device.
+     *
+     * @param deviceConfigMap device-config map
+     */
+    public void setDeviceConfigMap(Map<String, Config> deviceConfigMap) {
+        this.deviceConfigMap = deviceConfigMap;
+    }
+
+    /**
+     * Returns the port-config map.
+     *
+     * @return port-config map
+     */
+    public Map<Object, Object> getPortConfigMap() {
+        return ImmutableMap.copyOf(portConfigMap);
+    }
+
+    /**
+     * Returns the device-config map.
+     *
+     * @return device-config map
+     */
+    public Map<Object, Object> getDeviceConfigMap() {
+        return ImmutableMap.copyOf(deviceConfigMap);
+    }
+
+    /**
+     * Returns the configuration for the specified subject and configuration
+     * class if one is available; null otherwise.
+     *
+     * @param subject     configuration subject
+     * @param configClass configuration class
+     * @param <S>         type of subject
+     * @param <C>         type of configuration
+     * @return configuration or null if one is not available
+     */
+    public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+        if (configClass.equals(InterfaceConfig.class)) {
+            return (C) portConfigMap.get(subject.toString());
+        } else if (configClass.equals(SegmentRoutingDeviceConfig.class)) {
+            return (C) deviceConfigMap.get(subject.toString());
+        } else {
+            log.warn("Given configuration {} is not supported", configClass.toString());
+            return null;
+        }
+    }
+
+    /**
+     * Returns the singleton instance of multicast routes NIB.
+     *
+     * @return instance of multicast routes NIB
+     */
+    public static NetworkConfigNib getInstance() {
+        return NetworkConfigNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final NetworkConfigNib INSTANCE = new NetworkConfigNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/RouteNib.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/RouteNib.java
new file mode 100644
index 0000000..3edabd3
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/RouteNib.java
@@ -0,0 +1,103 @@
+/*
+ * 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 org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.routeservice.ResolvedRoute;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents Network Information Base (NIB) for routes
+ * and supports alternative functions to
+ * {@link org.onosproject.routeservice.RouteService} for offline data.
+ */
+public class RouteNib {
+
+    // TODO with method optimization, store into subdivided structures at the first load
+    // unresolved Route is treated as ResolvedRoute with nextHopMac null
+    Set<ResolvedRoute> routes;
+
+    // use the singleton helper to create the instance
+    protected RouteNib() {
+    }
+
+    /**
+     * Sets a set of routes.
+     *
+     * @param routes route set
+     */
+    public void setRoutes(Set<ResolvedRoute> routes) {
+        this.routes = routes;
+    }
+
+    /**
+     * Returns the set of routes.
+     *
+     * @return route set
+     */
+    public Set<ResolvedRoute> getRoutes() {
+        return routes;
+    }
+
+    /**
+     * Performs a longest prefix lookup on the given IP address.
+     *
+     * @param ip IP address to look up
+     * @return most specific matching route, if one exists
+     */
+    public Optional<ResolvedRoute> longestPrefixLookup(IpAddress ip) {
+        return routes.stream()
+                .filter(r -> r.prefix().contains(ip))
+                .max(Comparator.comparing(r -> r.prefix().prefixLength()));
+    }
+
+    /**
+     * Returns all resolved routes stored for the given prefix, including the
+     * best selected route.
+     *
+     * @param prefix IP prefix to look up routes for
+     * @return all stored resolved routes for this prefix
+     */
+    public Collection<ResolvedRoute> getAllResolvedRoutes(IpPrefix prefix) {
+        return routes.stream()
+                .filter(r -> r.prefix().contains(prefix)
+                        && r.nextHopMac() != null
+                        && r.nextHopVlan() != null)
+                .collect(Collectors.toCollection(ArrayList::new));
+    }
+
+    /**
+     * Returns the singleton instance of multicast routes NIB.
+     *
+     * @return instance of multicast routes NIB
+     */
+    public static RouteNib getInstance() {
+        return RouteNib.SingletonHelper.INSTANCE;
+    }
+
+    private static class SingletonHelper {
+        private static final RouteNib INSTANCE = new RouteNib();
+    }
+
+}
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java b/apps/t3/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
index a21baf5..9838e04 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
@@ -83,4 +83,17 @@
      * @return a list of trace result
      */
     List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId);
+
+    /**
+     * Checks the availability of all NIBs of the manager.
+     *
+     * @return true if any NIB objects is unavailable
+     */
+    boolean checkNibsUnavailable();
+
+    /**
+     * Applies created NIBs to the manager.
+     */
+    void applyNibs();
+
 }
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadFileCommand.java b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadFileCommand.java
new file mode 100644
index 0000000..36c22d3
--- /dev/null
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootLoadFileCommand.java
@@ -0,0 +1,482 @@
+/*
+ * 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.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.PlaceholderCompleter;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mcast.api.McastRoute;
+import org.onosproject.mcast.api.McastRouteData;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.basics.InterfaceConfig;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.group.Group;
+import org.onosproject.routeservice.ResolvedRoute;
+import org.onosproject.routeservice.Route;
+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.RouteNib;
+import org.onosproject.t3.api.TroubleshootService;
+import org.slf4j.Logger;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Reads network states from JSON files of onos-diagnostics
+ * and sets them to corresponding Network Information Bases (NIBs).
+ */
+@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 {
+
+    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)
+    @Completion(PlaceholderCompleter.class)
+    String rootDir;
+
+    @Override
+    protected void doExecute() {
+
+        if (!rootDir.endsWith("/")) {
+            rootDir = rootDir + "/";
+        }
+        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");
+        } 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;
+        }
+    }
+
+    /**
+     * 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));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Map<McastRoute, McastRouteData> mcastRoutes = new HashMap<>();
+
+        // 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);
+        });
+
+        MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
+        mcastRouteNib.setMcastRoutes(mcastRoutes);
+        print("the number of mcast routes: %d", mcastRouteNib.getMcastRoutes().size());
+
+        stream.close();
+    }
+
+    /**
+     * Fetches network config-related information and creates the network config NIB.
+     *
+     * @param fileName absolute path of JSON file to read
+     */
+    private void createNetworkConfigNib(String fileName) throws IOException {
+        InputStream stream = new FileInputStream(new File(fileName));
+        JsonNode jsonTree = mapper().readTree(stream);
+        Map<String, Config> portConfigMap = new HashMap<>();
+        Map<String, Config> deviceConfigMap = new HashMap<>();
+
+        // note: the parsing structure depends on NetworkConfigCommand
+        // TODO: improve the code quality by referring to target json
+        jsonTree.fields().forEachRemaining(e -> {
+            if (e.getKey().equals("ports")) {
+                JsonNode portConfigsNode = e.getValue();
+                portConfigsNode.fields().forEachRemaining(portConfigEntry -> {
+                    String key = portConfigEntry.getKey();
+                    InterfaceConfig config = new InterfaceConfig();
+                    config.init(ConnectPoint.fromString(key), "interfaces",
+                            portConfigEntry.getValue().get("interfaces"), mapper(), null);
+                    portConfigMap.put(key, config);
+                });
+            } else if (e.getKey().equals("devices")) {
+                JsonNode deviceConfigsNode = e.getValue();
+                deviceConfigsNode.fields().forEachRemaining(deviceConfigEntry -> {
+                    String key = deviceConfigEntry.getKey();
+                    SegmentRoutingDeviceConfig config = new SegmentRoutingDeviceConfig();
+                    config.init(DeviceId.deviceId(key), "segmentrouting",
+                            deviceConfigEntry.getValue().get("segmentrouting"), mapper(), null);
+                    deviceConfigMap.put(key, config);
+                });
+            } else {
+                log.warn("Given configuration subject {} is not supported", e.getKey());
+            }
+        });
+
+        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));
+        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);
+        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<>());
+            }
+            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);
+            }
+            flows.add(flow);
+        });
+
+        FlowNib flowNib = FlowNib.getInstance();
+        flowNib.setFlows(flows);
+        print("the number of flows: %d", flowNib.getFlows().size());
+
+        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/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
index 325691e..3df110f 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
@@ -18,12 +18,14 @@
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
 import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.VlanId;
 import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.PlaceholderCompleter;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -45,23 +47,31 @@
         description = "Traces all the mcast routes present in the system")
 public class TroubleshootMcastCommand extends AbstractShellCommand {
 
-
     @Option(name = "-v", aliases = "--verbose", description = "Outputs trace for each mcast route")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity1 = false;
 
     @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs middle level details of every trace")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity2 = false;
 
     @Option(name = "-vvv", aliases = "--veryveryverbose", description = "Outputs complete details of every trace")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity3 = false;
 
     @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
+    @Completion(PlaceholderCompleter.class)
     String vlan = "None";
 
 
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
+        if (service.checkNibsUnavailable()) {
+            print(TroubleshootLoadFileCommand.ERROR_NULL);
+            return;
+        }
+
         print("Tracing all Multicast routes in the System");
 
         //Create the generator for the list of traces.
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
index 93e843a..3e7003f 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
@@ -23,6 +23,7 @@
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onlab.packet.IpAddress;
 import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.PlaceholderCompleter;
 import org.onosproject.cli.net.EthTypeCompleter;
 import org.onosproject.net.Host;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -54,17 +55,24 @@
     String ethType = "ipv4";
 
     @Option(name = "-v", aliases = "--verbose", description = "Outputs trace for each host to host combination")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity1 = false;
 
     @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs details of every trace")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity2 = false;
 
     @Option(name = "-d", aliases = "--delay", description = "delay between host to host trace display")
+    @Completion(PlaceholderCompleter.class)
     private long delay = 0;
 
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
+        if (service.checkNibsUnavailable()) {
+            print(TroubleshootLoadFileCommand.ERROR_NULL);
+            return;
+        }
 
         EtherType type = EtherType.valueOf(ethType.toUpperCase());
 
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
index 7b18913..7a5ecc7 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
@@ -22,6 +22,7 @@
 import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.PlaceholderCompleter;
 import org.onosproject.cli.net.EthTypeCompleter;
 import org.onosproject.cli.net.HostIdCompleter;
 import org.onosproject.net.HostId;
@@ -53,9 +54,11 @@
     String dstHost = null;
 
     @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity1 = false;
 
     @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity2 = false;
 
     @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
@@ -65,6 +68,14 @@
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
+        if (service.checkNibsUnavailable()) {
+            print(TroubleshootLoadFileCommand.ERROR_NULL);
+            return;
+        }
+        if (srcHost.equals(dstHost)) {
+            print("Source and destination are same. Use different hosts");
+            return;
+        }
 
         EtherType type = EtherType.valueOf(ethType.toUpperCase());
 
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
index ed11cbe..5da3f8c 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
@@ -18,6 +18,7 @@
 
 import com.google.common.base.Preconditions;
 import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
 import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onlab.packet.IpAddress;
@@ -26,6 +27,10 @@
 import org.onlab.packet.TpPort;
 import org.onlab.packet.VlanId;
 import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.PlaceholderCompleter;
+import org.onosproject.cli.net.ConnectPointCompleter;
+import org.onosproject.cli.net.EthTypeCompleter;
+import org.onosproject.cli.net.IpProtocolCompleter;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -56,56 +61,77 @@
     private static final String CONTROLLER = "CONTROLLER";
 
     @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity1 = false;
 
     @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
+    @Completion(PlaceholderCompleter.class)
     private boolean verbosity2 = false;
 
     @Option(name = "-s", aliases = "--srcIp", description = "Source IP")
+    @Completion(PlaceholderCompleter.class)
     String srcIp = null;
 
     @Option(name = "-sp", aliases = "--srcPort", description = "Source Port", required = true)
+    @Completion(ConnectPointCompleter.class)
     String srcPort = null;
 
     @Option(name = "-sm", aliases = "--srcMac", description = "Source MAC")
+    @Completion(PlaceholderCompleter.class)
     String srcMac = null;
 
     @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
+    @Completion(EthTypeCompleter.class)
     String ethType = "ipv4";
 
     @Option(name = "-stp", aliases = "--srcTcpPort", description = "Source TCP Port")
+    @Completion(PlaceholderCompleter.class)
     String srcTcpPort = null;
 
     @Option(name = "-d", aliases = "--dstIp", description = "Destination IP")
+    @Completion(PlaceholderCompleter.class)
     String dstIp = null;
 
     @Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
+    @Completion(PlaceholderCompleter.class)
     String dstMac = null;
 
     @Option(name = "-dtp", aliases = "--dstTcpPort", description = "destination TCP Port")
+    @Completion(PlaceholderCompleter.class)
     String dstTcpPort = null;
 
     @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
+    @Completion(PlaceholderCompleter.class)
     String vlan = "None";
 
     @Option(name = "-ml", aliases = "--mplsLabel", description = "Mpls label of incoming packet")
+    @Completion(PlaceholderCompleter.class)
     String mplsLabel = null;
 
     @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS")
+    @Completion(PlaceholderCompleter.class)
     String mplsBos = null;
 
     @Option(name = "-ipp", aliases = "--ipProto", description = "IP Proto")
+    @Completion(IpProtocolCompleter.class)
     String ipProto = null;
 
     @Option(name = "-udps", aliases = "--udpSrc", description = "UDP Source")
+    @Completion(PlaceholderCompleter.class)
     String udpSrc = null;
 
     @Option(name = "-udpd", aliases = "--udpDst", description = "UDP Destination")
+    @Completion(PlaceholderCompleter.class)
     String udpDst = null;
 
     @Override
     protected void doExecute() {
         TroubleshootService service = get(TroubleshootService.class);
+        if (service.checkNibsUnavailable()) {
+            print(TroubleshootLoadFileCommand.ERROR_NULL);
+            return;
+        }
+
         String[] cpInfo = srcPort.split("/");
         Preconditions.checkArgument(cpInfo.length == 2, "wrong format of source port");
         ConnectPoint cp;
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java b/apps/t3/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java
index e93af15..7520843 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java
@@ -22,9 +22,9 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.mcast.api.McastRouteData;
-import org.onosproject.mcast.api.MulticastRouteService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.t3.api.MulticastRouteNib;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.slf4j.Logger;
 
@@ -44,27 +44,27 @@
     private static final String GENERATOR_ERROR =
             "Generator for mcast route trace has benn interrupted. The trace result may be incomplete.";
 
-    private final MulticastRouteService mcastService;
+    private final MulticastRouteNib mcastRouteNib;
     private final TroubleshootManager manager;
     private final VlanId vlanId;
 
     /**
      * Creates a generator for obtaining traces of all configured multicast routes.
      *
-     * @param service the host service
+     * @param mcastRouteNib the multicast route NIB
      * @param manager the troubleshoot manager issuing the request.
      * @param vlanId  the multicast configured VlanId.
      */
-    McastGenerator(MulticastRouteService service, TroubleshootManager manager, VlanId vlanId) {
-        this.mcastService = service;
+    McastGenerator(MulticastRouteNib mcastRouteNib, TroubleshootManager manager, VlanId vlanId) {
+        this.mcastRouteNib = mcastRouteNib;
         this.manager = manager;
         this.vlanId = vlanId;
     }
 
     @Override
     protected void run() {
-        mcastService.getRoutes().forEach(route -> {
-            McastRouteData routeData = mcastService.routeData(route);
+        mcastRouteNib.getRoutes().forEach(route -> {
+            McastRouteData routeData = mcastRouteNib.routeData(route);
             IpAddress group = route.group();
             routeData.sources().forEach((host, sources) -> {
                 sources.forEach(source -> {
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java b/apps/t3/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java
index bf4d4b4..e935fad 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.Sets;
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
-import org.onosproject.net.host.HostService;
+import org.onosproject.t3.api.HostNib;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.slf4j.Logger;
 
@@ -36,30 +36,30 @@
     private static final Logger log = getLogger(PingAllGenerator.class);
 
     private final EthType.EtherType etherType;
-    private final HostService hostService;
+    private final HostNib hostNib;
     private final TroubleshootManager manager;
 
     /**
      * Creates a generator for obtaining traces of pings between all the hosts in the network.
      *
      * @param etherType the type of traffic we are tracing.
-     * @param service   the host service
+     * @param hostNib   the host NIB
      * @param manager   the troubleshoot manager issuing the request.
      */
-    PingAllGenerator(EthType.EtherType etherType, HostService service, TroubleshootManager manager) {
+    PingAllGenerator(EthType.EtherType etherType, HostNib hostNib, TroubleshootManager manager) {
         this.etherType = etherType;
-        this.hostService = service;
+        this.hostNib = hostNib;
         this.manager = manager;
     }
 
     @Override
     protected void run() throws InterruptedException {
-        hostService.getHosts().forEach(host -> {
+        hostNib.getHosts().forEach(host -> {
             List<IpAddress> ipAddresses = manager.getIpAddresses(host, etherType, false);
             if (ipAddresses.size() > 0) {
                 //check if the host has only local IPs of that ETH type
                 boolean onlyLocalSrc = ipAddresses.size() == 1 && ipAddresses.get(0).isLinkLocal();
-                hostService.getHosts().forEach(hostToPing -> {
+                hostNib.getHosts().forEach(hostToPing -> {
                     List<IpAddress> ipAddressesToPing = manager.getIpAddresses(hostToPing, etherType, false);
                     //check if the other host has only local IPs of that ETH type
                     boolean onlyLocalDst = ipAddressesToPing.size() == 1 && ipAddressesToPing.get(0).isLinkLocal();
diff --git a/apps/t3/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/apps/t3/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index 51332da..b1359bc 100644
--- a/apps/t3/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/apps/t3/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -25,8 +25,6 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.cluster.NodeId;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.mcast.api.MulticastRouteService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
@@ -35,15 +33,10 @@
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.config.ConfigException;
-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.DefaultTrafficSelector;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.flow.IndexTableId;
 import org.onosproject.net.flow.TableId;
 import org.onosproject.net.flow.TrafficSelector;
@@ -59,20 +52,25 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.group.Group;
 import org.onosproject.net.group.GroupBucket;
-import org.onosproject.net.group.GroupService;
-import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.intf.Interface;
-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.GroupsInDevice;
+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.RouteNib;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.onosproject.t3.api.TroubleshootService;
 import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
 import java.net.UnknownHostException;
@@ -85,6 +83,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 import static org.onlab.packet.EthType.EtherType;
@@ -109,48 +108,50 @@
 
     static final String PACKET_TO_CONTROLLER = "Packet goes to the controller";
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected FlowRuleService flowRuleService;
+    // 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;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected GroupService groupService;
+    @Override
+    public boolean checkNibsUnavailable() {
+        return Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
+                mastershipNib, edgePortNib, routeNib, networkConfigNib, mcastRouteNib)
+                .anyMatch(x -> x == null);
+    }
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected LinkService linkService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected DriverService driverService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected NetworkConfigService networkConfigService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected EdgePortService edgePortService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected RouteService routeService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected MulticastRouteService mcastService;
+    @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();
+    }
 
     @Override
     public List<StaticPacketTrace> pingAll(EtherType type) {
         ImmutableList.Builder<StaticPacketTrace> tracesBuilder = ImmutableList.builder();
-        hostService.getHosts().forEach(host -> {
+        hostNib.getHosts().forEach(host -> {
             List<IpAddress> ipAddresses = getIpAddresses(host, type, false);
             if (ipAddresses.size() > 0) {
                 //check if the host has only local IPs of that ETH type
                 boolean onlyLocalSrc = ipAddresses.size() == 1 && ipAddresses.get(0).isLinkLocal();
-                hostService.getHosts().forEach(hostToPing -> {
+                hostNib.getHosts().forEach(hostToPing -> {
                     List<IpAddress> ipAddressesToPing = getIpAddresses(hostToPing, type, false);
                     //check if the other host has only local IPs of that ETH type
                     boolean onlyLocalDst = ipAddressesToPing.size() == 1 && ipAddressesToPing.get(0).isLinkLocal();
@@ -170,18 +171,18 @@
 
     @Override
     public Generator<Set<StaticPacketTrace>> pingAllGenerator(EtherType type) {
-        return new PingAllGenerator(type, hostService, this);
+        return new PingAllGenerator(type, hostNib, this);
     }
 
     @Override
     public Generator<Set<StaticPacketTrace>> traceMcast(VlanId vlanId) {
-        return new McastGenerator(mcastService, this, vlanId);
+        return new McastGenerator(mcastRouteNib, this, vlanId);
     }
 
     @Override
     public Set<StaticPacketTrace> trace(HostId sourceHost, HostId destinationHost, EtherType etherType) {
-        Host source = hostService.getHost(sourceHost);
-        Host destination = hostService.getHost(destinationHost);
+        Host source = hostNib.getHost(sourceHost);
+        Host destination = hostNib.getHost(destinationHost);
 
         //Temporary trace to fail in case we don't have enough information or what is provided is incoherent
         StaticPacketTrace failTrace = new StaticPacketTrace(null, null, Pair.of(source, destination));
@@ -243,7 +244,7 @@
             }
 
             //l3 unicast, we get the dst mac of the leaf the source is connected to from netcfg
-            SegmentRoutingDeviceConfig segmentRoutingConfig = networkConfigService.getConfig(source.location()
+            SegmentRoutingDeviceConfig segmentRoutingConfig = networkConfigNib.getConfig(source.location()
                     .deviceId(), SegmentRoutingDeviceConfig.class);
             if (segmentRoutingConfig != null) {
                 selectorBuilder.matchEthDst(segmentRoutingConfig.routerMac());
@@ -337,8 +338,8 @@
             return false;
         }
 
-        InterfaceConfig interfaceCfgH1 = networkConfigService.getConfig(source.location(), InterfaceConfig.class);
-        InterfaceConfig interfaceCfgH2 = networkConfigService.getConfig(destination.location(), InterfaceConfig.class);
+        InterfaceConfig interfaceCfgH1 = networkConfigNib.getConfig(source.location(), InterfaceConfig.class);
+        InterfaceConfig interfaceCfgH2 = networkConfigNib.getConfig(destination.location(), InterfaceConfig.class);
         if (interfaceCfgH1 != null && interfaceCfgH2 != null) {
 
             //following can be optimized but for clarity is left as is
@@ -376,7 +377,7 @@
     public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
         log.info("Tracing packet {} coming in through {}", packet, in);
         //device must exist in ONOS
-        Preconditions.checkNotNull(deviceService.getDevice(in.deviceId()),
+        Preconditions.checkNotNull(deviceNib.getDevice(in.deviceId()),
                 "Device " + in.deviceId() + " must exist in ONOS");
 
         StaticPacketTrace trace = new StaticPacketTrace(packet, in);
@@ -391,7 +392,7 @@
 
     @Override
     public List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId) {
-        Generator<Set<StaticPacketTrace>> gen = new McastGenerator(mcastService, this, vlanId);
+        Generator<Set<StaticPacketTrace>> gen = new McastGenerator(mcastRouteNib, this, vlanId);
         List<Set<StaticPacketTrace>> multicastTraceList =
                 StreamSupport.stream(gen.spliterator(), false).collect(Collectors.toList());
         return multicastTraceList;
@@ -440,7 +441,7 @@
             log.debug("{}", outputPath.getFinalPacket());
 
             //Hosts for the the given output
-            Set<Host> hostsList = hostService.getConnectedHosts(cp);
+            Set<Host> hostsList = hostNib.getConnectedHosts(cp);
             //Hosts queried from the original ip or mac
             Set<Host> hosts = getHosts(trace);
 
@@ -465,12 +466,13 @@
             } else if (cp.port().equals(PortNumber.CONTROLLER)) {
 
                 //Getting the master when the packet gets sent as packet in
-                NodeId master = mastershipService.getMasterFor(cp.deviceId());
+                NodeId master = mastershipNib.getMasterFor(cp.deviceId());
+                // TODO if we don't need to print master node id, exclude mastership NIB which is used only here
                 trace.addResultMessage(PACKET_TO_CONTROLLER + " " + master.id());
                 computePath(completePath, trace, outputPath.getOutput());
                 handleVlanToController(outputPath, trace);
 
-            } else if (linkService.getEgressLinks(cp).size() > 0) {
+            } else if (linkNib.getEgressLinks(cp).size() > 0) {
 
                 //TODO this can be optimized if we use a Tree structure for paths.
                 //if we already have outputs let's check if the one we are considering starts from one of the devices
@@ -502,7 +504,7 @@
                 //let's add the ouput for the input
                 completePath.add(cp);
                 //let's compute the links for the given output
-                Set<Link> links = linkService.getEgressLinks(cp);
+                Set<Link> links = linkNib.getEgressLinks(cp);
                 log.debug("Egress Links {}", links);
                 //For each link we trace the corresponding device
                 for (Link link : links) {
@@ -517,7 +519,7 @@
                     //continue the trace along the path
                     getTrace(completePath, dst, trace, isDualHomed);
                 }
-            } else if (edgePortService.isEdgePoint(outputPath.getOutput()) &&
+            } else if (edgePortNib.isEdgePoint(outputPath.getOutput()) &&
                     trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST) != null &&
                     ((EthCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST))
                             .mac().isMulticast()) {
@@ -528,7 +530,7 @@
                 if (!hasOtherOutput(in.deviceId(), trace, outputPath.getOutput())) {
                     return trace;
                 }
-            } else if (deviceService.getPort(cp) != null && deviceService.getPort(cp).isEnabled()) {
+            } else if (deviceNib.getPort(cp) != null && deviceNib.getPort(cp).isEnabled()) {
                 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
                         .getCriterion(Criterion.Type.ETH_TYPE);
                 //We treat as correct output only if it's not LLDP or BDDP
@@ -551,7 +553,7 @@
                             if (ipAddress != null) {
                                 IpAddress finalIpAddress = ipAddress;
                                 if (hostsList.stream().anyMatch(host -> host.ipAddresses().contains(finalIpAddress)) ||
-                                        hostService.getHostsByIp(finalIpAddress).isEmpty()) {
+                                        hostNib.getHostsByIp(finalIpAddress).isEmpty()) {
                                     trace.addResultMessage("Packet is " +
                                             ((EthTypeCriterion) outputPath.getFinalPacket()
                                                     .getCriterion(Criterion.Type.ETH_TYPE)).ethType() +
@@ -574,7 +576,7 @@
             } else {
                 computePath(completePath, trace, cp);
                 trace.setSuccess(false);
-                if (deviceService.getPort(cp) == null) {
+                if (deviceNib.getPort(cp) == null) {
                     //Port is not existent on device.
                     log.warn("Port {} is not available on device.", cp);
                     trace.addResultMessage("Port " + cp + "is not available on device. Packet is dropped");
@@ -660,15 +662,15 @@
                 .getCriterion(Criterion.Type.IPV6_DST));
         Set<Host> hosts = new HashSet<>();
         if (ipv4Criterion != null) {
-            hosts.addAll(hostService.getHostsByIp(ipv4Criterion.ip().address()));
+            hosts.addAll(hostNib.getHostsByIp(ipv4Criterion.ip().address()));
         }
         if (ipv6Criterion != null) {
-            hosts.addAll(hostService.getHostsByIp(ipv6Criterion.ip().address()));
+            hosts.addAll(hostNib.getHostsByIp(ipv6Criterion.ip().address()));
         }
         EthCriterion ethCriterion = ((EthCriterion) trace.getInitialPacket()
                 .getCriterion(Criterion.Type.ETH_DST));
         if (ethCriterion != null) {
-            hosts.addAll(hostService.getHostsByMac(ethCriterion.mac()));
+            hosts.addAll(hostNib.getHostsByMac(ethCriterion.mac()));
         }
         return hosts;
     }
@@ -727,7 +729,7 @@
         log.debug("Packet {} coming in from {}", packet, in);
 
         //if device is not available exit here.
-        if (!deviceService.isAvailable(in.deviceId())) {
+        if (!deviceNib.isAvailable(in.deviceId())) {
             trace.addResultMessage("Device is offline " + in.deviceId());
             computePath(completePath, trace, null);
             return trace;
@@ -765,7 +767,7 @@
             log.debug("Found Flow Entry {}", flowEntry);
 
             boolean isOfdpaHardware = TroubleshootUtils.hardwareOfdpaMap
-                    .getOrDefault(driverService.getDriver(in.deviceId()).name(), false);
+                    .getOrDefault(driverNib.getDriverName(in.deviceId()), false);
 
             //if the flow entry on a table is null and we are on hardware we treat as table miss, with few exceptions
             if (flowEntry == null && isOfdpaHardware) {
@@ -945,11 +947,10 @@
             ip = ((IPCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.IPV6_DST)).ip().address();
         }
         if (ip != null) {
-            Optional<ResolvedRoute> optionalRoute = routeService.longestPrefixLookup(ip);
+            Optional<ResolvedRoute> optionalRoute = routeNib.longestPrefixLookup(ip);
             if (optionalRoute.isPresent()) {
                 ResolvedRoute route = optionalRoute.get();
-                route.prefix();
-                multipleRoutes = routeService.getAllResolvedRoutes(route.prefix()).size() > 1;
+                multipleRoutes = routeNib.getAllResolvedRoutes(route.prefix()).size() > 1;
             }
         }
         return multipleRoutes;
@@ -972,7 +973,7 @@
         if (ethTypeCriterion != null && (ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
                 || ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
             //get the active ports
-            List<Port> enabledPorts = deviceService.getPorts(in.deviceId()).stream()
+            List<Port> enabledPorts = deviceNib.getPorts(in.deviceId()).stream()
                     .filter(Port::isEnabled)
                     .collect(Collectors.toList());
             //build an output from each one
@@ -1012,9 +1013,9 @@
         if (packetVlanIdCriterion.vlanId().equals(entryModVlanIdInstruction.vlanId())) {
             //find a rule on the same table that matches the vlan and
             // also all the other elements of the flow such as input port
-            secondVlanFlow = Lists.newArrayList(flowRuleService.getFlowEntriesByState(in.deviceId(),
-                    FlowEntry.FlowEntryState.ADDED).iterator())
-                    .stream()
+            secondVlanFlow = Lists.newArrayList(flowNib.getFlowEntriesByState(in.deviceId(),
+                    FlowEntry.FlowEntryState.ADDED)
+                    .iterator()).stream()
                     .filter(entry -> {
                         return entry.table().equals(IndexTableId.of(10));
                     })
@@ -1065,8 +1066,7 @@
     private FlowEntry findNextTableIdEntry(DeviceId deviceId, int currentId) {
 
         final Comparator<FlowEntry> comparator = Comparator.comparing((FlowEntry f) -> ((IndexTableId) f.table()).id());
-
-        return Lists.newArrayList(flowRuleService.getFlowEntriesByState(deviceId, FlowEntry.FlowEntryState.ADDED)
+        return Lists.newArrayList(flowNib.getFlowEntriesByState(deviceId, FlowEntry.FlowEntryState.ADDED)
                 .iterator()).stream()
                 .filter(f -> ((IndexTableId) f.table()).id() > currentId).min(comparator).orElse(null);
     }
@@ -1149,7 +1149,7 @@
         //handle all the internal instructions pointing to a group.
         for (Instruction instr : groupInstructionlist) {
             GroupInstruction groupInstruction = (GroupInstruction) instr;
-            Group group = Lists.newArrayList(groupService.getGroups(deviceId)).stream().filter(groupInternal -> {
+            Group group = Lists.newArrayList(groupNib.getGroups(deviceId)).stream().filter(groupInternal -> {
                 return groupInternal.id().equals(groupInstruction.groupId());
             }).findAny().orElse(null);
             if (group == null) {
@@ -1312,7 +1312,7 @@
     private FlowEntry matchHighestPriority(TrafficSelector packet, ConnectPoint in, TableId tableId) {
         //Computing the possible match rules.
         final Comparator<FlowEntry> comparator = Comparator.comparing(FlowRule::priority);
-        return Lists.newArrayList(flowRuleService.getFlowEntriesByState(in.deviceId(), FlowEntry.FlowEntryState.ADDED)
+        return Lists.newArrayList(flowNib.getFlowEntriesByState(in.deviceId(), FlowEntry.FlowEntryState.ADDED)
                 .iterator()).stream()
                 .filter(flowEntry -> {
                     return flowEntry.table().equals(tableId);