You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by mb...@apache.org on 2016/09/09 03:35:34 UTC

asterixdb git commit: Extend Cluster API Servlet, += Config / Stats

Repository: asterixdb
Updated Branches:
  refs/heads/master 78c6115d1 -> f902d66f1


Extend Cluster API Servlet, += Config / Stats

- New APIs to return NC stats & configs, discoverable URIs returned as
part of /admin/cluster.
- Tests for constant info (still need non-exact match tests for config /
  stats endpoints)

Change-Id: Ia7549f2bb0b6621886356d50df800d447928aa2c
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1147
Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <ti...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/f902d66f
Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/f902d66f
Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/f902d66f

Branch: refs/heads/master
Commit: f902d66f1c7f57f21a93ebebf5c258900e482459
Parents: 78c6115
Author: Michael Blow <mb...@apache.org>
Authored: Thu Sep 8 13:48:26 2016 -0400
Committer: Michael Blow <mb...@apache.org>
Committed: Thu Sep 8 20:34:51 2016 -0700

----------------------------------------------------------------------
 .../api/http/servlet/ClusterAPIServlet.java     |  36 +++-
 .../servlet/ClusterNodeDetailsAPIServlet.java   | 163 +++++++++++++++++++
 .../bootstrap/CCApplicationEntryPoint.java      |  20 ++-
 .../cluster_state_3.1.cstate.aql                |  25 +++
 .../cluster_state_4.1.cstate.aql                |  25 +++
 .../api/cluster_state_1/cluster_state_1.1.adm   |  28 +++-
 .../api/cluster_state_3/cluster_state_3.1.adm   |  20 +++
 .../api/cluster_state_4/cluster_state_4.1.adm   |   9 +
 .../src/test/resources/runtimets/testsuite.xml  |  10 ++
 .../asterix/common/utils/ServletUtil.java       |   3 +-
 .../runtime/util/AsterixClusterProperties.java  |  15 +-
 .../client/HyracksClientInterfaceFunctions.java |  33 +++-
 .../HyracksClientInterfaceRemoteProxy.java      |   7 +
 .../hyracks/api/client/HyracksConnection.java   |   6 +
 .../api/client/IHyracksClientConnection.java    |   9 +
 .../api/client/IHyracksClientInterface.java     |   1 +
 .../control/cc/ClusterControllerService.java    |  23 ++-
 .../hyracks/control/cc/NodeControllerState.java |  98 ++++++-----
 .../control/cc/web/NodesRESTAPIFunction.java    |   2 +-
 .../control/cc/work/GetNodeDetailsJSONWork.java |  25 ++-
 .../common/controllers/NodeRegistration.java    |  13 +-
 .../hyracks/control/common/utils/PidHelper.java |  52 ++++++
 .../control/nc/NodeControllerService.java       |   4 +-
 23 files changed, 543 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java
index 8a16cd7..8cedabc 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java
@@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.asterix.app.result.ResultUtil;
 import org.apache.asterix.common.config.AbstractAsterixProperties;
 import org.apache.asterix.runtime.util.AsterixClusterProperties;
