Skip to content

Commit 2d2f21e

Browse files
committed
Merge pull request #12534 from metadave/feature/cat_nodeattrs
Add _cat/nodeattrs API
2 parents 0c53204 + f209809 commit 2d2f21e

File tree

6 files changed

+269
-0
lines changed

6 files changed

+269
-0
lines changed

core/src/main/java/org/elasticsearch/rest/action/RestActionModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ protected void configure() {
250250
catActionMultibinder.addBinding().to(RestThreadPoolAction.class).asEagerSingleton();
251251
catActionMultibinder.addBinding().to(RestPluginsAction.class).asEagerSingleton();
252252
catActionMultibinder.addBinding().to(RestFielddataAction.class).asEagerSingleton();
253+
catActionMultibinder.addBinding().to(RestNodeAttrsAction.class).asEagerSingleton();
253254
// no abstract cat action
254255
bind(RestCatAction.class).asEagerSingleton();
255256
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.rest.action.cat;
21+
import com.google.common.collect.ImmutableMap;
22+
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
23+
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest;
24+
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
25+
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequest;
26+
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
27+
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
28+
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
29+
import org.elasticsearch.client.Client;
30+
import org.elasticsearch.cluster.node.DiscoveryNode;
31+
import org.elasticsearch.cluster.node.DiscoveryNodes;
32+
import org.elasticsearch.common.Strings;
33+
import org.elasticsearch.common.Table;
34+
import org.elasticsearch.common.inject.Inject;
35+
import org.elasticsearch.common.settings.Settings;
36+
import org.elasticsearch.common.transport.InetSocketTransportAddress;
37+
import org.elasticsearch.rest.*;
38+
import org.elasticsearch.rest.action.support.RestActionListener;
39+
import org.elasticsearch.rest.action.support.RestResponseListener;
40+
import org.elasticsearch.rest.action.support.RestTable;
41+
42+
import static org.elasticsearch.rest.RestRequest.Method.GET;
43+
44+
public class RestNodeAttrsAction extends AbstractCatAction {
45+
46+
@Inject
47+
public RestNodeAttrsAction(Settings settings, RestController controller, Client client) {
48+
super(settings, controller, client);
49+
controller.registerHandler(GET, "/_cat/nodeattrs", this);
50+
}
51+
52+
@Override
53+
void documentation(StringBuilder sb) {
54+
sb.append("/_cat/nodeattrs\n");
55+
}
56+
57+
@Override
58+
public void doRequest(final RestRequest request, final RestChannel channel, final Client client) {
59+
final ClusterStateRequest clusterStateRequest = new ClusterStateRequest();
60+
clusterStateRequest.clear().nodes(true);
61+
clusterStateRequest.local(request.paramAsBoolean("local", clusterStateRequest.local()));
62+
clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout()));
63+
64+
client.admin().cluster().state(clusterStateRequest, new RestActionListener<ClusterStateResponse>(channel) {
65+
@Override
66+
public void processResponse(final ClusterStateResponse clusterStateResponse) {
67+
NodesInfoRequest nodesInfoRequest = new NodesInfoRequest();
68+
nodesInfoRequest.clear().jvm(false).os(false).process(true);
69+
client.admin().cluster().nodesInfo(nodesInfoRequest, new RestActionListener<NodesInfoResponse>(channel) {
70+
@Override
71+
public void processResponse(final NodesInfoResponse nodesInfoResponse) {
72+
NodesStatsRequest nodesStatsRequest = new NodesStatsRequest();
73+
nodesStatsRequest.clear().jvm(false).os(false).fs(false).indices(false).process(false);
74+
client.admin().cluster().nodesStats(nodesStatsRequest, new RestResponseListener<NodesStatsResponse>(channel) {
75+
@Override
76+
public RestResponse buildResponse(NodesStatsResponse nodesStatsResponse) throws Exception {
77+
return RestTable.buildResponse(buildTable(request, clusterStateResponse, nodesInfoResponse, nodesStatsResponse), channel);
78+
}
79+
});
80+
}
81+
});
82+
}
83+
});
84+
}
85+
86+
@Override
87+
Table getTableWithHeader(final RestRequest request) {
88+
Table table = new Table();
89+
table.startHeaders();
90+
table.addCell("node", "default:true;alias:name;desc:node name");
91+
table.addCell("id", "default:false;alias:id,nodeId;desc:unique node id");
92+
table.addCell("pid", "default:false;alias:p;desc:process id");
93+
table.addCell("host", "alias:h;desc:host name");
94+
table.addCell("ip", "alias:i;desc:ip address");
95+
table.addCell("port", "default:false;alias:po;desc:bound transport port");
96+
table.addCell("attr", "default:true;alias:attr.name;desc:attribute description");
97+
table.addCell("value","default:true;alias:attr.value;desc:attribute value");
98+
table.endHeaders();
99+
return table;
100+
}
101+
102+
private Table buildTable(RestRequest req, ClusterStateResponse state, NodesInfoResponse nodesInfo, NodesStatsResponse nodesStats) {
103+
boolean fullId = req.paramAsBoolean("full_id", false);
104+
105+
DiscoveryNodes nodes = state.getState().nodes();
106+
Table table = getTableWithHeader(req);
107+
108+
for (DiscoveryNode node : nodes) {
109+
NodeInfo info = nodesInfo.getNodesMap().get(node.id());
110+
ImmutableMap<String, String> attrs = node.getAttributes();
111+
for(String att : attrs.keySet()) {
112+
table.startRow();
113+
table.addCell(node.name());
114+
table.addCell(fullId ? node.id() : Strings.substring(node.getId(), 0, 4));
115+
table.addCell(info == null ? null : info.getProcess().getId());
116+
table.addCell(node.getHostName());
117+
table.addCell(node.getHostAddress());
118+
if (node.address() instanceof InetSocketTransportAddress) {
119+
table.addCell(((InetSocketTransportAddress) node.address()).address().getPort());
120+
} else {
121+
table.addCell("-");
122+
}
123+
table.addCell(att);
124+
table.addCell(attrs.containsKey(att) ? attrs.get(att) : null);
125+
table.endRow();
126+
}
127+
}
128+
129+
return table;
130+
}
131+
}

