Start cat api with shards endpoint.

This commit is contained in:
Andrew Raines 2013-07-02 18:00:05 -05:00
parent 8919e7e602
commit 7aa9d4bc9f
10 changed files with 571 additions and 0 deletions

View File

@ -19,6 +19,7 @@
package org.elasticsearch.action.admin.indices.stats;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@ -43,6 +44,8 @@ public class IndicesStatsResponse extends BroadcastOperationResponse implements
private ShardStats[] shards;
private ImmutableMap<String, CommonStats> shardStatsMap;
IndicesStatsResponse() {
}
@ -50,6 +53,21 @@ public class IndicesStatsResponse extends BroadcastOperationResponse implements
IndicesStatsResponse(ShardStats[] shards, ClusterState clusterState, int totalShards, int successfulShards, int failedShards, List<ShardOperationFailedException> shardFailures) {
super(totalShards, successfulShards, failedShards, shardFailures);
this.shards = shards;
this.shardStatsMap = buildShardsStatsMap();
}
private ImmutableMap<String, CommonStats> buildShardsStatsMap() {
ImmutableMap.Builder<String, CommonStats> mb = ImmutableMap.builder();
for (ShardStats ss : shards) {
mb.put(ss.getShardRouting().globalId(), ss.getStats());
}
return mb.build();
}
public ImmutableMap<String, CommonStats> asMap() {
return this.shardStatsMap;
}
public ShardStats[] getShards() {

View File

@ -84,6 +84,29 @@ public class ImmutableShardRouting implements Streamable, Serializable, ShardRou
this.version = version;
}
@Override
public String globalId() {
String pri = "r";
if (this.primary()) {
pri = "p";
}
String node = "unassigned";
if (null != this.currentNodeId()) {
node = this.currentNodeId();
}
StringBuilder sb = new StringBuilder();
sb.append(getIndex());
sb.append("/");
sb.append(this.shardId().id());
sb.append("/");
sb.append(pri);
sb.append("/");
sb.append(node);
return sb.toString();
}
@Override
public String index() {
return this.index;

View File

@ -131,6 +131,11 @@ public interface ShardRouting extends Streamable, Serializable, ToXContent {
*/
ShardIterator shardsIt();
/**
* String identifier to uniquely refer to this shard routing (once it's assigned).
*/
String globalId();
/**
* Does not write index name and shard id
*/

View File

@ -0,0 +1,32 @@
/*
* Licensed to ElasticSearch and Shay Banon 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.common.table;
public enum Align {
LEFT((byte) 0),
CENTER((byte) 1),
RIGHT((byte) 2);
private byte id;
Align (byte id) {
this.id = id;
}
}

View File

@ -0,0 +1,89 @@
/*
* Licensed to ElasticSearch and Shay Banon 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.common.table;
/**
* A String container that supports alignment.
*/
public class Cell {
private final String content;
private final Align align;
private final byte width;
public Cell(String content) {
this.content = content;
this.align = Align.LEFT;
this.width = (byte) content.length();
}
public Cell(String content, Align align) {
this.content = content;
this.align = align;
this.width = (byte) content.length();
}
public Cell(String content, Align align, byte width) {
this.content = content;
this.align = align;
this.width = width;
}
public byte width() {
return this.width;
}
public Align align() {
return this.align;
}
public static String pad(String orig, byte width, Align align) {
StringBuilder s = new StringBuilder();
byte leftOver = (byte) (width - orig.length());
if (leftOver > 0 && align == Align.LEFT) {
s.append(orig);
for (byte i = 0; i < leftOver; i++) {
s.append(" ");
}
} else if (leftOver > 0 && align == Align.RIGHT) {
for (byte i = 0; i < leftOver; i++) {
s.append(" ");
}
s.append(orig);
} else {
s.append(orig);
}
return s.toString();
}
public String toString(byte outWidth, Align outAlign) {
return pad(content, outWidth, outAlign);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(align());
sb.append("]");
sb.append(content);
return sb.toString();
}
}

View File

@ -0,0 +1,60 @@
/*
* Licensed to ElasticSearch and Shay Banon 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.common.table;
import java.util.ArrayList;
/**
*
*/
public class Row {
private final ArrayList<Cell> cells;
private final ArrayList<Byte> widths;
public Row () {
this.cells = new ArrayList<Cell>();
this.widths = new ArrayList<Byte>();
}
public ArrayList<Cell> cells() {
return this.cells;
}
public int size () {
return cells.size();
}
public Row addCell(Cell cell) {
cells.add(cell);
widths.add(cell.width());
return this;
}
public Row addCell(String content) {
addCell(new Cell(content));
return this;
}
public Row addCell(String content, Align align) {
addCell(new Cell(content, align));
return this;
}
}

View File

@ -0,0 +1,147 @@
/*
* Licensed to ElasticSearch and Shay Banon 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.common.table;
import java.util.ArrayList;
/**
* A generic table renderer. Can optionally print header row.
* Will justify all cells in a column to the widest one. All rows need
* to have same number of cells.
*
* Eg, new Table.addRow(new Row().addCell("foo").addCell("bar")).render()
*/
public class Table {
private ArrayList<Column> cols;
private byte numcols;
private byte height;
public Table() {
this.cols = new ArrayList<Column>();
this.numcols = 0;
this.height = 0;
}
public Table addRow(Row row) {
addRow(row, false);
return this;
}
public void ensureCapacity(int size) {
if (numcols < size) {
for (int i = 0; i < (size - numcols); i++) {
cols.add(new Column());
}
}
}
public Table addRow(Row row, boolean header) {
ensureCapacity(row.size());
byte curCol = 0;
for (Cell cell : row.cells()) {
Column col = cols.get(curCol);
col.addCell(cell, header);
curCol += 1;
}
numcols = curCol;
height += 1;
return this;
}
public String render() {
return render(false);
}
public String render(boolean withHeaders) {
StringBuilder out = new StringBuilder();
for (byte i = 0; i < height; i++) {
StringBuilder row = new StringBuilder();
for (Column col : cols) {
Cell cell = col.getCell(i);
boolean headerRowWhenNotWantingHeaders = i == 0 && !withHeaders && col.hasHeader();
if (! headerRowWhenNotWantingHeaders) {
row.append(cell.toString(col.width(), col.align()));
row.append(" ");
}
}
out.append(row.toString().trim());
out.append("\n");
}
return out.toString().trim();
}
private class Column {
private boolean hasHeader;
private ArrayList<Cell> cells;
private byte width;
private Align align;
Column () {
cells = new ArrayList<Cell>();
width = 0;
hasHeader = false;
align = Align.LEFT;
}
public Column addCell(Cell cell) {
addCell(cell, false);
return this;
}
public Column addCell(Cell cell, boolean header) {
cells.add(cell);
if (header) {
hasHeader = true;
}
if (cell.width() > width) {
width = cell.width();
}
if (align != cell.align()) {
align = cell.align();
}
return this;
}
public Cell getCell(int index) {
return cells.get(index);
}
public Align align() {
return this.align;
}
public byte width() {
return this.width;
}
public boolean hasHeader() {
return this.hasHeader;
}
}
}

View File

@ -67,6 +67,7 @@ import org.elasticsearch.rest.action.admin.indices.warmer.delete.RestDeleteWarme
import org.elasticsearch.rest.action.admin.indices.warmer.get.RestGetWarmerAction;
import org.elasticsearch.rest.action.admin.indices.warmer.put.RestPutWarmerAction;
import org.elasticsearch.rest.action.bulk.RestBulkAction;
import org.elasticsearch.rest.action.cat.RestShardsAction;
import org.elasticsearch.rest.action.count.RestCountAction;
import org.elasticsearch.rest.action.delete.RestDeleteAction;
import org.elasticsearch.rest.action.deletebyquery.RestDeleteByQueryAction;
@ -83,7 +84,9 @@ import org.elasticsearch.rest.action.search.RestMultiSearchAction;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.rest.action.search.RestSearchScrollAction;
import org.elasticsearch.rest.action.suggest.RestSuggestAction;
import org.elasticsearch.rest.action.termvector.RestTermVectorAction;
import org.elasticsearch.rest.action.update.RestUpdateAction;
import java.util.List;
@ -180,5 +183,7 @@ public class RestActionModule extends AbstractModule {
bind(RestMoreLikeThisAction.class).asEagerSingleton();
bind(RestExplainAction.class).asEagerSingleton();
bind(RestShardsAction.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,147 @@
/*
* Licensed to ElasticSearch and Shay Banon 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 org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.routing.*;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.table.Row;
import org.elasticsearch.common.table.Table;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.rest.*;
import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.GET;
public class RestShardsAction extends BaseRestHandler {
@Inject
public RestShardsAction(Settings settings, Client client, RestController controller) {
super(settings, client);
controller.registerHandler(GET, "/_cat/shards", this);
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel) {
final boolean verbose = request.paramAsBoolean("verbose", false);
final StringBuilder out = new StringBuilder();
final ClusterStateRequest clusterStateRequest = new ClusterStateRequest(); clusterStateRequest.listenerThreaded(false);
clusterStateRequest.filterMetaData(true);
clusterStateRequest.local(false);
client.admin().cluster().state(clusterStateRequest, new ActionListener<ClusterStateResponse>() {
@Override
public void onResponse(final ClusterStateResponse clusterStateResponse) {
IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest();
client.admin().indices().stats(indicesStatsRequest, new ActionListener<IndicesStatsResponse>() {
@Override
public void onResponse(IndicesStatsResponse indicesStatsResponse) {
RestStatus status = RestStatus.OK;
try {
channel.sendResponse(new StringRestResponse(status, process(clusterStateResponse, indicesStatsResponse, verbose)));
} catch (Throwable e) {
onFailure(e);
}
}
@Override
public void onFailure(Throwable e) {
try {
channel.sendResponse(new XContentThrowableRestResponse(request, e));
} catch (IOException e1) {
logger.error("Failed to send failure response", e1);
}
}
});
}
@Override
public void onFailure(Throwable e) {
try {
channel.sendResponse(new XContentThrowableRestResponse(request, e));
} catch (IOException e1) {
logger.error("Failed to send failure response", e1);
}
}
});
}
private String process(ClusterStateResponse state, IndicesStatsResponse stats, boolean headers) {
Table tab = new Table();
if (headers) {
tab.addRow(new Row()
.addCell("index")
.addCell("shard")
.addCell("replica")
.addCell("state")
.addCell("docs")
.addCell("size")
.addCell("bytes")
.addCell("host")
.addCell("node"), true);
}
for (ShardRouting shard : state.getState().routingTable().allShards()) {
Row row = new Row();
String pri = "r";
String host = "";
String docs = "";
String size = "";
String bytes = "";
String nodeName = "";
if (shard.assignedToNode()) {
host = ((InetSocketTransportAddress) state.getState().nodes().get(shard.currentNodeId()).address()).address().getHostString();
nodeName = state.getState().nodes().get(shard.currentNodeId()).name();
}
if (null != stats.asMap().get(shard.globalId())) {
size = stats.asMap().get(shard.globalId()).getStore().size().toString();
bytes = new Long(stats.asMap().get(shard.globalId()).getStore().getSizeInBytes()).toString();
docs = new Long(stats.asMap().get(shard.globalId()).getDocs().getCount()).toString();
}
if (shard.primary()) {
pri = "p";
}
row.addCell(shard.index())
.addCell(new Integer(shard.shardId().id()).toString())
.addCell(pri)
.addCell(shard.state().toString())
.addCell(docs)
.addCell(size)
.addCell(bytes)
.addCell(host)
.addCell(nodeName);
tab.addRow(row);
}
return tab.render(headers);
}
}

View File

@ -0,0 +1,45 @@
/*
* Licensed to ElasticSearch and Shay Banon 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.test.unit.common.table;
import org.elasticsearch.common.table.Row;
import org.testng.annotations.Test;
import org.elasticsearch.common.table.Table;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
public class TableTests {
@Test
public void testTable() {
Table tab = new Table();
tab.addRow(new Row().addCell("123").addCell("4567"));
tab.addRow(new Row().addCell("1234").addCell("567890123"));
assertThat(tab.render(), equalTo("123 4567\n1234 567890123"));
}
@Test
public void testHeader() {
Table tab = new Table();
tab.addRow(new Row().addCell("loooong").addCell("short"), true);
tab.addRow(new Row().addCell("012").addCell("3456789"));
assertThat(tab.render(true), equalTo("loooong short\n012 3456789"));
assertThat(tab.render(), equalTo("012 3456789"));
}
}