[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/mcast/cli/BUILD b/apps/mcast/cli/BUILD
index 9a9d616..553eb5b 100644
--- a/apps/mcast/cli/BUILD
+++ b/apps/mcast/cli/BUILD
@@ -1,4 +1,4 @@
-COMPILE_DEPS = CORE_DEPS + CLI + REST + [
+COMPILE_DEPS = CORE_DEPS + JACKSON + CLI + REST + [
     "//core/store/serializers:onos-core-serializers",
     "//apps/mcast/api:onos-apps-mcast-api",
 ]
diff --git a/apps/mcast/cli/src/main/java/org/onosproject/mcast/cli/McastShowHostCommand.java b/apps/mcast/cli/src/main/java/org/onosproject/mcast/cli/McastShowHostCommand.java
index 7ab5da0..2261378 100644
--- a/apps/mcast/cli/src/main/java/org/onosproject/mcast/cli/McastShowHostCommand.java
+++ b/apps/mcast/cli/src/main/java/org/onosproject/mcast/cli/McastShowHostCommand.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.mcast.cli;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
 import org.apache.karaf.shell.api.action.Option;
@@ -29,7 +31,6 @@
 import java.util.Comparator;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
 
@@ -41,7 +42,9 @@
 public class McastShowHostCommand extends AbstractShellCommand {
 
     // Format for group line
-    private static final String FORMAT_MAPPING = "origin=%s, group=%s, source IP=%s, sources=%s, sinks=%s";
+    private static final String FORMAT_MAPPING = "origin=%s, group=%s, source IP=%s, sources=%s, sinks=%s\n";
+    private StringBuilder routesBuilder = new StringBuilder();
+    private ArrayNode routesNode = mapper().createArrayNode();
 
     @Option(name = "-gAddr", aliases = "--groupAddress",
             description = "IP Address of the multicast group",
@@ -65,41 +68,43 @@
                     .findAny().orElse(null);
             // If it exists
             if (mcastRoute != null) {
-                printRoute(mcastService, mcastRoute);
+                prepareResult(mcastService, mcastRoute);
             }
-            return;
+        } else {
+            routes.stream()
+                    .filter(mcastRoute -> mcastRoute.group().isIp4())
+                    .sorted(Comparator.comparing(McastRoute::group))
+                    .forEach(route -> {
+                        prepareResult(mcastService, route);
+                    });
+            routes.stream()
+                    .filter(mcastRoute -> mcastRoute.group().isIp6())
+                    .sorted(Comparator.comparing(McastRoute::group))
+                    .forEach(route -> {
+                        prepareResult(mcastService, route);
+                    });
         }
-        // Filter ipv4
-        Set<McastRoute> ipv4Routes = routes.stream()
-                .filter(mcastRoute -> mcastRoute.group().isIp4())
-                .collect(Collectors.toSet());
-        // Print ipv4 first
-        ipv4Routes.stream()
-                .sorted(Comparator.comparing(McastRoute::group))
-                .forEach(route -> {
-                    printRoute(mcastService, route);
-                });
-        // Filter ipv6
-        Set<McastRoute> ipv6Routes = routes.stream()
-                .filter(mcastRoute -> mcastRoute.group().isIp6())
-                .collect(Collectors.toSet());
-        // Then print ipv6
-        ipv6Routes.stream()
-                .sorted(Comparator.comparing(McastRoute::group))
-                .forEach(route -> {
-                    printRoute(mcastService, route);
-                });
+        if (outputJson()) {
+            print("%s", routesNode);
+        } else {
+            print("%s", routesBuilder.toString());
+        }
     }
 
-    private void printRoute(MulticastRouteService mcastService, McastRoute route) {
-        Map<HostId, Set<ConnectPoint>> sinks = mcastService.routeData(route).sinks();
-        Map<HostId, Set<ConnectPoint>> sources = mcastService.routeData(route).sources();
-        String srcIp = "*";
-        if (route.source().isPresent()) {
-            srcIp = route.source().get().toString();
+    private void prepareResult(MulticastRouteService mcastService, McastRoute route) {
+        if (outputJson()) {
+            // McastHostRouteCodec is used to encode McastRoute
+            ObjectNode routeNode = jsonForEntity(route, McastRoute.class);
+            routesNode.add(routeNode);
+        } else {
+            Map<HostId, Set<ConnectPoint>> sinks = mcastService.routeData(route).sinks();
+            Map<HostId, Set<ConnectPoint>> sources = mcastService.routeData(route).sources();
+            String srcIp = "*";
+            if (route.source().isPresent()) {
+                srcIp = route.source().get().toString();
+            }
+            routesBuilder.append(String.format(FORMAT_MAPPING, route.type(), route.group(), srcIp, sources, sinks));
         }
-
-        print(FORMAT_MAPPING, route.type(), route.group(), srcIp, sources, sinks);
     }
 
 }
diff --git a/apps/route-service/app/src/main/java/org/onosproject/routeservice/cli/RoutesListCommand.java b/apps/route-service/app/src/main/java/org/onosproject/routeservice/cli/RoutesListCommand.java
index e2cb714..cb7ff17 100644
--- a/apps/route-service/app/src/main/java/org/onosproject/routeservice/cli/RoutesListCommand.java
+++ b/apps/route-service/app/src/main/java/org/onosproject/routeservice/cli/RoutesListCommand.java
@@ -23,6 +23,7 @@
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.routeservice.ResolvedRoute;
+import org.onosproject.routeservice.Route;
 import org.onosproject.routeservice.RouteInfo;
 import org.onosproject.routeservice.RouteService;
 import org.onosproject.routeservice.RouteTableId;
@@ -113,23 +114,17 @@
 
         routes.stream()
                 .flatMap(ri -> ri.allRoutes().stream())
-                .forEach(r -> result.add(json(mapper, r)));
-
-        return result;
-    }
-
-    /**
-     * Produces JSON object for a route.
-     *
-     * @param mapper the JSON object mapper to use
-     * @param route the route with the data
-     * @return JSON object for the route
-     */
-    private ObjectNode json(ObjectMapper mapper, ResolvedRoute route) {
-        ObjectNode result = mapper.createObjectNode();
-
-        result.put("prefix", route.prefix().toString());
-        result.put("nextHop", route.nextHop().toString());
+                .forEach(r -> {
+                    // use RouteCodec to encode the Route object inside ResolvedRoute
+                    ObjectNode routeNode = jsonForEntity(r.route(), Route.class);
+                    if (r.nextHopMac() != null) {
+                        routeNode.put("nextHopMac", r.nextHopMac().toString());
+                    }
+                    if (r.nextHopVlan() != null) {
+                        routeNode.put("nextHopVlan", r.nextHopVlan().toString());
+                    }
+                    result.add(routeNode);
+                });
 
         return result;
     }
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);
diff --git a/apps/t3/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java b/apps/t3/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
index c00d373..d1eba21 100644
--- a/apps/t3/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
+++ b/apps/t3/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
@@ -25,7 +25,6 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.cluster.NodeId;
-import org.onosproject.mastership.MastershipServiceAdapter;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultDevice;
@@ -37,29 +36,27 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceServiceAdapter;
-import org.onosproject.net.driver.DefaultDriver;
-import org.onosproject.net.driver.Driver;
-import org.onosproject.net.driver.DriverServiceAdapter;
-import org.onosproject.net.edge.EdgePortServiceAdapter;
 import org.onosproject.net.flow.FlowEntry;
-import org.onosproject.net.flow.FlowRuleServiceAdapter;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.EthTypeCriterion;
 import org.onosproject.net.flow.criteria.MplsCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupServiceAdapter;
-import org.onosproject.net.host.HostServiceAdapter;
-import org.onosproject.net.link.LinkServiceAdapter;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.RouteServiceAdapter;
+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.RouteNib;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.slf4j.Logger;
 
-import java.util.HashMap;
 import java.util.Optional;
 import java.util.Set;
 
@@ -84,24 +81,28 @@
     @Before
     public void setUp() throws Exception {
         mngr = new TroubleshootManager();
-        mngr.flowRuleService = new TestFlowRuleService();
-        mngr.hostService = new TestHostService();
-        mngr.linkService = new TestLinkService();
-        mngr.driverService = new TestDriverService();
-        mngr.groupService = new TestGroupService();
-        mngr.deviceService = new TestDeviceService();
-        mngr.mastershipService = new TestMastershipService();
-        mngr.edgePortService = new TestEdgePortService();
-        mngr.routeService = new TestRouteService();
+
+        mngr.flowNib = new TestFlowRuleService();
+        mngr.groupNib = new TestGroupService();
+        mngr.hostNib = new TestHostService();
+        mngr.linkNib = new TestLinkService();
+        mngr.deviceNib = new TestDeviceService();
+        mngr.driverNib = new TestDriverService();
+        mngr.mastershipNib = new TestMastershipService();
+        mngr.edgePortNib = new TestEdgePortService();
+        mngr.routeNib = new TestRouteService();
 
         assertNotNull("Manager should not be null", mngr);
 
-        assertNotNull("Flow rule Service should not be null", mngr.flowRuleService);
-        assertNotNull("Host Service should not be null", mngr.hostService);
-        assertNotNull("Group Service should not be null", mngr.groupService);
-        assertNotNull("Driver Service should not be null", mngr.driverService);
-        assertNotNull("Link Service should not be null", mngr.linkService);
-        assertNotNull("Device Service should not be null", mngr.deviceService);
+        assertNotNull("Flow rule Service should not be null", mngr.flowNib);
+        assertNotNull("Group Service should not be null", mngr.groupNib);
+        assertNotNull("Host Service should not be null", mngr.hostNib);
+        assertNotNull("Link Service should not be null", mngr.linkNib);
+        assertNotNull("Device Service should not be null", mngr.deviceNib);
+        assertNotNull("Driver Service should not be null", mngr.driverNib);
+        assertNotNull("Mastership Service should not be null", mngr.driverNib);
+        assertNotNull("EdgePort Service should not be null", mngr.driverNib);
+        assertNotNull("Route Service should not be null", mngr.routeNib);
     }
 
     /**
@@ -436,7 +437,7 @@
         assertNull("Trace should have 0 output", traceFail.getGroupOuputs(deviceId));
     }
 
-    private class TestFlowRuleService extends FlowRuleServiceAdapter {
+    private class TestFlowRuleService extends FlowNib {
         @Override
         public Iterable<FlowEntry> getFlowEntriesByState(DeviceId deviceId, FlowEntry.FlowEntryState state) {
             if (deviceId.equals(SINGLE_FLOW_DEVICE)) {
@@ -486,19 +487,7 @@
         }
     }
 
-    private class TestDriverService extends DriverServiceAdapter {
-        @Override
-        public Driver getDriver(DeviceId deviceId) {
-            if (deviceId.equals(HARDWARE_DEVICE) || deviceId.equals(HARDWARE_DEVICE_10)) {
-                return new DefaultDriver("ofdpa", ImmutableList.of(),
-                        "test", "test", "test", new HashMap<>(), new HashMap<>());
-            }
-            return new DefaultDriver("NotHWDriver", ImmutableList.of(),
-                    "test", "test", "test", new HashMap<>(), new HashMap<>());
-        }
-    }
-
-    private class TestGroupService extends GroupServiceAdapter {
+    private class TestGroupService extends GroupNib {
         @Override
         public Iterable<Group> getGroups(DeviceId deviceId) {
             if (deviceId.equals(GROUP_FLOW_DEVICE)) {
@@ -520,7 +509,7 @@
         }
     }
 
-    private class TestHostService extends HostServiceAdapter {
+    private class TestHostService extends HostNib {
         @Override
         public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
             if (connectPoint.equals(TOPO_FLOW_3_OUT_CP)) {
@@ -570,7 +559,7 @@
         }
     }
 
-    private class TestLinkService extends LinkServiceAdapter {
+    private class TestLinkService extends LinkNib {
         @Override
         public Set<Link> getEgressLinks(ConnectPoint connectPoint) {
             if (connectPoint.equals(TOPO_FLOW_1_OUT_CP)
@@ -649,7 +638,7 @@
         }
     }
 
-    private class TestDeviceService extends DeviceServiceAdapter {
+    private class TestDeviceService extends DeviceNib {
         @Override
         public Device getDevice(DeviceId deviceId) {
             if (deviceId.equals(DeviceId.deviceId("nonexistent"))) {
@@ -671,8 +660,24 @@
         }
     }
 
-    private class TestEdgePortService extends EdgePortServiceAdapter {
+    private class TestDriverService extends DriverNib {
+        @Override
+        public String getDriverName(DeviceId deviceId) {
+            if (deviceId.equals(HARDWARE_DEVICE) || deviceId.equals(HARDWARE_DEVICE_10)) {
+                return "ofdpa";
+            }
+            return "NotHWDriver";
+        }
+    }
 
+    private class TestMastershipService extends MastershipNib {
+        @Override
+        public NodeId getMasterFor(DeviceId deviceId) {
+            return NodeId.nodeId(MASTER_1);
+        }
+    }
+
+    private class TestEdgePortService extends EdgePortNib {
         @Override
         public boolean isEdgePoint(ConnectPoint point) {
             return point.equals(MULTICAST_OUT_CP) ||
@@ -680,17 +685,11 @@
         }
     }
 
-    private class TestRouteService extends RouteServiceAdapter {
+    private class TestRouteService extends RouteNib {
         @Override
         public Optional<ResolvedRoute> longestPrefixLookup(IpAddress ip) {
             return Optional.empty();
         }
     }
 
-    private class TestMastershipService extends MastershipServiceAdapter {
-        @Override
-        public NodeId getMasterFor(DeviceId deviceId) {
-            return NodeId.nodeId(MASTER_1);
-        }
-    }
 }
\ No newline at end of file
diff --git a/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java
index 86887f7..f3d63bd 100644
--- a/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java
@@ -114,6 +114,7 @@
         for (Port port : service.getPorts(device.id())) {
             if (isIncluded(port)) {
                 ports.add(mapper.createObjectNode()
+                                  .put("element", device.id().toString())
                                   .put("port", port.number().toString())
                                   .put("isEnabled", port.isEnabled())
                                   .put("type", port.type().toString().toLowerCase())
diff --git a/cli/src/main/java/org/onosproject/cli/net/EdgePortsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/EdgePortsListCommand.java
index 6f7ae26..2f24ceb 100644
--- a/cli/src/main/java/org/onosproject/cli/net/EdgePortsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/EdgePortsListCommand.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.cli.net;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
@@ -56,7 +57,17 @@
     }
 
     private void printEdgePoints(Iterable<ConnectPoint> edgePoints) {
-        sort(edgePoints).forEach(e -> print(FMT, e.deviceId(), e.port()));
+        List<ConnectPoint> sorted = sort(edgePoints);
+        if (outputJson()) {
+            ArrayNode result = mapper().createObjectNode().putArray(null);
+            sorted.forEach(e -> {
+                result.add(mapper().createObjectNode()
+                        .put(e.deviceId().toString(), e.port().toString()));
+            });
+            print("%s", result.toString());
+        } else {
+            sorted.forEach(e -> print(FMT, e.deviceId(), e.port()));
+        }
     }
 
     private static List<ConnectPoint> sort(Iterable<ConnectPoint> connectPoints) {
diff --git a/core/common/BUILD b/core/common/BUILD
index 9abdc58..be6cac4 100644
--- a/core/common/BUILD
+++ b/core/common/BUILD
@@ -1,4 +1,6 @@
-COMPILE_DEPS = CORE_DEPS + JACKSON + METRICS
+COMPILE_DEPS = CORE_DEPS + JACKSON + METRICS + KRYO + [
+    "//core/store/serializers:onos-core-serializers",
+]
 
 TEST_DEPS = TEST + ["//core/api:onos-api-tests"]
 
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
index 24b749c..93cfe5d 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
@@ -82,6 +82,7 @@
     static final String PI_MATCH_MASK = "mask";
     static final String PI_MATCH_HIGH_VALUE = "highValue";
     static final String PI_MATCH_LOW_VALUE = "lowValue";
+    static final String EXTENSION = "extension";
 
     @Override
     public ObjectNode encode(Criterion criterion, CodecContext context) {
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
index f690d5d..9516ed3 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.codec.impl;
 
+import com.esotericsoftware.kryo.io.Input;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.packet.Ip6Address;
@@ -33,20 +34,28 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.ExtensionCriterion;
 import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.slf4j.Logger;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
 import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Decode portion of the criterion codec.
  */
 public final class DecodeCriterionCodecHelper {
 
+    private static final Logger log = getLogger(DecodeCriterionCodecHelper.class);
+
     private final ObjectNode json;
 
     protected static final String MISSING_MEMBER_MESSAGE =
@@ -114,6 +123,7 @@
         decoderMap.put(Criterion.Type.ODU_SIGID.name(), new OduSigIdDecoder());
         decoderMap.put(Criterion.Type.ODU_SIGTYPE.name(), new OduSigTypeDecoder());
         decoderMap.put(Criterion.Type.PROTOCOL_INDEPENDENT.name(), new PiDecoder());
+        decoderMap.put(Criterion.Type.EXTENSION.name(), new ExtensionDecoder());
     }
 
     private class EthTypeDecoder implements CriterionDecoder {
@@ -130,6 +140,7 @@
             return Criteria.matchEthType(ethType);
         }
     }