+import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -42,13 +43,16 @@ public class ClusterAPIServlet extends HttpServlet {
         response.setContentType("application/json");
         response.setCharacterEncoding("utf-8");
         PrintWriter responseWriter = response.getWriter();
+        JSONObject json;
+
         try {
-            JSONObject responseObject = AsterixClusterProperties.INSTANCE.getClusterStateDescription();
-            Map<String, Object> allProperties = getAllClusterProperties();
-            responseObject.put("config", allProperties);
-            responseWriter.write(responseObject.toString(4));
+            json = getClusterStateJSON(request, "node/");
             response.setStatus(HttpServletResponse.SC_OK);
-        } catch (JSONException e) {
+            responseWriter.write(json.toString(4));
+        } catch (IllegalArgumentException e) {
+            ResultUtil.apiErrorHandler(responseWriter, e);
+            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+        } catch (Exception e) {
             ResultUtil.apiErrorHandler(responseWriter, e);
             response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
         }
@@ -66,4 +70,26 @@ public class ClusterAPIServlet extends HttpServlet {
     protected List<AbstractAsterixProperties> getPropertiesInstances() {
         return AbstractAsterixProperties.getImplementations();
     }
+
+    protected JSONObject getClusterStateJSON(HttpServletRequest request, String pathToNode)
+            throws JSONException {
+        JSONObject json;
+        json = AsterixClusterProperties.INSTANCE.getClusterStateDescription();
+        Map<String, Object> allProperties = getAllClusterProperties();
+        json.put("config", allProperties);
+
+        JSONArray ncs = json.getJSONArray("ncs");
+        final StringBuffer requestURL = request.getRequestURL();
+        if (requestURL.charAt(requestURL.length() - 1) != '/') {
+            requestURL.append('/');
+        }
+        requestURL.append(pathToNode);
+        String nodeURL = requestURL.toString().replaceAll("/[^./]+/\\.\\./", "/");
+        for (int i = 0; i < ncs.length(); i++) {
+            JSONObject nc = ncs.getJSONObject(i);
+            nc.put("configUri", nodeURL + nc.getString("node_id") + "/config");
+            nc.put("statsUri", nodeURL + nc.getString("node_id") + "/stats");
+        }
+        return json;
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterNodeDetailsAPIServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterNodeDetailsAPIServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterNodeDetailsAPIServlet.java
new file mode 100644
index 0000000..9cccdad
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterNodeDetailsAPIServlet.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.api.http.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.asterix.app.result.ResultUtil;
+import org.apache.hyracks.api.client.IHyracksClientConnection;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
+
+public class ClusterNodeDetailsAPIServlet extends ClusterAPIServlet {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        PrintWriter responseWriter = response.getWriter();
+        ServletContext context = getServletContext();
+        IHyracksClientConnection hcc = (IHyracksClientConnection) context.getAttribute(HYRACKS_CONNECTION_ATTR);
+        JSONObject json;
+
+        try {
+            if (request.getPathInfo() == null) {
+                json = new JSONObject();
+                json.put("ncs", getClusterStateJSON(request, "").getJSONArray("ncs"));
+            } else {
+                json = processNode(request, hcc);
+            }
+            response.setStatus(HttpServletResponse.SC_OK);
+            response.setContentType("application/json");
+            response.setCharacterEncoding("utf-8");
+            responseWriter.write(json.toString(4));
+        } catch (IllegalArgumentException e) { //NOSONAR - exception not logged or rethrown
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+        } catch (Exception e) {
+            ResultUtil.apiErrorHandler(responseWriter, e);
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        }
+        responseWriter.flush();
+    }
+
+    private JSONObject processNode(HttpServletRequest request, IHyracksClientConnection hcc)
+            throws Exception {
+        String[] parts = request.getPathInfo().substring(1).replaceAll("/+", "/").split("/");
+        final String node = parts[0];
+
+        if (parts.length == 1) {
+            JSONArray ncs = getClusterStateJSON(request, "../").getJSONArray("ncs");
+            for (int i = 0; i < ncs.length(); i++) {
+                JSONObject json = ncs.getJSONObject(i);
+                if (node.equals(json.getString("node_id"))) {
+                    return json;
+                }
+            }
+            if ("cc".equals(node)) {
+                return new JSONObject();
+            }
+            throw new IllegalArgumentException();
+        } else if (parts.length == 2) {
+            JSONObject json;
+
+            switch (parts[1]) {
+                case "config":
+                    json = processNodeConfig(hcc, node);
+                    break;
+                case "stats":
+                    json = processNodeStats(hcc, node);
+                    break;
+                default:
+                    throw new IllegalArgumentException();
+            }
+            fixupKeys(json);
+
+            return json;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private void fixupKeys(JSONObject json) throws JSONException {
+        // TODO (mblow): generate the keys with _ to begin with
+        List<String> keys = new ArrayList<>();
+        for (Iterator iter = json.keys(); iter.hasNext(); ) {
+            keys.add((String) iter.next());
+        }
+        for (String key : keys) {
+            String newKey = key.replace('-', '_');
+            if (!newKey.equals(key)) {
+                json.put(newKey, json.remove(key));
+            }
+        }
+    }
+
+    private JSONObject processNodeStats(IHyracksClientConnection hcc, String node) throws Exception {
+        if ("cc".equals(node)) {
+            return new JSONObject();
+        }
+
+        final String details = hcc.getNodeDetailsJSON(node, true, false);
+        if (details == null) {
+            throw new IllegalArgumentException();
+        }
+        JSONObject json = new JSONObject(details);
+        int index = json.getInt("rrd-ptr") - 1;
+        json.remove("rrd-ptr");
+
+        List<String> keys = new ArrayList<>();
+        for (Iterator iter = json.keys(); iter.hasNext(); ) {
+            keys.add((String) iter.next());
+        }
+
+        int gcNames = json.getJSONArray("gc-names").length();
+        for (String key : keys) {
+            if (key.startsWith("gc-collection-")) {
+                final JSONArray gcArray = json.getJSONArray(key);
+                for (int i = 0; i < gcNames; i++) {
+                    gcArray.put(i, gcArray.getJSONArray(i).get(index));
+                }
+            } else if (!"node-id".equals(key) && !"gc-names".equals(key)) {
+                json.put(key, json.getJSONArray(key).get(index));
+            }
+        }
+        return json;
+    }
+
+    private JSONObject processNodeConfig(IHyracksClientConnection hcc, String node) throws Exception {
+        if ("cc".equals(node)) {
+            return new JSONObject();
+        }
+        String config = hcc.getNodeDetailsJSON(node, false, true);
+        if (config == null) {
+            throw new IllegalArgumentException();
+        }
+        return new JSONObject(config);
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplicationEntryPoint.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplicationEntryPoint.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplicationEntryPoint.java
index 5b36782..9883960 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplicationEntryPoint.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplicationEntryPoint.java
@@ -18,9 +18,6 @@
  */
 package org.apache.asterix.hyracks.bootstrap;
 
-import static org.apache.asterix.api.http.servlet.ServletConstants.ASTERIX_BUILD_PROP_ATTR;
-import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Level;
@@ -32,6 +29,7 @@ import org.apache.asterix.active.ActiveLifecycleListener;
 import org.apache.asterix.api.http.servlet.APIServlet;
 import org.apache.asterix.api.http.servlet.AQLAPIServlet;
 import org.apache.asterix.api.http.servlet.ClusterAPIServlet;
+import org.apache.asterix.api.http.servlet.ClusterNodeDetailsAPIServlet;
 import org.apache.asterix.api.http.servlet.ConnectorAPIServlet;
 import org.apache.asterix.api.http.servlet.DDLAPIServlet;
 import org.apache.asterix.api.http.servlet.FeedServlet;
@@ -72,6 +70,10 @@ import org.apache.hyracks.control.cc.ClusterControllerService;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletMapping;
+
+import static org.apache.asterix.api.http.servlet.ServletConstants.ASTERIX_BUILD_PROP_ATTR;
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
 
 public class CCApplicationEntryPoint implements ICCApplicationEntryPoint {
 
@@ -216,6 +218,7 @@ public class CCApplicationEntryPoint implements ICCApplicationEntryPoint {
         addServlet(context, Servlets.SHUTDOWN);
         addServlet(context, Servlets.VERSION);
         addServlet(context, Servlets.CLUSTER_STATE);
+        addServlet(context, Servlets.CLUSTER_STATE_NODE_DETAIL);
 
         return jsonAPIServer;
     }
@@ -235,8 +238,13 @@ public class CCApplicationEntryPoint implements ICCApplicationEntryPoint {
         return queryWebServer;
     }
 
-    protected void addServlet(ServletContextHandler context, Servlet servlet, String path) {
-        context.addServlet(new ServletHolder(servlet), path);
+    protected void addServlet(ServletContextHandler context, Servlet servlet, String... paths) {
+        final ServletHolder holder = new ServletHolder(servlet);
+        context.getServletHandler().addServlet(holder);
+        ServletMapping mapping = new ServletMapping();
+        mapping.setServletName(holder.getName());
+        mapping.setPathSpecs(paths);
+        context.getServletHandler().addServletMapping(mapping);
     }
 
     protected void addServlet(ServletContextHandler context, Servlets key) {
@@ -284,6 +292,8 @@ public class CCApplicationEntryPoint implements ICCApplicationEntryPoint {
                 return new VersionAPIServlet();
             case CLUSTER_STATE:
                 return new ClusterAPIServlet();
+            case CLUSTER_STATE_NODE_DETAIL:
+                return new ClusterNodeDetailsAPIServlet();
             default:
                 throw new IllegalStateException(String.valueOf(key));
         }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_3/cluster_state_3.1.cstate.aql
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_3/cluster_state_3.1.cstate.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_3/cluster_state_3.1.cstate.aql
new file mode 100644
index 0000000..22b4a19
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_3/cluster_state_3.1.cstate.aql
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+/*
+ * Test case Name  : cluster_state_3
+ * Description     : cluster state api all nodes
+ * Expected Result : Positive
+ * Date            : 8th September 2016
+ */
+/node

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_4/cluster_state_4.1.cstate.aql
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_4/cluster_state_4.1.cstate.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_4/cluster_state_4.1.cstate.aql
new file mode 100644
index 0000000..0faba10
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/cluster_state_4/cluster_state_4.1.cstate.aql
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+/*
+ * Test case Name  : cluster_state_4
+ * Description     : cluster state api valid node
+ * Expected Result : Positive
+ * Date            : 8th September 2016
+ */
+/node/asterix_nc1

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_1/cluster_state_1.1.adm
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_1/cluster_state_1.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_1/cluster_state_1.1.adm
index 0bc8f17..1dfa743 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_1/cluster_state_1.1.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_1/cluster_state_1.1.adm
@@ -1,6 +1,4 @@
 {
-    "Metadata_Node": "asterix_nc1",
-    "State": "ACTIVE",
     "config": {
         "api.port": 19002,
         "cc.java.opts": "-Xmx1024m",
@@ -51,8 +49,26 @@
         "web.queryinterface.port": 19006,
         "web.secondary.port": 19005
     },
-    "partition_0": "asterix_nc1",
-    "partition_1": "asterix_nc1",
-    "partition_2": "asterix_nc2",
-    "partition_3": "asterix_nc2"
+    "metadata_node": "asterix_nc1",
+    "ncs": [
+        {
+            "configUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc1/config",
+            "node_id": "asterix_nc1",
+            "partitions": [
+                "partition_0",
+                "partition_1"
+            ],
+            "statsUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc1/stats"
+        },
+        {
+            "configUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc2/config",
+            "node_id": "asterix_nc2",
+            "partitions": [
+                "partition_2",
+                "partition_3"
+            ],
+            "statsUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc2/stats"
+        }
+    ],
+    "state": "ACTIVE"
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_3/cluster_state_3.1.adm
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_3/cluster_state_3.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_3/cluster_state_3.1.adm
new file mode 100644
index 0000000..2bbfa10
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_3/cluster_state_3.1.adm
@@ -0,0 +1,20 @@
+{"ncs": [
+    {
+        "configUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc1/config",
+        "node_id": "asterix_nc1",
+        "partitions": [
+            "partition_0",
+            "partition_1"
+        ],
+        "statsUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc1/stats"
+    },
+    {
+        "configUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc2/config",
+        "node_id": "asterix_nc2",
+        "partitions": [
+            "partition_2",
+            "partition_3"
+        ],
+        "statsUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc2/stats"
+    }
+]}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_4/cluster_state_4.1.adm
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_4/cluster_state_4.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_4/cluster_state_4.1.adm
new file mode 100644
index 0000000..819c1e7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/cluster_state_4/cluster_state_4.1.adm
@@ -0,0 +1,9 @@
+{
+    "configUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc1/config",
+    "node_id": "asterix_nc1",
+    "partitions": [
+        "partition_0",
+        "partition_1"
+    ],
+    "statsUri": "http://127.0.0.1:19002/admin/cluster/node/asterix_nc1/stats"
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-app/src/test/resources/runtimets/testsuite.xml
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite.xml
index 34a0805..b00d995 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite.xml
@@ -7315,5 +7315,15 @@
         <expected-error>HTTP/1.1 404 Not Found</expected-error>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="api">
+      <compilation-unit name="cluster_state_3">
+        <output-dir compare="Text">cluster_state_3</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="api">
+      <compilation-unit name="cluster_state_4">
+        <output-dir compare="Text">cluster_state_4</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
 </test-suite>

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/ServletUtil.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/ServletUtil.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/ServletUtil.java
index 9e9bbc5..047f010 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/ServletUtil.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/ServletUtil.java
@@ -35,7 +35,8 @@ public class ServletUtil {
         CONNECTOR("/connector"),
         SHUTDOWN("/admin/shutdown"),
         VERSION("/admin/version"),
-        CLUSTER_STATE("/admin/cluster");
+        CLUSTER_STATE("/admin/cluster"),
+        CLUSTER_STATE_NODE_DETAIL("/admin/cluster/node/*");
 
         private final String path;
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/util/AsterixClusterProperties.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/util/AsterixClusterProperties.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/util/AsterixClusterProperties.java
index 2457ddc..d201d60 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/util/AsterixClusterProperties.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/util/AsterixClusterProperties.java
@@ -668,10 +668,17 @@ public class AsterixClusterProperties {
 
     public synchronized JSONObject getClusterStateDescription() throws JSONException {
         JSONObject stateDescription = new JSONObject();
-        stateDescription.put("State", state.name());
-        stateDescription.put("Metadata_Node", currentMetadataNode);
-        for (ClusterPartition partition : clusterPartitions.values()) {
-            stateDescription.put("partition_" + partition.getPartitionId(), partition.getActiveNodeId());
+        stateDescription.put("state", state.name());
+        stateDescription.put("metadata_node", currentMetadataNode);
+        for (Map.Entry<String, ClusterPartition[]> entry : node2PartitionsMap.entrySet()) {
+            JSONObject nodeJSON = new JSONObject();
+            nodeJSON.put("node_id", entry.getKey());
+            List<String> partitions = new ArrayList<>();
+            for (ClusterPartition part : entry.getValue()) {
+                partitions.add("partition_" + part.getPartitionId());
+            }
+            nodeJSON.put("partitions", partitions);
+            stateDescription.accumulate("ncs", nodeJSON);
         }
         return stateDescription;
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java
index bf36183..ca0783b 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceFunctions.java
@@ -45,7 +45,8 @@ public class HyracksClientInterfaceFunctions {
         GET_NODE_CONTROLLERS_INFO,
         CLI_DEPLOY_BINARY,
         CLI_UNDEPLOY_BINARY,
-        CLUSTER_SHUTDOWN
+        CLUSTER_SHUTDOWN,
+        GET_NODE_DETAILS_JSON
     }
 
     public abstract static class Function implements Serializable {
@@ -294,4 +295,34 @@ public class HyracksClientInterfaceFunctions {
         }
     }
 
+    public static class GetNodeDetailsJSONFunction extends Function {
+        private static final long serialVersionUID = 1L;
+        private final String nodeId;
+        private final boolean includeStats;
+        private final boolean includeConfig;
+
+        public GetNodeDetailsJSONFunction(String nodeId, boolean includeStats, boolean includeConfig) {
+            this.nodeId = nodeId;
+            this.includeStats = includeStats;
+            this.includeConfig = includeConfig;
+        }
+
+        public String getNodeId() {
+            return nodeId;
+        }
+
+        public boolean isIncludeStats() {
+            return includeStats;
+        }
+
+        public boolean isIncludeConfig() {
+            return includeConfig;
+        }
+
+        @Override
+        public FunctionId getFunctionId() {
+            return FunctionId.GET_NODE_DETAILS_JSON;
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java
index 25b5a0f..3f453e5 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksClientInterfaceRemoteProxy.java
@@ -131,4 +131,11 @@ public class HyracksClientInterfaceRemoteProxy implements IHyracksClientInterfac
             throw new IPCException("CC refused to release connection after 9 seconds");
         }
     }
+
+    @Override
+    public String getNodeDetailsJSON(String nodeId, boolean includeStats, boolean includeConfig) throws Exception {
+        HyracksClientInterfaceFunctions.GetNodeDetailsJSONFunction gjsf =
+                new HyracksClientInterfaceFunctions.GetNodeDetailsJSONFunction(nodeId, includeStats, includeConfig);
+        return (String) rpci.call(ipcHandle, gjsf);
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java
index 3f1ced6..73813f3 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/HyracksConnection.java
@@ -187,8 +187,14 @@ public final class HyracksConnection implements IHyracksClientConnection {
     public JobInfo getJobInfo(JobId jobId) throws Exception {
         return hci.getJobInfo(jobId);
     }
+
     @Override
     public void stopCluster() throws Exception{
         hci.stopCluster();
     }
+
+    @Override
+    public String getNodeDetailsJSON(String nodeId, boolean includeStats, boolean includeConfig) throws Exception {
+        return hci.getNodeDetailsJSON(nodeId, includeStats, includeConfig);
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java
index 824c914..0690c9f 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientConnection.java
@@ -188,4 +188,13 @@ public interface IHyracksClientConnection {
      */
     public void stopCluster() throws Exception;
 
+    /**
+     * Get details of specified node as JSON object
+     * @param nodeId
+     *              id the subject node
+     * @param includeStats
+     * @param includeConfig @return serialized JSON containing the node details
+     * @throws Exception
+     */
+    public String getNodeDetailsJSON(String nodeId, boolean includeStats, boolean includeConfig) throws Exception;
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java
index b70ef41..4ddb81f 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/client/IHyracksClientInterface.java
@@ -56,4 +56,5 @@ public interface IHyracksClientInterface {
 
     public void stopCluster() throws Exception;
 
+    public String getNodeDetailsJSON(String nodeId, boolean includeStats, boolean includeConfig) throws Exception;
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java
index ae097a6..8dada48 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java
@@ -41,7 +41,6 @@ import org.apache.hyracks.api.client.HyracksClientInterfaceFunctions;
 import org.apache.hyracks.api.client.NodeControllerInfo;
 import org.apache.hyracks.api.comm.NetworkAddress;
 import org.apache.hyracks.api.context.ICCContext;
-import org.apache.hyracks.api.dataset.DatasetDirectoryRecord;
 import org.apache.hyracks.api.dataset.DatasetJobRecord.Status;
 import org.apache.hyracks.api.deployment.DeploymentId;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -67,6 +66,7 @@ import org.apache.hyracks.control.cc.work.GetIpAddressNodeNameMapWork;
 import org.apache.hyracks.control.cc.work.GetJobInfoWork;
 import org.apache.hyracks.control.cc.work.GetJobStatusWork;
 import org.apache.hyracks.control.cc.work.GetNodeControllersInfoWork;
+import org.apache.hyracks.control.cc.work.GetNodeDetailsJSONWork;
 import org.apache.hyracks.control.cc.work.GetResultPartitionLocationsWork;
 import org.apache.hyracks.control.cc.work.GetResultStatusWork;
 import org.apache.hyracks.control.cc.work.JobStartWork;
@@ -418,6 +418,8 @@ public class ClusterControllerService implements IControllerService {
                     return;
                 }
 
+                case CREATE_JOB:
+                    break;
                 case GET_JOB_STATUS: {
                     HyracksClientInterfaceFunctions.GetJobStatusFunction gjsf =
                             (HyracksClientInterfaceFunctions.GetJobStatusFunction) fn;
@@ -457,12 +459,14 @@ public class ClusterControllerService implements IControllerService {
                     return;
                 }
 
+                case GET_DATASET_RECORD_DESCRIPTOR:
+                    break;
                 case GET_DATASET_RESULT_LOCATIONS: {
                     HyracksClientInterfaceFunctions.GetDatasetResultLocationsFunction gdrlf =
                             (HyracksClientInterfaceFunctions.GetDatasetResultLocationsFunction) fn;
                     workQueue.schedule(new GetResultPartitionLocationsWork(ClusterControllerService.this,
                             gdrlf.getJobId(), gdrlf.getResultSetId(), gdrlf.getKnownRecords(),
-                            new IPCResponder<DatasetDirectoryRecord[]>(handle, mid)));
+                            new IPCResponder<>(handle, mid)));
                     return;
                 }
 
@@ -476,7 +480,7 @@ public class ClusterControllerService implements IControllerService {
 
                 case GET_NODE_CONTROLLERS_INFO: {
                     workQueue.schedule(new GetNodeControllersInfoWork(ClusterControllerService.this,
-                            new IPCResponder<Map<String, NodeControllerInfo>>(handle, mid)));
+                            new IPCResponder<>(handle, mid)));
                     return;
                 }
 
@@ -493,7 +497,7 @@ public class ClusterControllerService implements IControllerService {
                     HyracksClientInterfaceFunctions.CliDeployBinaryFunction dbf =
                             (HyracksClientInterfaceFunctions.CliDeployBinaryFunction) fn;
                     workQueue.schedule(new CliDeployBinaryWork(ClusterControllerService.this, dbf.getBinaryURLs(),
-                            dbf.getDeploymentId(), new IPCResponder<DeploymentId>(handle, mid)));
+                            dbf.getDeploymentId(), new IPCResponder<>(handle, mid)));
                     return;
                 }
 
@@ -501,14 +505,21 @@ public class ClusterControllerService implements IControllerService {
                     HyracksClientInterfaceFunctions.CliUnDeployBinaryFunction udbf =
                             (HyracksClientInterfaceFunctions.CliUnDeployBinaryFunction) fn;
                     workQueue.schedule(new CliUnDeployBinaryWork(ClusterControllerService.this, udbf.getDeploymentId(),
-                            new IPCResponder<DeploymentId>(handle, mid)));
+                            new IPCResponder<>(handle, mid)));
                     return;
                 }
                 case CLUSTER_SHUTDOWN: {
                     workQueue.schedule(new ClusterShutdownWork(ClusterControllerService.this,
-                            new IPCResponder<Boolean>(handle, mid)));
+                            new IPCResponder<>(handle, mid)));
                     return;
                 }
+
+                case GET_NODE_DETAILS_JSON:
+                    HyracksClientInterfaceFunctions.GetNodeDetailsJSONFunction gndjf =
+                            (HyracksClientInterfaceFunctions.GetNodeDetailsJSONFunction) fn;
+                    workQueue.schedule(new GetNodeDetailsJSONWork(ClusterControllerService.this, gndjf.getNodeId(),
+                            gndjf.isIncludeStats(), gndjf.isIncludeConfig(), new IPCResponder<>(handle, mid)));
+                    return;
             }
             try {
                 handle.send(mid, null, new IllegalArgumentException("Unknown function " + fn.getFunctionId()));

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/NodeControllerState.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/NodeControllerState.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/NodeControllerState.java
index 7fd027b..a848c6e 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/NodeControllerState.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/NodeControllerState.java
@@ -20,7 +20,6 @@ package org.apache.hyracks.control.cc;
 
 import java.io.File;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -77,6 +76,8 @@ public class NodeControllerState {
 
     private final Map<String, String> systemProperties;
 
+    private final int pid;
+
     private final HeartbeatSchema hbSchema;
 
     private final long[] hbTime;
@@ -147,7 +148,7 @@ public class NodeControllerState {
         dataPort = reg.getDataPort();
         datasetPort = reg.getDatasetPort();
         messagingPort = reg.getMessagingPort();
-        activeJobIds = new HashSet<JobId>();
+        activeJobIds = new HashSet<>();
 
         osName = reg.getOSName();
         arch = reg.getArch();
@@ -161,6 +162,7 @@ public class NodeControllerState {
         bootClasspath = reg.getBootClasspath();
         inputArguments = reg.getInputArguments();
         systemProperties = reg.getSystemProperties();
+        pid = reg.getPid();
 
         hbSchema = reg.getHeartbeatSchema();
 
@@ -203,7 +205,7 @@ public class NodeControllerState {
         rrdPtr = 0;
     }
 
-    public void notifyHeartbeat(HeartbeatData hbData) {
+    public synchronized void notifyHeartbeat(HeartbeatData hbData) {
         lastHeartbeatDuration = 0;
         hbTime[rrdPtr] = System.currentTimeMillis();
         if (hbData != null) {
@@ -282,51 +284,57 @@ public class NodeControllerState {
         return o;
     }
 
-    public JSONObject toDetailedJSON() throws JSONException {
+    public synchronized JSONObject toDetailedJSON(boolean includeStats, boolean includeConfig) throws JSONException {
         JSONObject o = new JSONObject();
 
         o.put("node-id", ncConfig.nodeId);
-        o.put("os-name", osName);
-        o.put("arch", arch);
-        o.put("os-version", osVersion);
-        o.put("num-processors", nProcessors);
-        o.put("vm-name", vmName);
-        o.put("vm-version", vmVersion);
-        o.put("vm-vendor", vmVendor);
-        o.put("classpath", new JSONArray(Arrays.asList(classpath.split(File.pathSeparator))));
-        o.put("library-path", new JSONArray(Arrays.asList(libraryPath.split(File.pathSeparator))));
-        o.put("boot-classpath", new JSONArray(Arrays.asList(bootClasspath.split(File.pathSeparator))));
-        o.put("input-arguments", new JSONArray(inputArguments));
-        o.put("rrd-ptr", rrdPtr);
-        o.put("heartbeat-times", hbTime);
-        o.put("heap-init-sizes", heapInitSize);
-        o.put("heap-used-sizes", heapUsedSize);
-        o.put("heap-committed-sizes", heapCommittedSize);
-        o.put("heap-max-sizes", heapMaxSize);
-        o.put("nonheap-init-sizes", nonheapInitSize);
-        o.put("nonheap-used-sizes", nonheapUsedSize);
-        o.put("nonheap-committed-sizes", nonheapCommittedSize);
-        o.put("nonheap-max-sizes", nonheapMaxSize);
-        o.put("thread-counts", threadCount);
-        o.put("peak-thread-counts", peakThreadCount);
-        o.put("system-load-averages", systemLoadAverage);
-        o.put("gc-names", gcNames);
-        o.put("gc-collection-counts", gcCollectionCounts);
-        o.put("gc-collection-times", gcCollectionTimes);
-        o.put("net-payload-bytes-read", netPayloadBytesRead);
-        o.put("net-payload-bytes-written", netPayloadBytesWritten);
-        o.put("net-signaling-bytes-read", netSignalingBytesRead);
-        o.put("net-signaling-bytes-written", netSignalingBytesWritten);
-        o.put("dataset-net-payload-bytes-read", datasetNetPayloadBytesRead);
-        o.put("dataset-net-payload-bytes-written", datasetNetPayloadBytesWritten);
-        o.put("dataset-net-signaling-bytes-read", datasetNetSignalingBytesRead);
-        o.put("dataset-net-signaling-bytes-written", datasetNetSignalingBytesWritten);
-        o.put("ipc-messages-sent", ipcMessagesSent);
-        o.put("ipc-message-bytes-sent", ipcMessageBytesSent);
-        o.put("ipc-messages-received", ipcMessagesReceived);
-        o.put("ipc-message-bytes-received", ipcMessageBytesReceived);
-        o.put("disk-reads", diskReads);
-        o.put("disk-writes", diskWrites);
+        if (includeConfig) {
+            o.put("os-name", osName);
+            o.put("arch", arch);
+            o.put("os-version", osVersion);
+            o.put("num-processors", nProcessors);
+            o.put("vm-name", vmName);
+            o.put("vm-version", vmVersion);
+            o.put("vm-vendor", vmVendor);
+            o.put("classpath", new JSONArray(Arrays.asList(classpath.split(File.pathSeparator))));
+            o.put("library-path", new JSONArray(Arrays.asList(libraryPath.split(File.pathSeparator))));
+            o.put("boot-classpath", new JSONArray(Arrays.asList(bootClasspath.split(File.pathSeparator))));
+            o.put("input-arguments", new JSONArray(inputArguments));
+            o.put("system-properties", new JSONObject(systemProperties));
+            o.put("pid", pid);
+        }
+        if (includeStats) {
+            o.put("rrd-ptr", rrdPtr);
+            o.put("heartbeat-times", hbTime);
+            o.put("heap-init-sizes", heapInitSize);
+            o.put("heap-used-sizes", heapUsedSize);
+            o.put("heap-committed-sizes", heapCommittedSize);
+            o.put("heap-max-sizes", heapMaxSize);
+            o.put("nonheap-init-sizes", nonheapInitSize);
+            o.put("nonheap-used-sizes", nonheapUsedSize);
+            o.put("nonheap-committed-sizes", nonheapCommittedSize);
+            o.put("nonheap-max-sizes", nonheapMaxSize);
+            o.put("thread-counts", threadCount);
+            o.put("peak-thread-counts", peakThreadCount);
+            o.put("system-load-averages", systemLoadAverage);
+            o.put("gc-names", gcNames);
+            o.put("gc-collection-counts", gcCollectionCounts);
+            o.put("gc-collection-times", gcCollectionTimes);
+            o.put("net-payload-bytes-read", netPayloadBytesRead);
+            o.put("net-payload-bytes-written", netPayloadBytesWritten);
+            o.put("net-signaling-bytes-read", netSignalingBytesRead);
+            o.put("net-signaling-bytes-written", netSignalingBytesWritten);
+            o.put("dataset-net-payload-bytes-read", datasetNetPayloadBytesRead);
+            o.put("dataset-net-payload-bytes-written", datasetNetPayloadBytesWritten);
+            o.put("dataset-net-signaling-bytes-read", datasetNetSignalingBytesRead);
+            o.put("dataset-net-signaling-bytes-written", datasetNetSignalingBytesWritten);
+            o.put("ipc-messages-sent", ipcMessagesSent);
+            o.put("ipc-message-bytes-sent", ipcMessageBytesSent);
+            o.put("ipc-messages-received", ipcMessagesReceived);
+            o.put("ipc-message-bytes-received", ipcMessageBytesReceived);
+            o.put("disk-reads", diskReads);
+            o.put("disk-writes", diskWrites);
+        }
 
         return o;
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/NodesRESTAPIFunction.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/NodesRESTAPIFunction.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/NodesRESTAPIFunction.java
index 8423ea6..58deb55 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/NodesRESTAPIFunction.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/web/NodesRESTAPIFunction.java
@@ -43,7 +43,7 @@ public class NodesRESTAPIFunction implements IJSONOutputFunction {
                     result.put("result", gnse.getSummaries());
                 } else {
                     String nodeId = arguments[0];
-                    GetNodeDetailsJSONWork gnde = new GetNodeDetailsJSONWork(ccs, nodeId);
+                    GetNodeDetailsJSONWork gnde = new GetNodeDetailsJSONWork(ccs, nodeId, true, true);
                     ccs.getWorkQueue().scheduleAndSync(gnde);
                     result.put("result", gnde.getDetail());
                 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/GetNodeDetailsJSONWork.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/GetNodeDetailsJSONWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/GetNodeDetailsJSONWork.java
index ca3f1e5..6d20874 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/GetNodeDetailsJSONWork.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/GetNodeDetailsJSONWork.java
@@ -18,6 +18,7 @@
  */
 package org.apache.hyracks.control.cc.work;
 
+import org.apache.hyracks.control.common.work.IPCResponder;
 import org.json.JSONObject;
 
 import org.apache.hyracks.control.cc.ClusterControllerService;
@@ -27,21 +28,35 @@ import org.apache.hyracks.control.common.work.SynchronizableWork;
 public class GetNodeDetailsJSONWork extends SynchronizableWork {
     private final ClusterControllerService ccs;
     private final String nodeId;
+    private final boolean includeStats;
+    private final boolean includeConfig;
+    private final IPCResponder<String> callback;
     private JSONObject detail;
 
-    public GetNodeDetailsJSONWork(ClusterControllerService ccs, String nodeId) {
+    public GetNodeDetailsJSONWork(ClusterControllerService ccs, String nodeId, boolean includeStats,
+                                  boolean includeConfig, IPCResponder<String> callback) {
         this.ccs = ccs;
         this.nodeId = nodeId;
+        this.includeStats = includeStats;
+        this.includeConfig = includeConfig;
+        this.callback = callback;
+    }
+
+    public GetNodeDetailsJSONWork(ClusterControllerService ccs, String nodeId, boolean includeStats,
+                                  boolean includeConfig) {
+        this(ccs, nodeId, includeStats, includeConfig, null);
     }
 
     @Override
     protected void doRun() throws Exception {
         NodeControllerState ncs = ccs.getNodeMap().get(nodeId);
-        if (ncs == null) {
-            detail = new JSONObject();
-            return;
+        if (ncs != null) {
+            detail = ncs.toDetailedJSON(includeStats, includeConfig);
+        }
+
+        if (callback != null) {
+            callback.setValue(detail == null ? null : detail.toString());
         }
-        detail = ncs.toDetailedJSON();
     }
 
     public JSONObject getDetail() {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NodeRegistration.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NodeRegistration.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NodeRegistration.java
index bb8022e..e95a004 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NodeRegistration.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NodeRegistration.java
@@ -67,11 +67,13 @@ public final class NodeRegistration implements Serializable {
 
     private final NetworkAddress messagingPort;
 
+    private final int pid;
+
     public NodeRegistration(InetSocketAddress ncAddress, String nodeId, NCConfig ncConfig, NetworkAddress dataPort,
-            NetworkAddress datasetPort, String osName, String arch, String osVersion, int nProcessors, String vmName,
-            String vmVersion, String vmVendor, String classpath, String libraryPath, String bootClasspath,
-            List<String> inputArguments, Map<String, String> systemProperties, HeartbeatSchema hbSchema,
-            NetworkAddress messagingPort) {
+                            NetworkAddress datasetPort, String osName, String arch, String osVersion, int nProcessors,
+                            String vmName, String vmVersion, String vmVendor, String classpath, String libraryPath,
+                            String bootClasspath, List<String> inputArguments, Map<String, String> systemProperties,
+                            HeartbeatSchema hbSchema, NetworkAddress messagingPort, int pid) {
         this.ncAddress = ncAddress;
         this.nodeId = nodeId;
         this.ncConfig = ncConfig;
@@ -91,6 +93,7 @@ public final class NodeRegistration implements Serializable {
         this.systemProperties = systemProperties;
         this.hbSchema = hbSchema;
         this.messagingPort = messagingPort;
+        this.pid = pid;
     }
 
     public InetSocketAddress getNodeControllerAddress() {
@@ -168,4 +171,6 @@ public final class NodeRegistration implements Serializable {
     public NetworkAddress getMessagingPort() {
         return messagingPort;
     }
+
+    public int getPid() { return pid; }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/utils/PidHelper.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/utils/PidHelper.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/utils/PidHelper.java
new file mode 100644
index 0000000..79642c0
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/utils/PidHelper.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.hyracks.control.common.utils;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class PidHelper {
+
+    private static final Logger LOGGER = Logger.getLogger(PidHelper.class.getName());
+
+    private PidHelper() {
+    }
+
+    public static int getPid() {
+        return getPid(ManagementFactory.getRuntimeMXBean());
+    }
+
+    public static int getPid(RuntimeMXBean runtimeMXBean) {
+        try {
+            Field jvmField = runtimeMXBean.getClass().getDeclaredField("jvm");
+            jvmField.setAccessible(true);
+            Object vmManagement = jvmField.get(runtimeMXBean);
+            Method getProcessIdMethod = vmManagement.getClass().getDeclaredMethod("getProcessId");
+            getProcessIdMethod.setAccessible(true);
+            return (Integer) getProcessIdMethod.invoke(vmManagement);
+        } catch (Exception e) {
+            LOGGER.log(Level.INFO, "Unable to determine PID due to exception", e);
+            return -1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/f902d66f/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java
index 8373ebe..edadf57 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/NodeControllerService.java
@@ -65,6 +65,7 @@ import org.apache.hyracks.control.common.ipc.CCNCFunctions;
 import org.apache.hyracks.control.common.ipc.CCNCFunctions.StateDumpRequestFunction;
 import org.apache.hyracks.control.common.ipc.ClusterControllerRemoteProxy;
 import org.apache.hyracks.control.common.job.profiling.om.JobProfile;
+import org.apache.hyracks.control.common.utils.PidHelper;
 import org.apache.hyracks.control.common.work.FutureValue;
 import org.apache.hyracks.control.common.work.WorkQueue;
 import org.apache.hyracks.control.nc.application.NCApplicationContext;
@@ -290,7 +291,8 @@ public class NodeControllerService implements IControllerService {
                 osMXBean.getName(), osMXBean.getArch(), osMXBean.getVersion(), osMXBean.getAvailableProcessors(),
                 runtimeMXBean.getVmName(), runtimeMXBean.getVmVersion(), runtimeMXBean.getVmVendor(),
                 runtimeMXBean.getClassPath(), runtimeMXBean.getLibraryPath(), runtimeMXBean.getBootClassPath(),
-                runtimeMXBean.getInputArguments(), runtimeMXBean.getSystemProperties(), hbSchema, meesagingPort));
+                runtimeMXBean.getInputArguments(), runtimeMXBean.getSystemProperties(), hbSchema, meesagingPort,
+                PidHelper.getPid()));
 
         synchronized (this) {
             while (registrationPending) {