more cat support

- add attributes to each cell
- change how it gets rendered, allow for other formats
- various other changes
This commit is contained in:
Shay Banon 2013-07-03 21:05:08 +02:00
parent 0c2d12bda3
commit ceb7d55857
12 changed files with 397 additions and 530 deletions

View File

@ -26,6 +26,7 @@ import com.google.common.collect.Sets;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.action.support.broadcast.BroadcastOperationResponse;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
@ -44,7 +45,7 @@ public class IndicesStatsResponse extends BroadcastOperationResponse implements
private ShardStats[] shards;
private ImmutableMap<String, CommonStats> shardStatsMap;
private ImmutableMap<ShardRouting, CommonStats> shardStatsMap;
IndicesStatsResponse() {
@ -53,21 +54,18 @@ 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();
public ImmutableMap<ShardRouting, CommonStats> asMap() {
if (shardStatsMap == null) {
ImmutableMap.Builder<ShardRouting, CommonStats> mb = ImmutableMap.builder();
for (ShardStats ss : shards) {
mb.put(ss.getShardRouting(), ss.getStats());
}
for (ShardStats ss : shards) {
mb.put(ss.getShardRouting().globalId(), ss.getStats());
shardStatsMap = mb.build();
}
return mb.build();
}
public ImmutableMap<String, CommonStats> asMap() {
return this.shardStatsMap;
return shardStatsMap;
}
public ShardStats[] getShards() {

View File

@ -84,29 +84,6 @@ 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;
@ -201,7 +178,6 @@ public class ImmutableShardRouting implements Streamable, Serializable, ShardRou
*
* @param in {@link InputStream} to read the entry from
* @return {@link ImmutableShardRouting} instances read from the given {@link InputStream}
*
* @throws IOException if some exception occurs during the read operations
*/
public static ImmutableShardRouting readShardRoutingEntry(StreamInput in) throws IOException {
@ -214,11 +190,10 @@ public class ImmutableShardRouting implements Streamable, Serializable, ShardRou
* Reads a routingentry from an inputstream with given <code>index</code> and
* <code>shardId</code>.
*
* @param in inputstream to read the entry from
* @param in inputstream to read the entry from
* @param index shards index
* @param id id of the shard
* @param id id of the shard
* @return Shard routing entry read
*
* @throws IOException if some exception occurs during the read operations
*/
public static ImmutableShardRouting readShardRoutingEntry(StreamInput in, String index, int shardId) throws IOException {
@ -231,10 +206,9 @@ public class ImmutableShardRouting implements Streamable, Serializable, ShardRou
* Read information from an inputstream with given <code>index</code> and
* <code>shardId</code>.
*
* @param in inputstream to read the entry from
* @param in inputstream to read the entry from
* @param index shards index
* @param id id of the shard
*
* @param id id of the shard
* @throws IOException if some exception occurs during the read operations
*/
public void readFrom(StreamInput in, String index, int shardId) throws IOException {
@ -265,6 +239,7 @@ public class ImmutableShardRouting implements Streamable, Serializable, ShardRou
/**
* Writes shard information to {@link StreamOutput} without writing index name and shard id
*
* @param out {@link StreamOutput} to write shard information to
* @throws IOException if something happens during write
*/

View File

@ -131,11 +131,6 @@ 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,125 @@
/*
* 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;
import com.google.common.collect.ImmutableMap;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*/
public class Table {
private List<Cell> headers = new ArrayList<Cell>();
private List<List<Cell>> rows = new ArrayList<List<Cell>>();
private List<Cell> currentCells;
private boolean inHeaders = false;
public Table startHeaders() {
inHeaders = true;
currentCells = new ArrayList<Cell>();
return this;
}
public Table endHeaders() {
inHeaders = false;
headers = currentCells;
currentCells = null;
return this;
}
public Table startRow() {
if (headers.isEmpty()) {
throw new ElasticSearchIllegalArgumentException("no headers added...");
}
currentCells = new ArrayList<Cell>(headers.size());
return this;
}
public Table endRow() {
if (currentCells.size() != headers.size()) {
throw new ElasticSearchIllegalArgumentException("mismatch on number of cells in a row compared to header");
}
rows.add(currentCells);
currentCells = null;
return this;
}
public Table addCell(Object value) {
return addCell(value, "");
}
public Table addCell(Object value, String attributes) {
if (!inHeaders) {
if (currentCells.size() == headers.size()) {
throw new ElasticSearchIllegalArgumentException("can't add more cells to a row than the header");
}
}
Map<String, String> mAttr;
if (attributes.length() == 0) {
if (inHeaders) {
mAttr = ImmutableMap.of();
} else {
// get the attributes of the header cell we are going to add to
mAttr = headers.get(currentCells.size()).attr;
}
} else {
mAttr = new HashMap<String, String>();
if (!inHeaders) {
// get the attributes of the header cell we are going to add
mAttr.putAll(headers.get(currentCells.size()).attr);
}
String[] sAttrs = Strings.split(attributes, ";");
for (String sAttr : sAttrs) {
if (sAttr.length() == 0) {
continue;
}
int idx = sAttr.indexOf('=');
mAttr.put(sAttr.substring(0, idx), sAttr.substring(idx + 1));
}
}
currentCells.add(new Cell(value, mAttr));
return this;
}
public List<Cell> getHeaders() {
return this.headers;
}
public Iterable<List<Cell>> getRows() {
return rows;
}
public static class Cell {
public final Object value;
public final Map<String, String> attr;
Cell(Object value, Map<String, String> attr) {
this.value = value;
this.attr = attr;
}
}
}

View File

@ -1,32 +0,0 @@
/*
* 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

@ -1,89 +0,0 @@
/*
* 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

@ -1,60 +0,0 @@
/*
* 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

@ -1,147 +0,0 @@
/*
* 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();
}
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,7 +67,6 @@ 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.RestMasterAction;
import org.elasticsearch.rest.action.cat.RestShardsAction;
import org.elasticsearch.rest.action.count.RestCountAction;
import org.elasticsearch.rest.action.delete.RestDeleteAction;
@ -85,9 +84,7 @@ 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;
@ -186,6 +183,5 @@ public class RestActionModule extends AbstractModule {
bind(RestExplainAction.class).asEagerSingleton();
bind(RestShardsAction.class).asEagerSingleton();
bind(RestMasterAction.class).asEagerSingleton();
}
}

View File

@ -1,85 +0,0 @@
/*
* 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.client.Client;
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 RestMasterAction extends BaseRestHandler {
@Inject
public RestMasterAction(Settings settings, Client client, RestController controller) {
super(settings, client);
controller.registerHandler(GET, "/_cat/master", 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) {
try {
RestStatus status = RestStatus.OK;
Table tab = new Table();
tab.addRow(new Row()
.addCell("id")
.addCell("transport addr")
.addCell("name"), true);
tab.addRow(new Row()
.addCell(clusterStateResponse.getState().nodes().masterNode().id())
.addCell(((InetSocketTransportAddress)clusterStateResponse.getState().nodes()
.masterNode().address()).address().getAddress().getHostAddress())
.addCell(clusterStateResponse.getState().nodes().masterNode().name()));
channel.sendResponse(new StringRestResponse(status, tab.render(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);
}
}
});
}
}

View File

@ -22,22 +22,24 @@ 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.CommonStats;
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.cluster.routing.ShardRouting;
import org.elasticsearch.common.Table;
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 org.elasticsearch.rest.action.support.RestTable;
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);
@ -46,12 +48,10 @@ public class RestShardsAction extends BaseRestHandler {
@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);
final ClusterStateRequest clusterStateRequest = new ClusterStateRequest();
clusterStateRequest.filterMetaData(true);
clusterStateRequest.local(false);
clusterStateRequest.local(request.paramAsBoolean("local", clusterStateRequest.local()));
clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout()));
client.admin().cluster().state(clusterStateRequest, new ActionListener<ClusterStateResponse>() {
@Override
@ -60,9 +60,8 @@ public class RestShardsAction extends BaseRestHandler {
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)));
channel.sendResponse(RestTable.buildResponse(buildTable(clusterStateResponse, indicesStatsResponse), request, channel));
} catch (Throwable e) {
onFailure(e);
}
@ -90,64 +89,41 @@ public class RestShardsAction extends BaseRestHandler {
});
}
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);
}
private Table buildTable(ClusterStateResponse state, IndicesStatsResponse stats) {
Table table = new Table();
table.startHeaders()
.addCell("index", "default=true;")
.addCell("shard", "default=true;")
.addCell("p/r", "default=true;")
.addCell("state", "default=true;")
.addCell("docs", "default=true;")
.addCell("store", "default=true;")
.addCell("ip", "default=true;")
.addCell("node", "default=true;")
.endHeaders();
for (ShardRouting shard : state.getState().routingTable().allShards()) {
Row row = new Row();
String pri = "r";
StringBuilder host = new StringBuilder();
String docs = "";
String size = "";
String bytes = "";
String nodeName = "";
CommonStats shardStats = stats.asMap().get(shard);
table.startRow();
table.addCell(shard.index());
table.addCell(shard.id());
table.addCell(shard.primary() ? "p" : "r");
table.addCell(shard.state());
table.addCell(shardStats == null ? null : shardStats.getDocs().getCount());
table.addCell(shardStats == null ? null : shardStats.getStore().getSize());
if (shard.assignedToNode()) {
host.append(((InetSocketTransportAddress) state.getState().nodes().get(shard.currentNodeId()).address()).address().getAddress().getHostAddress());
nodeName = state.getState().nodes().get(shard.currentNodeId()).name();
table.addCell(((InetSocketTransportAddress) state.getState().nodes().get(shard.currentNodeId()).address()).address().getAddress().getHostAddress());
table.addCell(state.getState().nodes().get(shard.currentNodeId()).name());
} else {
table.addCell(null);
table.addCell(null);
}
if (shard.relocating()) {
host.append(" -> ");
host.append(((InetSocketTransportAddress) state.getState().nodes().get(shard.relocatingNodeId()).address()).address().getAddress().getHostAddress());
host.append(state.getState().nodes().get(shard.relocatingNodeId()).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.toString())
.addCell(nodeName);
tab.addRow(row);
table.endRow();
}
return tab.render(headers);
return table;
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.support;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.Table;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.SizeValue;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*/
public class RestTable {
public static RestResponse buildResponse(Table table, RestRequest request, RestChannel channel) throws Exception {
XContentType xContentType = XContentType.fromRestContentType(request.param("format", request.header("Content-Type")));
if (xContentType != null) {
return buildXContentBuilder(table, request, channel);
}
return buildTextPlainResponse(table, request, channel);
}
public static RestResponse buildXContentBuilder(Table table, RestRequest request, RestChannel channel) throws Exception {
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
Set<String> displayHeaders = buildDisplayHeaders(table, request);
List<Table.Cell> headers = table.getHeaders();
builder.startArray();
for (List<Table.Cell> row : table.getRows()) {
builder.startObject();
for (int i = 0; i < headers.size(); i++) {
String headerName = headers.get(i).value.toString();
if (displayHeaders.contains(headerName)) {
builder.field(headerName, renderValue(request, row.get(i).value));
}
}
builder.endObject();
}
builder.endArray();
return new XContentRestResponse(request, RestStatus.OK, builder);
}
public static RestResponse buildTextPlainResponse(Table table, RestRequest request, RestChannel channel) {
int[] width = buildWidths(table, request);
Set<String> displayHeaders = buildDisplayHeaders(table, request);
boolean verbose = request.paramAsBoolean("v", true);
StringBuilder out = new StringBuilder();
if (verbose) {
// print the headers
for (int i = 0; i < width.length; i++) {
String headerName = table.getHeaders().get(i).value.toString();
if (displayHeaders.contains(headerName)) {
pad(table.getHeaders().get(i), width[i], request, out);
out.append(" ");
}
}
out.append("\n");
}
for (List<Table.Cell> row : table.getRows()) {
for (int i = 0; i < width.length; i++) {
String headerName = table.getHeaders().get(i).value.toString();
if (displayHeaders.contains(headerName)) {
pad(row.get(i), width[i], request, out);
out.append(" ");
}
}
out.append("\n");
}
return new StringRestResponse(RestStatus.OK, out.toString());
}
private static Set<String> buildDisplayHeaders(Table table, RestRequest request) {
String pHeaders = request.param("headers");
Set<String> display;
if (pHeaders != null) {
display = Strings.commaDelimitedListToSet(pHeaders);
} else {
display = new HashSet<String>();
for (Table.Cell cell : table.getHeaders()) {
String d = cell.attr.get("default");
if (Booleans.parseBoolean(d, true)) {
display.add(cell.value.toString());
}
}
}
return display;
}
private static int[] buildWidths(Table table, RestRequest request) {
int[] width = new int[table.getHeaders().size()];
for (int col = 0; col < width.length; col++) {
String v = renderValue(request, table.getHeaders().get(col).value);
int vWidth = v == null ? 0 : v.length();
if (width[col] < vWidth) {
width[col] = vWidth;
}
}
for (List<Table.Cell> row : table.getRows()) {
for (int col = 0; col < width.length; col++) {
String v = renderValue(request, row.get(col).value);
int vWidth = v == null ? 0 : v.length();
if (width[col] < vWidth) {
width[col] = vWidth;
}
}
}
return width;
}
private static void pad(Table.Cell cell, int width, RestRequest request, StringBuilder out) {
String sValue = renderValue(request, cell.value);
int length = sValue == null ? 0 : sValue.length();
byte leftOver = (byte) (width - length);
String textAlign = cell.attr.get("text-align");
if (textAlign == null) {
textAlign = "left";
}
if (leftOver > 0 && textAlign.equals("right")) {
for (byte i = 0; i < leftOver; i++) {
out.append(" ");
}
if (sValue != null) {
out.append(sValue);
}
} else {
if (sValue != null) {
out.append(sValue);
}
for (byte i = 0; i < leftOver; i++) {
out.append(" ");
}
}
}
private static String renderValue(RestRequest request, Object value) {
if (value == null) {
return null;
}
if (value instanceof ByteSizeValue) {
ByteSizeValue v = (ByteSizeValue) value;
String resolution = request.param("bytes");
if ("b".equals(resolution)) {
return Long.toString(v.bytes());
} else if ("k".equals(resolution)) {
return Long.toString(v.kb());
} else if ("m".equals(resolution)) {
return Long.toString(v.mb());
} else if ("g".equals(resolution)) {
return Long.toString(v.gb());
} else {
return v.toString();
}
}
if (value instanceof SizeValue) {
SizeValue v = (SizeValue) value;
String resolution = request.param("size");
if ("b".equals(resolution)) {
return Long.toString(v.singles());
} else if ("k".equals(resolution)) {
return Long.toString(v.kilo());
} else if ("m".equals(resolution)) {
return Long.toString(v.mega());
} else if ("g".equals(resolution)) {
return Long.toString(v.giga());
} else {
return v.toString();
}
}
if (value instanceof TimeValue) {
TimeValue v = (TimeValue) value;
String resolution = request.param("time");
if ("ms".equals(resolution)) {
return Long.toString(v.millis());
} else if ("s".equals(resolution)) {
return Long.toString(v.seconds());
} else if ("m".equals(resolution)) {
return Long.toString(v.minutes());
} else if ("h".equals(resolution)) {
return Long.toString(v.hours());
} else {
return v.toString();
}
}
// Add additional built in data points we can render based on request parameters?
return value.toString();
}
}