+
     private class EthDstDecoder implements CriterionDecoder {
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
@@ -139,6 +150,7 @@
             return Criteria.matchEthDst(mac);
         }
     }
+
     private class EthDstMaskedDecoder implements CriterionDecoder {
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
@@ -149,6 +161,7 @@
             return Criteria.matchEthDstMasked(mac, macMask);
         }
     }
+
     private class EthSrcDecoder implements CriterionDecoder {
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
@@ -669,6 +682,24 @@
         }
     }
 
+    private class ExtensionDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            try {
+                byte[] buffer = nullIsIllegal(json.get(CriterionCodec.EXTENSION),
+                        CriterionCodec.EXTENSION + MISSING_MEMBER_MESSAGE).binaryValue();
+                Input input = new Input(new ByteArrayInputStream(buffer));
+                ExtensionCriterion extensionCriterion =
+                        KryoNamespaces.API.borrow().readObject(input, ExtensionCriterion.class);
+                input.close();
+                return extensionCriterion;
+            } catch (IOException e) {
+                log.warn("Cannot convert the {} field into byte array", CriterionCodec.EXTENSION);
+                return null;
+            }
+        }
+    }
+
     /**
      * Decodes the JSON into a criterion object.
      *
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
index 2933ca9..cfe5447 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
@@ -137,7 +137,10 @@
             long tunnelId = nullIsIllegal(json.get(InstructionCodec.TUNNEL_ID),
                     InstructionCodec.TUNNEL_ID + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong();
             return Instructions.modTunnelId(tunnelId);
+        } else if (subType.equals(L2ModificationInstruction.L2SubType.MPLS_BOS.name())) {
+            return Instructions.modMplsBos(json.get("bos").asBoolean());
         }
+
         throw new IllegalArgumentException("L2 Instruction subtype "
                 + subType + " is not supported");
     }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
index d34092a..c42dcfd 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DeviceCodec.java
@@ -20,6 +20,7 @@
 import org.onlab.packet.ChassisId;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.Device;
 import org.onosproject.net.Device.Type;
@@ -99,4 +100,26 @@
         return new DefaultDevice(pid, id, type, mfr, hw, sw, serial,
                                  chassisId, annotations);
     }
+
+    /**
+     * Extracts annotations of given Object.
+     *
+     * @param deviceNode annotated JSON object node representing a device
+     * @param context decode context
+     * @return extracted Annotations
+     */
+    @Override
+    protected Annotations extractAnnotations(ObjectNode deviceNode, CodecContext context) {
+        ObjectNode annotationsNode = get(deviceNode, "annotations");
+        if (annotationsNode != null) {
+            // add needed fields to the annotations of the Device object
+            if (deviceNode.get(AVAILABLE) != null) {
+                annotationsNode.put(AVAILABLE, deviceNode.get(AVAILABLE).asText());
+            }
+            return context.codec(Annotations.class).decode(annotationsNode, context);
+        } else {
+            return DefaultAnnotations.EMPTY;
+        }
+    }
+
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
index 067a99a..c99e353 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.codec.impl;
 
