Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add _cat/nodeattrs API #12534

Merged
merged 1 commit into from
Jul 29, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ protected void configure() {
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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we want this optimization, but we could always run these three requests in parallel, putting the responses in three AtomicReferences and using three CountDownLatchs to wait until all three complete.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented the early cat APIs that way and @kimchy wanted to nest instead. If the cluster's busy then you only have one dangling request.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works for me!

@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 changes: 2 additions & 0 deletions docs/reference/cat.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down
71 changes: 71 additions & 0 deletions docs/reference/cat/nodeattrs.asciidoc
Original file line number Diff line number Diff line change
@@ -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
|=======================================================================
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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*)+/