Browse Source

Add _cat/nodeattrs API

This provides a _cat/nodeattrs API call, which presents
custom node attributes in a denormalized table.

Closes #8000
Dave Parfitt 10 years ago
parent
commit
f209809716

+ 1 - 0
core/src/main/java/org/elasticsearch/rest/action/RestActionModule.java

@@ -250,6 +250,7 @@ public class RestActionModule extends AbstractModule {
         catActionMultibinder.addBinding().to(RestThreadPoolAction.class).asEagerSingleton();
         catActionMultibinder.addBinding().to(RestPluginsAction.class).asEagerSingleton();
         catActionMultibinder.addBinding().to(RestFielddataAction.class).asEagerSingleton();
+        catActionMultibinder.addBinding().to(RestNodeAttrsAction.class).asEagerSingleton();
         // no abstract cat action
         bind(RestCatAction.class).asEagerSingleton();
     }

+ 131 - 0
core/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java

@@ -0,0 +1,131 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.elasticsearch.rest.action.cat;
+import com.google.common.collect.ImmutableMap;
+import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
+import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest;
+import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequest;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
+import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
+import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.cluster.node.DiscoveryNode;
+import org.elasticsearch.cluster.node.DiscoveryNodes;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.Table;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.rest.*;
+import org.elasticsearch.rest.action.support.RestActionListener;
+import org.elasticsearch.rest.action.support.RestResponseListener;
+import org.elasticsearch.rest.action.support.RestTable;
+
+import static org.elasticsearch.rest.RestRequest.Method.GET;
+
+public class RestNodeAttrsAction extends AbstractCatAction {
+
+    @Inject
+    public RestNodeAttrsAction(Settings settings, RestController controller, Client client) {
+        super(settings, controller, client);
+        controller.registerHandler(GET, "/_cat/nodeattrs", this);
+    }
+
+    @Override
+    void documentation(StringBuilder sb) {
+        sb.append("/_cat/nodeattrs\n");
+    }
+
+    @Override
+    public void doRequest(final RestRequest request, final RestChannel channel, final Client client) {
+        final ClusterStateRequest clusterStateRequest = new ClusterStateRequest();
+        clusterStateRequest.clear().nodes(true);
+        clusterStateRequest.local(request.paramAsBoolean("local", clusterStateRequest.local()));
+        clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout()));
+
+        client.admin().cluster().state(clusterStateRequest, new RestActionListener<ClusterStateResponse>(channel) {
+            @Override
+            public void processResponse(final ClusterStateResponse clusterStateResponse) {
+                NodesInfoRequest nodesInfoRequest = new NodesInfoRequest();
+                nodesInfoRequest.clear().jvm(false).os(false).process(true);
+                client.admin().cluster().nodesInfo(nodesInfoRequest, new RestActionListener<NodesInfoResponse>(channel) {
+                    @Override
+                    public void processResponse(final NodesInfoResponse nodesInfoResponse) {
+                        NodesStatsRequest nodesStatsRequest = new NodesStatsRequest();
+                        nodesStatsRequest.clear().jvm(false).os(false).fs(false).indices(false).process(false);
+                        client.admin().cluster().nodesStats(nodesStatsRequest, new RestResponseListener<NodesStatsResponse>(channel) {
+                            @Override
+                            public RestResponse buildResponse(NodesStatsResponse nodesStatsResponse) throws Exception {
+                                return RestTable.buildResponse(buildTable(request, clusterStateResponse, nodesInfoResponse, nodesStatsResponse), channel);
+                            }
+                        });
+                    }
+                });
+            }
+        });
+    }
+
+    @Override
+    Table getTableWithHeader(final RestRequest request) {
+        Table table = new Table();
+        table.startHeaders();
+        table.addCell("node", "default:true;alias:name;desc:node name");
+        table.addCell("id",   "default:false;alias:id,nodeId;desc:unique node id");
+        table.addCell("pid",  "default:false;alias:p;desc:process id");
+        table.addCell("host", "alias:h;desc:host name");
+        table.addCell("ip",   "alias:i;desc:ip address");
+        table.addCell("port", "default:false;alias:po;desc:bound transport port");
+        table.addCell("attr", "default:true;alias:attr.name;desc:attribute description");
+        table.addCell("value","default:true;alias:attr.value;desc:attribute value");
+        table.endHeaders();
+        return table;
+    }
+
+    private Table buildTable(RestRequest req, ClusterStateResponse state, NodesInfoResponse nodesInfo, NodesStatsResponse nodesStats) {
+        boolean fullId = req.paramAsBoolean("full_id", false);
+
+        DiscoveryNodes nodes = state.getState().nodes();
+        Table table = getTableWithHeader(req);
+
+        for (DiscoveryNode node : nodes) {
+            NodeInfo info = nodesInfo.getNodesMap().get(node.id());
+            ImmutableMap<String, String> attrs = node.getAttributes();
+            for(String att : attrs.keySet()) {
+                table.startRow();
+                table.addCell(node.name());
+                table.addCell(fullId ? node.id() : Strings.substring(node.getId(), 0, 4));
+                table.addCell(info == null ? null : info.getProcess().getId());
+                table.addCell(node.getHostName());
+                table.addCell(node.getHostAddress());
+                if (node.address() instanceof InetSocketTransportAddress) {
+                    table.addCell(((InetSocketTransportAddress) node.address()).address().getPort());
+                } else {
+                    table.addCell("-");
+                }
+                table.addCell(att);
+                table.addCell(attrs.containsKey(att) ? attrs.get(att) : null);
+                table.endRow();
+            }
+        }
+
+        return table;
+    }
+}