+import com.esotericsoftware.kryo.io.Output;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.util.HexString;
@@ -57,7 +58,9 @@
 import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
 import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
 import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
+import org.onosproject.store.serializers.KryoNamespaces;
 
+import java.io.ByteArrayOutputStream;
 import java.util.EnumMap;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -133,6 +136,7 @@
         formatMap.put(Criterion.Type.ODU_SIGID, new FormatOduSignalId());
         formatMap.put(Criterion.Type.ODU_SIGTYPE, new FormatOduSignalType());
         formatMap.put(Criterion.Type.PROTOCOL_INDEPENDENT, new FormatProtocolIndependent());
+        formatMap.put(Criterion.Type.EXTENSION, new FormatExtension());
         // Currently unimplemented
         formatMap.put(Criterion.Type.ARP_OP, new FormatUnknown());
         formatMap.put(Criterion.Type.ARP_SPA, new FormatUnknown());
@@ -146,7 +150,6 @@
         formatMap.put(Criterion.Type.TCP_FLAGS, new FormatUnknown());
         formatMap.put(Criterion.Type.ACTSET_OUTPUT, new FormatUnknown());
         formatMap.put(Criterion.Type.PACKET_TYPE, new FormatUnknown());
-        formatMap.put(Criterion.Type.EXTENSION, new FormatUnknown());
         formatMap.put(Criterion.Type.ETH_SRC_MASKED, new FormatUnknown());
         formatMap.put(Criterion.Type.TCP_SRC_MASKED, new FormatUnknown());
         formatMap.put(Criterion.Type.TCP_DST_MASKED, new FormatUnknown());