docs/reference/cat.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ include::cat/indices.asciidoc[]
112112

113113
include::cat/master.asciidoc[]
114114

115+
include::cat/nodeattrs.asciidoc[]
116+
115117
include::cat/nodes.asciidoc[]
116118

117119
include::cat/pending_tasks.asciidoc[]

docs/reference/cat/nodeattrs.asciidoc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
[[cat-nodeattrs]]
2+
== cat nodeattrs
3+
4+
The `nodeattrs` command shows custom node attributes.
5+
6+
["source","sh",subs="attributes,callouts"]
7+
--------------------------------------------------
8+
% curl 192.168.56.10:9200/_cat/nodeattrs
9+
node host ip attr value
10+
Black Bolt epsilon 192.168.1.8 rack rack314
11+
Black Bolt epsilon 192.168.1.8 azone us-east-1
12+
--------------------------------------------------
13+
14+
The first few columns give you basic info per node.
15+
16+
17+
["source","sh",subs="attributes,callouts"]
18+
--------------------------------------------------
19+
node host ip
20+
Black Bolt epsilon 192.168.1.8
21+
Black Bolt epsilon 192.168.1.8
22+
--------------------------------------------------
23+
24+
25+
The attr and value columns can give you a picture of custom node attributes.
26+
27+
[source,sh]
28+
--------------------------------------------------
29+
attr value
30+
rack rack314
31+
azone us-east-1
32+
--------------------------------------------------
33+
34+
[float]
35+
=== Columns
36+
37+
Below is an exhaustive list of the existing headers that can be
38+
passed to `nodes?h=` to retrieve the relevant details in ordered
39+
columns. If no headers are specified, then those marked to Appear
40+
by Default will appear. If any header is specified, then the defaults
41+
are not used.
42+
43+
Aliases can be used in place of the full header name for brevity.
44+
Columns appear in the order that they are listed below unless a
45+
different order is specified (e.g., `h=attr,value` versus `h=value,attr`).
46+
47+
When specifying headers, the headers are not placed in the output
48+
by default. To have the headers appear in the output, use verbose
49+
mode (`v`). The header name will match the supplied value (e.g.,
50+
`pid` versus `p`). For example:
51+
52+
["source","sh",subs="attributes,callouts"]
53+
--------------------------------------------------
54+
% curl 192.168.56.10:9200/_cat/nodeattrs?v&h=name,pid,attr,value
55+
name pid attr value
56+
Black Bolt 28000 rack rack314
57+
Black Bolt 28000 azone us-east-1
58+
--------------------------------------------------
59+
60+
[cols="<,<,<,<,<",options="header",subs="normal"]
61+
|=======================================================================
62+
|Header |Alias |Appear by Default |Description |Example
63+
|`node`|`name`|Yes|Name of the node|Black Bolt
64+
|`id` |`nodeId` |No |Unique node ID |k0zy
65+
|`pid` |`p` |No |Process ID |13061
66+
|`host` |`h` |Yes |Host name |n1
67+
|`ip` |`i` |Yes |IP address |127.0.1.1
68+
|`port` |`po` |No |Bound transport port |9300
69+
|`attr` | `attr.name` | Yes | Attribute name | rack
70+
|`value` | `attr.value` | Yes | Attribute value | rack123
71+
|=======================================================================
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"cat.nodeattrs": {
3+
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/cat-nodeattrs.html",
4+
"methods": ["GET"],
5+
"url": {
6+
"path": "/_cat/nodeattrs",
7+
"paths": ["/_cat/nodeattrs"],
8+
"parts": {
9+
},
10+
"params": {
11+
"local": {
12+
"type" : "boolean",
13+
"description" : "Return local information, do not retrieve the state from master node (default: false)"
14+
},
15+
"master_timeout": {
16+
"type" : "time",
17+
"description" : "Explicit operation timeout for connection to master node"
18+
},
19+
"h": {
20+
"type": "list",
21+
"description" : "Comma-separated list of column names to display"
22+
},
23+
"help": {
24+
"type": "boolean",
25+
"description": "Return help information",
26+
"default": false
27+
},
28+
"v": {
29+
"type": "boolean",
30+
"description": "Verbose mode. Display column headers",
31+
"default": true
32+
}
33+
}
34+
},
35+
"body": null
36+
}
37+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
"Test cat nodes attrs output":
3+
4+
- do:
5+
cat.nodeattrs:
6+
v: false
7+
8+
- match:
9+
$body: |
10+
/((\S+)\s+(\S+)\s+(\d{1,3}\.){3}\d{1,3}\s+(\S+)\s+(\S+)\s*)+/
11+
12+
- do:
13+
cat.nodeattrs:
14+
v: true
15+
16+
- match:
17+
$body: |
18+
/((\S+)\s+(\S+)\s+(\d{1,3}\.){3}\d{1,3}\s+(\S+)\s+(\S+)\s*)+/
19+
20+
- do:
21+
cat.nodeattrs:
22+
h: attr,value
23+
v: true
24+
25+
- match:
26+
$body: |
27+
/((\S+)\s+(\S+)\s*)+/

0 commit comments

Comments
 (0)