+ 2 - 0
docs/reference/cat.asciidoc

@@ -112,6 +112,8 @@ include::cat/indices.asciidoc[]
 
 include::cat/master.asciidoc[]
 
+include::cat/nodeattrs.asciidoc[]
+
 include::cat/nodes.asciidoc[]
 
 include::cat/pending_tasks.asciidoc[]

+ 71 - 0
docs/reference/cat/nodeattrs.asciidoc

@@ -0,0 +1,71 @@
+[[cat-nodeattrs]]
+== cat nodeattrs
+
+The `nodeattrs` command shows custom node attributes.
+
+["source","sh",subs="attributes,callouts"]
+--------------------------------------------------
+% curl 192.168.56.10:9200/_cat/nodeattrs
+node       host    ip          attr  value
+Black Bolt epsilon 192.168.1.8 rack  rack314
+Black Bolt epsilon 192.168.1.8 azone us-east-1
+--------------------------------------------------
+
+The first few columns give you basic info per node.
+
+
+["source","sh",subs="attributes,callouts"]
+--------------------------------------------------
+node       host    ip
+Black Bolt epsilon 192.168.1.8
+Black Bolt epsilon 192.168.1.8
+--------------------------------------------------
+
+
+The attr and value columns can give you a picture of custom node attributes.
+
+[source,sh]
+--------------------------------------------------
+attr  value
+rack  rack314
+azone us-east-1
+--------------------------------------------------
+
+[float]
+=== Columns
+
+Below is an exhaustive list of the existing headers that can be
+passed to `nodes?h=` to retrieve the relevant details in ordered
+columns.  If no headers are specified, then those marked to Appear
+by Default will appear. If any header is specified, then the defaults
+are not used.
+
+Aliases can be used in place of the full header name for brevity.
+Columns appear in the order that they are listed below unless a
+different order is specified (e.g., `h=attr,value` versus `h=value,attr`).
+
+When specifying headers, the headers are not placed in the output
+by default.  To have the headers appear in the output, use verbose
+mode (`v`). The header name will match the supplied value (e.g.,
+`pid` versus `p`).  For example:
+
+["source","sh",subs="attributes,callouts"]
+--------------------------------------------------
+% curl 192.168.56.10:9200/_cat/nodeattrs?v&h=name,pid,attr,value
+name       pid   attr  value
+Black Bolt 28000 rack  rack314
+Black Bolt 28000 azone us-east-1
+--------------------------------------------------
+
+[cols="<,<,<,<,<",options="header",subs="normal"]
+|=======================================================================
+|Header |Alias |Appear by Default |Description |Example
+|`node`|`name`|Yes|Name of the node|Black Bolt
+|`id` |`nodeId` |No |Unique node ID |k0zy
+|`pid` |`p` |No |Process ID |13061
+|`host` |`h` |Yes |Host name |n1
+|`ip` |`i` |Yes |IP address |127.0.1.1
+|`port` |`po` |No |Bound transport port |9300
+|`attr` | `attr.name` | Yes | Attribute name | rack
+|`value` | `attr.value` | Yes | Attribute value | rack123
+|=======================================================================

+ 37 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json

@@ -0,0 +1,37 @@
+{
+  "cat.nodeattrs": {
+    "documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/cat-nodeattrs.html",
+    "methods": ["GET"],
+    "url": {
+      "path": "/_cat/nodeattrs",
+      "paths": ["/_cat/nodeattrs"],
+      "parts": {
+      },
+      "params": {
+        "local": {
+          "type" : "boolean",
+          "description" : "Return local information, do not retrieve the state from master node (default: false)"
+        },
+        "master_timeout": {
+          "type" : "time",
+          "description" : "Explicit operation timeout for connection to master node"
+        },
+        "h": {
+            "type": "list",
+            "description" : "Comma-separated list of column names to display"
+        },
+        "help": {
+          "type": "boolean",
+          "description": "Return help information",
+          "default": false
+        },
+        "v": {
+          "type": "boolean",
+          "description": "Verbose mode. Display column headers",
+          "default": true
+        }
+      }
+    },
+    "body": null
+  }
+}

+ 27 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yaml

@@ -0,0 +1,27 @@
+---
+"Test cat nodes attrs output":
+
+  - do:
+      cat.nodeattrs:
+        v: false
+
+  - match:
+      $body: |
+               /((\S+)\s+(\S+)\s+(\d{1,3}\.){3}\d{1,3}\s+(\S+)\s+(\S+)\s*)+/
+
+  - do:
+      cat.nodeattrs:
+        v: true
+
+  - match:
+      $body: |
+               /((\S+)\s+(\S+)\s+(\d{1,3}\.){3}\d{1,3}\s+(\S+)\s+(\S+)\s*)+/
+
+  - do:
+      cat.nodeattrs:
+          h: attr,value
+          v: true
+
+  - match:
+      $body: |
+               /((\S+)\s+(\S+)\s*)+/