Support pretty JSON output for openstack node and networking apps

Change-Id: Id837ce5730a4073d207c1ec68913f953deb6a190
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
index ae30021..99a95c0 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
@@ -26,7 +25,7 @@
 
 import java.util.List;
 
-import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
 
 /**
  * Lists external peer router lists.
@@ -54,11 +53,11 @@
         }
     }
 
-    private JsonNode json(AbstractShellCommand context, List<ExternalPeerRouter> routers) {
+    private String json(AbstractShellCommand context, List<ExternalPeerRouter> routers) {
         ObjectMapper mapper = new ObjectMapper();
-        ArrayNode result = mapper.enable(INDENT_OUTPUT).createArrayNode();
+        ArrayNode result = mapper.createArrayNode();
         routers.forEach(r -> result.add(context.jsonForEntity(r, ExternalPeerRouter.class)));
 
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/InstancePortListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/InstancePortListCommand.java
index 369b27a..8b0d111 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/InstancePortListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/InstancePortListCommand.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
@@ -27,7 +26,7 @@
 import java.util.Comparator;
 import java.util.List;
 
-import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
 
 /**
  * Lists OpenStack instance ports.
@@ -55,11 +54,11 @@
         }
     }
 
-    private JsonNode json(AbstractShellCommand context, List<InstancePort> ports) {
+    private String json(AbstractShellCommand context, List<InstancePort> ports) {
         ObjectMapper mapper = new ObjectMapper();
-        ArrayNode result = mapper.enable(INDENT_OUTPUT).createArrayNode();
+        ArrayNode result = mapper.createArrayNode();
         ports.forEach(p -> result.add(context.jsonForEntity(p, InstancePort.class)));
 
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
index 8569122..5f954ef 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
@@ -77,8 +77,8 @@
     }
 
     private void purgeRules() {
-        FlowRuleService flowRuleService = AbstractShellCommand.get(FlowRuleService.class);
-        CoreService coreService = AbstractShellCommand.get(CoreService.class);
+        FlowRuleService flowRuleService = get(FlowRuleService.class);
+        CoreService coreService = get(CoreService.class);
         ApplicationId appId = coreService.getAppId(Constants.OPENSTACK_NETWORKING_APP_ID);
         if (appId == null) {
             error("Failed to purge OpenStack networking flow rules.");
@@ -101,8 +101,8 @@
     private void syncRules() {
         // All handlers in this application reacts the node complete event and
         // tries to re-configure flow rules for the complete node.
-        OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
-        OpenstackNodeAdminService osNodeAdminService = AbstractShellCommand.get(OpenstackNodeAdminService.class);
+        OpenstackNodeService osNodeService = get(OpenstackNodeService.class);
+        OpenstackNodeAdminService osNodeAdminService = get(OpenstackNodeAdminService.class);
         if (osNodeService == null) {
             error("Failed to re-install flow rules for OpenStack networking.");
             return;
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
index 3270ac4..b30cf1e 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
@@ -15,8 +15,7 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
@@ -29,8 +28,8 @@
 import java.util.Comparator;
 import java.util.List;
 
-import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.modelEntityToJson;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
 
 /**
  * Lists OpenStack floating IP addresses.
@@ -48,28 +47,24 @@
         floatingIps.sort(Comparator.comparing(NetFloatingIP::getFloatingIpAddress));
 
         if (outputJson()) {
-            try {
-                print("%s", mapper().writeValueAsString(json(floatingIps)));
-            } catch (JsonProcessingException e) {
-                print("Failed to list networks in JSON format");
+            print("%s", json(floatingIps));
+        } else {
+            print(FORMAT, "ID", "Floating IP", "Fixed IP");
+            for (NetFloatingIP floatingIp: floatingIps) {
+                print(FORMAT, floatingIp.getId(),
+                        floatingIp.getFloatingIpAddress(),
+                        Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
+                                "" : floatingIp.getFixedIpAddress());
             }
-            return;
-        }
-
-        print(FORMAT, "ID", "Floating IP", "Fixed IP");
-        for (NetFloatingIP floatingIp: floatingIps) {
-            print(FORMAT, floatingIp.getId(),
-                    floatingIp.getFloatingIpAddress(),
-                    Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
-            "" : floatingIp.getFixedIpAddress());
         }
     }
 
-    private JsonNode json(List<NetFloatingIP> floatingIps) {
-        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+    private String json(List<NetFloatingIP> floatingIps) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
         for (NetFloatingIP floatingIp: floatingIps) {
             result.add(modelEntityToJson(floatingIp, NeutronFloatingIP.class));
         }
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
index b383a11..4784da8 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
@@ -15,8 +15,7 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Command;
@@ -30,8 +29,8 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.modelEntityToJson;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
 
 /**
  * Lists OpenStack networks.
@@ -49,44 +48,40 @@
         networks.sort(Comparator.comparing(Network::getName));
 
         if (outputJson()) {
-            try {
-                print("%s", mapper().writeValueAsString(json(networks)));
-            } catch (JsonProcessingException e) {
-                print("Failed to list networks in JSON format");
+            print("%s", json(networks));
+        } else {
+            print(FORMAT, "ID", "Name", "Network Mode", "VNI", "Subnets", "HostRoutes");
+            for (Network net: networks) {
+                List<Subnet> subnets = service.subnets().stream()
+                        .filter(subnet -> subnet.getNetworkId().equals(net.getId()))
+                        .collect(Collectors.toList());
+
+                List<String> subnetsString = subnets.stream()
+                        .map(Subnet::getCidr)
+                        .collect(Collectors.toList());
+
+                List<String> hostRoutes = Lists.newArrayList();
+
+                subnets.forEach(subnet -> {
+                    subnet.getHostRoutes().forEach(h -> hostRoutes.add(h.toString()));
+                });
+
+                print(FORMAT, net.getId(),
+                        net.getName(),
+                        net.getNetworkType().toString(),
+                        net.getProviderSegID(),
+                        subnets.isEmpty() ? "" : subnetsString,
+                        hostRoutes.isEmpty() ? "" : hostRoutes);
             }
-            return;
-        }
-
-        print(FORMAT, "ID", "Name", "Network Mode", "VNI", "Subnets", "HostRoutes");
-        for (Network net: networks) {
-            List<Subnet> subnets = service.subnets().stream()
-                    .filter(subnet -> subnet.getNetworkId().equals(net.getId()))
-                    .collect(Collectors.toList());
-
-            List<String> subnetsString = subnets.stream()
-                    .map(Subnet::getCidr)
-                    .collect(Collectors.toList());
-
-            List<String> hostRoutes = Lists.newArrayList();
-
-            subnets.forEach(subnet -> {
-                subnet.getHostRoutes().forEach(h -> hostRoutes.add(h.toString()));
-            });
-
-            print(FORMAT, net.getId(),
-                    net.getName(),
-                    net.getNetworkType().toString(),
-                    net.getProviderSegID(),
-                    subnets.isEmpty() ? "" : subnetsString,
-                    hostRoutes.isEmpty() ? "" : hostRoutes);
         }
     }
 
-    private JsonNode json(List<Network> networks) {
-        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+    private String json(List<Network> networks) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
         for (Network net: networks) {
             result.add(modelEntityToJson(net, NeutronNetwork.class));
         }
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
index 4583878..1861f44 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
@@ -15,8 +15,7 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
@@ -33,8 +32,8 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.modelEntityToJson;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
 
 /**
  * Lists OpenStack ports.
@@ -60,32 +59,29 @@
         }
 
         if (outputJson()) {
-            try {
-                print("%s", mapper().writeValueAsString(json(ports)));
-            } catch (JsonProcessingException e) {
-                print("Failed to list ports in JSON format");
-            }
-            return;
-        }
+            print("%s", json(ports));
 
-        print(FORMAT, "ID", "Network", "MAC", "Fixed IPs");
-        for (Port port: ports) {
-            List<String> fixedIps = port.getFixedIps().stream()
-                    .map(IP::getIpAddress)
-                    .collect(Collectors.toList());
-            Network osNet = service.network(port.getNetworkId());
-            print(FORMAT, port.getId(),
-                    osNet.getName(),
-                    port.getMacAddress(),
-                    fixedIps.isEmpty() ? "" : fixedIps);
+        } else {
+            print(FORMAT, "ID", "Network", "MAC", "Fixed IPs");
+            for (Port port: ports) {
+                List<String> fixedIps = port.getFixedIps().stream()
+                        .map(IP::getIpAddress)
+                        .collect(Collectors.toList());
+                Network osNet = service.network(port.getNetworkId());
+                print(FORMAT, port.getId(),
+                        osNet.getName(),
+                        port.getMacAddress(),
+                        fixedIps.isEmpty() ? "" : fixedIps);
+            }
         }
     }
 
-    private JsonNode json(List<Port> ports) {
-        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+    private String json(List<Port> ports) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
         for (Port port: ports) {
             result.add(modelEntityToJson(port, NeutronPort.class));
         }
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
index 3a69a3b..a1aca70 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
@@ -15,8 +15,7 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Command;
@@ -34,8 +33,8 @@
 import java.util.Objects;
 import java.util.stream.Collectors;
 
-import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.modelEntityToJson;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
 
 /**
  * Lists OpenStack routers.
@@ -57,34 +56,30 @@
         routers.sort(Comparator.comparing(Router::getName));
 
         if (outputJson()) {
-            try {
-                print("%s", mapper().writeValueAsString(json(routers)));
-            } catch (JsonProcessingException e) {
-                print("Failed to list routers in JSON format");
-            }
-            return;
-        }
-        print(FORMAT, "ID", "Name", "External", "Internal");
-        for (Router router: routers) {
-            String exNetId = router.getExternalGatewayInfo() != null ?
-                router.getExternalGatewayInfo().getNetworkId() : null;
+            print("%s", json(routers));
+        } else {
+            print(FORMAT, "ID", "Name", "External", "Internal");
+            for (Router router: routers) {
+                String exNetId = router.getExternalGatewayInfo() != null ?
+                        router.getExternalGatewayInfo().getNetworkId() : null;
 
-            List<String> externals = Lists.newArrayList();
-            if (exNetId != null) {
-                // FIXME fix openstack4j to provide external gateway ip info
-                externals = netService.ports(exNetId).stream()
-                        .filter(port -> Objects.equals(port.getDeviceId(),
-                                router.getId()))
-                        .flatMap(port -> port.getFixedIps().stream())
-                        .map(IP::getIpAddress)
-                        .collect(Collectors.toList());
-            }
+                List<String> externals = Lists.newArrayList();
+                if (exNetId != null) {
+                    // FIXME fix openstack4j to provide external gateway ip info
+                    externals = netService.ports(exNetId).stream()
+                            .filter(port -> Objects.equals(port.getDeviceId(),
+                                    router.getId()))
+                            .flatMap(port -> port.getFixedIps().stream())
+                            .map(IP::getIpAddress)
+                            .collect(Collectors.toList());
+                }
 
-            List<String> internals = Lists.newArrayList();
-            routerService.routerInterfaces(router.getId()).forEach(iface -> {
+                List<String> internals = Lists.newArrayList();
+                routerService.routerInterfaces(router.getId()).forEach(iface -> {
                     internals.add(getRouterIfaceIp(iface));
-            });
-            print(FORMAT, router.getId(), router.getName(), externals, internals);
+                });
+                print(FORMAT, router.getId(), router.getName(), externals, internals);
+            }
         }
     }
 
@@ -96,11 +91,12 @@
         return ipAddr == null ? "" : ipAddr.getIpAddress();
     }
 
-    private JsonNode json(List<Router> routers) {
-        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+    private String json(List<Router> routers) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
         for (Router router: routers) {
             result.add(modelEntityToJson(router, NeutronRouter.class));
         }
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSecurityGroupListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSecurityGroupListCommand.java
index 735cc87..b20408c 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSecurityGroupListCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSecurityGroupListCommand.java
@@ -15,8 +15,7 @@
  */
 package org.onosproject.openstacknetworking.cli;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Argument;