@@ -579,6 +582,19 @@
         }
     }
 
+    private class FormatExtension implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            Output output = new Output(new ByteArrayOutputStream());
+            KryoNamespaces.API.borrow().writeObject(output, criterion);
+            root.put(CriterionCodec.EXTENSION, output.toBytes());
+            output.flush();
+            output.close();
+
+            return root;
+        }
+    }
+
     private class FormatDummyType implements CriterionTypeFormatter {
 
         @Override
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
index a30068e..589a386 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
@@ -309,6 +309,8 @@
         }
 
         if (device.is(ExtensionTreatmentCodec.class)) {
+            // for extension instructions, encoding device id is needed for the corresponding decoder
+            result.put("deviceId", deviceId.toString());
             ExtensionTreatmentCodec treatmentCodec = device.as(ExtensionTreatmentCodec.class);
             ObjectNode node = treatmentCodec.encode(extensionInstruction.extensionInstruction(), context);
             result.set(InstructionCodec.EXTENSION, node);
diff --git a/tools/package/runtime/bin/onos-diagnostics b/tools/package/runtime/bin/onos-diagnostics
index 2ac2310..0daa320 100755
--- a/tools/package/runtime/bin/onos-diagnostics
+++ b/tools/package/runtime/bin/onos-diagnostics
@@ -20,7 +20,7 @@
 # Tool to collect cluster-wide diagnostics into a single tar stream.
 # -----------------------------------------------------------------------------
 function usage() {
-    echo "usage: $(basename $0) [-x] [-n name] [-P port] [-u user] [-p password] [ip1 ip2...]"
+    echo "usage: $(basename $0) [-x] [-j] [-n name] [-P port] [-u user] [-p password] [ip1 ip2...]"
     echo ""
     echo "Environment Variables:"
     echo "    ONOS_INSTANCES    IPs or hostnames of ONOS cluster machines"
@@ -41,6 +41,11 @@
     echo "    # The cluster node IPs will be drawn from ONOS_INSTANCES variable."
     echo "    > $(basename $0) -x -n prague -u onos -p rules"
     echo ""
+    echo "    # Collect diagnostics for the cluster and store them in JSON files."
+    echo "    # JSON_CLI_COMMANDS below lists JSON-supported diagnostics commands."
+    echo "    # Collection archive will be named /tmp/onos-diags.tar.gz"
+    echo "    > $(basename $0) -j"
+    echo ""
     echo "    # Collect compressed diagnostics for a cluster."
     echo "    # REST API user name is 'onos' and password is 'rules'."
     echo "    # Collection archive will be named /tmp/onos-diags.tar.gz"
@@ -75,14 +80,15 @@
     "hosts"
     "interfaces"
 
-    "ports -e"
+    "ports"
     "portstats -nz"
+    "edge-ports"
 
     "packet-processors"
     "packet-requests"
 
     "intents"
-    "flows -s"
+    "flows"
     "groups"
 
     "roles"
@@ -118,18 +124,34 @@
     "mcast-host-show"
 )
 
+JSON_CLI_COMMANDS=(
+    "netcfg -j"
+    "devices -j"
+    "device-drivers -j"
+    "links -j"
+    "hosts -j"
+    "ports -j"
+    "edge-ports -j"
+    "flows -j"
+    "groups -j"
+    "masters -j"
+    "routes -j"
+    "mcast-host-show -j"
+)
+
 port=${ONOS_WEB_PORT}
 user=${ONOS_WEB_USER}
 password=${ONOS_WEB_PASS}
 
 # Scan arguments for user/password or other options...
-while getopts n:P:u:p:x?h o; do
+while getopts n:P:u:p:x?j?h o; do
     case "$o" in
         n) name=$OPTARG;;
         P) port=$OPTARG;;
         u) user=$OPTARG;;
         p) password=$OPTARG;;
         x) extract=true;;
+        j) json=true;;
         *) usage;;
     esac
 done
@@ -158,8 +180,10 @@
     tar zxf ../$node.tar.gz && rm ../$node.tar.gz
 
     # Acquire remotely obtained diagnostics via ssh CLI
+    [ ! -z $json ] && CLI_COMMANDS=("${JSON_CLI_COMMANDS[@]}")
     for cmd in "${CLI_COMMANDS[@]}"; do
         cmdLog="$(echo $cmd | cut -d\  -f1 | sed 's/:/-/g').txt"
+        [ ! -z $json ] && cmdLog="${cmdLog%.txt}.json"
         printf "$cmdLog "
         onos $node $cmd 2>/dev/null >$cmdLog
     done