@@ -29,8 +28,8 @@
 import java.util.Comparator;
 import java.util.List;
 
-import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.modelEntityToJson;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
 
 /**
  * Lists OpenStack security groups.
@@ -53,26 +52,22 @@
         sgs.sort(Comparator.comparing(SecurityGroup::getId));
 
         if (outputJson()) {
-            try {
-                print("%s", mapper().writeValueAsString(json(sgs)));
-            } catch (JsonProcessingException e) {
-                error("Failed to list security groups in JSON format");
+            print("%s", json(sgs));
+        } else {
+            print("Hint: use --json option to see security group rules as well\n");
+            print(FORMAT, "ID", "Name");
+            for (SecurityGroup sg: service.securityGroups()) {
+                print(FORMAT, sg.getId(), sg.getName());
             }
-            return;
-        }
-
-        print("Hint: use --json option to see security group rules as well\n");
-        print(FORMAT, "ID", "Name");
-        for (SecurityGroup sg: service.securityGroups()) {
-            print(FORMAT, sg.getId(), sg.getName());
         }
     }
 
-    private JsonNode json(List<SecurityGroup> securityGroups) {
-        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+    private String json(List<SecurityGroup> securityGroups) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
         for (SecurityGroup sg: securityGroups) {
             result.add(modelEntityToJson(sg, NeutronSecurityGroup.class));
         }
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index c384a44..1338587 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -369,6 +369,23 @@
     }
 
     /**
+     * Prints out the JSON string in pretty format.
+     *
+     * @param mapper        Object mapper
+     * @param jsonString    JSON string
+     * @return pretty formatted JSON string
+     */
+    public static String prettyJson(ObjectMapper mapper, String jsonString) {
+        try {
+            Object jsonObject = mapper.readValue(jsonString, Object.class);
+            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
+        } catch (IOException e) {
+            log.debug("Json string parsing exception caused by {}", e);
+        }
+        return null;
+    }
+
+    /**
      * Checks the validity of ARP mode.
      *
      * @param arpMode ARP mode
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
index 937ce3d..2463e26 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
@@ -16,7 +16,6 @@
 
 package org.onosproject.openstacknode.cli;
 
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
@@ -28,6 +27,8 @@
 import java.util.Comparator;
 import java.util.List;
 
+import static org.onosproject.openstacknode.util.OpenstackNodeUtil.prettyJson;
+
 /**
  * Lists all nodes registered to the service.
  */
@@ -63,12 +64,12 @@
         }
     }
 
-    private JsonNode json(List<OpenstackNode> osNodes) {
+    private String json(List<OpenstackNode> osNodes) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
         for (OpenstackNode osNode : osNodes) {
             result.add(jsonForEntity(osNode, OpenstackNode.class));
         }
-        return result;
+        return prettyJson(mapper, result.toString());
     }
 }
\ No newline at end of file
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/util/OpenstackNodeUtil.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/util/OpenstackNodeUtil.java
index 6012c6f..095d2bc 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/util/OpenstackNodeUtil.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/util/OpenstackNodeUtil.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.openstacknode.util;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Strings;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.openstacknode.api.OpenstackAuth;
@@ -38,6 +39,7 @@
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
 import java.security.cert.X509Certificate;
 import java.util.Dictionary;
 
@@ -156,6 +158,23 @@
     }
 
     /**
+     * Prints out the JSON string in pretty format.
+     *
+     * @param mapper        Object mapper
+     * @param jsonString    JSON string
+     * @return pretty formatted JSON string
+     */
+    public static String prettyJson(ObjectMapper mapper, String jsonString) {
+        try {
+            Object jsonObject = mapper.readValue(jsonString, Object.class);
+            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
+        } catch (IOException e) {
+            log.debug("Json string parsing exception caused by {}", e);
+        }
+        return null;
+    }
+
+    /**
      * Builds up and a complete endpoint URL from gateway node.
      *
      * @param node gateway node