Skip to content

Commit

Permalink
Add _cat/tasks
Browse files Browse the repository at this point in the history
Adds new _cat endpoint that lists all tasks
  • Loading branch information
imotov committed Apr 7, 2016
1 parent 6a0e2d1 commit 81c59ca
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
import org.elasticsearch.rest.action.cat.RestSegmentsAction;
import org.elasticsearch.rest.action.cat.RestShardsAction;
import org.elasticsearch.rest.action.cat.RestSnapshotAction;
import org.elasticsearch.rest.action.cat.RestTasksAction;
import org.elasticsearch.rest.action.cat.RestThreadPoolAction;
import org.elasticsearch.rest.action.delete.RestDeleteAction;
import org.elasticsearch.rest.action.explain.RestExplainAction;
Expand Down Expand Up @@ -284,6 +285,7 @@ public class NetworkModule extends AbstractModule {
RestShardsAction.class,
RestMasterAction.class,
RestNodesAction.class,
RestTasksAction.class,
RestIndicesAction.class,
RestSegmentsAction.class,
// Fully qualified to prevent interference with rest.action.count.RestCountAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@ public RestListTasksAction(Settings settings, RestController controller, Client
controller.registerHandler(GET, "/_tasks/{taskId}", this);
}

@Override
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
public static ListTasksRequest generateListTasksRequest(RestRequest request) {
boolean detailed = request.paramAsBoolean("detailed", false);
String[] nodesIds = Strings.splitStringByCommaToArray(request.param("node_id"));
TaskId taskId = new TaskId(request.param("taskId"));
TaskId taskId = new TaskId(request.param("taskId", request.param("task_id")));
String[] actions = Strings.splitStringByCommaToArray(request.param("actions"));
TaskId parentTaskId = new TaskId(request.param("parent_task_id"));
boolean waitForCompletion = request.paramAsBoolean("wait_for_completion", false);
Expand All @@ -62,6 +61,11 @@ public void handleRequest(final RestRequest request, final RestChannel channel,
listTasksRequest.setParentTaskId(parentTaskId);
listTasksRequest.setWaitForCompletion(waitForCompletion);
listTasksRequest.setTimeout(timeout);
client.admin().cluster().listTasks(listTasksRequest, new RestToXContentListener<>(channel));
return listTasksRequest;
}

@Override
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
client.admin().cluster().listTasks(generateListTasksRequest(request), new RestToXContentListener<>(channel));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* 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 org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest;
import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse;
import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskGroup;
import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskInfo;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.node.DiscoveryNode;
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.common.unit.TimeValue;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.action.support.RestResponseListener;
import org.elasticsearch.rest.action.support.RestTable;
import org.elasticsearch.tasks.TaskId;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.util.ArrayList;
import java.util.List;

import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.action.admin.cluster.node.tasks.RestListTasksAction.generateListTasksRequest;

public class RestTasksAction extends AbstractCatAction {

@Inject
public RestTasksAction(Settings settings, RestController controller, Client client) {
super(settings, controller, client);
controller.registerHandler(GET, "/_cat/tasks", this);
}

@Override
protected void documentation(StringBuilder sb) {
sb.append("/_cat/tasks\n");
}

@Override
public void doRequest(final RestRequest request, final RestChannel channel, final Client client) {
client.admin().cluster().listTasks(generateListTasksRequest(request), new RestResponseListener<ListTasksResponse>(channel) {
@Override
public RestResponse buildResponse(ListTasksResponse listTasksResponse) throws Exception {
return RestTable.buildResponse(buildTable(request, listTasksResponse), channel);
}
});
}

@Override
protected Table getTableWithHeader(final RestRequest request) {
boolean detailed = request.paramAsBoolean("detailed", false);
Table table = new Table();
table.startHeaders();

// Task main info
table.addCell("id", "default:false;desc:id of the task with the node");
table.addCell("action", "alias:ac;desc:task action");
table.addCell("task_id", "alias:ti;desc:unique task id");
table.addCell("parent_task_id", "alias:pti;desc:parent task id");
table.addCell("type", "alias:ty;desc:task type");
table.addCell("start_time", "alias:start;desc:start time in ms");
table.addCell("timestamp", "alias:ts,hms,hhmmss;desc:start time in HH:MM:SS");
table.addCell("running_time_ns", "default:false;alias:time;desc:running time ns");
table.addCell("running_time", "default:true;alias:time;desc:running time");

// Node info
table.addCell("node_id", "default:false;alias:ni;desc:unique node id");
table.addCell("ip", "default:true;alias:i;desc:ip address");
table.addCell("port", "default:false;alias:po;desc:bound transport port");
table.addCell("node", "default:true;alias:n;desc:node name");
table.addCell("version", "default:false;alias:v;desc:es version");

// Task detailed info
if (detailed) {
table.addCell("description", "default:false;alias:desc;desc:task action");
}
table.endHeaders();
return table;
}

private DateTimeFormatter dateFormat = DateTimeFormat.forPattern("HH:mm:ss");

private void buildRow(Table table, boolean fullId, boolean detailed, TaskInfo taskInfo) {
table.startRow();
DiscoveryNode node = taskInfo.getNode();

table.addCell(taskInfo.getId());
table.addCell(taskInfo.getAction());
table.addCell(taskInfo.getTaskId().toString());
if (taskInfo.getParentTaskId().isSet()) {
table.addCell(taskInfo.getParentTaskId().toString());
} else {
table.addCell("-");
}
table.addCell(taskInfo.getType());
table.addCell(taskInfo.getStartTime());
table.addCell(dateFormat.print(taskInfo.getStartTime()));
table.addCell(taskInfo.getRunningTimeNanos());
table.addCell(TimeValue.timeValueNanos(taskInfo.getRunningTimeNanos()).toString());

table.addCell(fullId ? node.getId() : Strings.substring(node.getId(), 0, 4));
table.addCell(node.getHostAddress());
if (node.getAddress() instanceof InetSocketTransportAddress) {
table.addCell(((InetSocketTransportAddress) node.getAddress()).address().getPort());
} else {
table.addCell("-");
}
table.addCell(node.getName());
table.addCell(node.getVersion().toString());

if (detailed) {
table.addCell(taskInfo.getDescription());
}
table.endRow();
}

private void buildGroups(Table table, boolean detailed, boolean fullId, List<TaskGroup> taskGroups) {
List<TaskGroup> sortedGroups = new ArrayList<>(taskGroups);
sortedGroups.sort((o1, o2) -> Long.compare(o1.getTaskInfo().getStartTime(), o2.getTaskInfo().getStartTime()));
for (TaskGroup taskGroup : sortedGroups) {
buildRow(table, fullId, detailed, taskGroup.getTaskInfo());
buildGroups(table, fullId, detailed, taskGroup.getChildTasks());
}
}

private Table buildTable(RestRequest request, ListTasksResponse listTasksResponse) {
boolean fullId = request.paramAsBoolean("full_id", false);
boolean detailed = request.paramAsBoolean("detailed", false);
Table table = getTableWithHeader(request);
buildGroups(table, fullId, detailed, listTasksResponse.getTaskGroups());
return table;
}
}
8 changes: 8 additions & 0 deletions docs/reference/cluster/tasks.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ GET /_tasks/oTUltX4IQMOUUVeiohTt8A:12345?wait_for_completion=true&timeout=10s
--------------------------------------------------
// AUTOSENSE

Tasks can be also listed using _cat version of the list tasks command, which accepts the same arguments
as the standard list tasks command.

[source,js]
--------------------------------------------------
GET /_cat/tasks
--------------------------------------------------
// AUTOSENSE

[float]
=== Task Cancellation
Expand Down
53 changes: 53 additions & 0 deletions rest-api-spec/src/main/resources/rest-api-spec/api/cat.tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"cat.tasks": {
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/tasks.html",
"methods": ["GET"],
"url": {
"path": "/_cat/tasks",
"paths": ["/_cat/tasks"],
"parts": {
},
"params": {
"format": {
"type" : "string",
"description" : "a short version of the Accept header, e.g. json, yaml"
},
"node_id": {
"type": "list",
"description": "A comma-separated list of node IDs or names to limit the returned information; use `_local` to return information from the node you're connecting to, leave empty to get information from all nodes"
},
"actions": {
"type": "list",
"description": "A comma-separated list of actions that should be returned. Leave empty to return all."
},
"detailed": {
"type": "boolean",
"description": "Return detailed task information (default: false)"
},
"parent_node": {
"type": "string",
"description": "Return tasks with specified parent node."
},
"parent_task": {
"type" : "number",
"description" : "Return tasks with specified parent task id. Set to -1 to return all."
},
"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": false
}
}
},
"body": null
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"tasks.list": {
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/tasks-list.html",
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/tasks.html",
"methods": ["GET"],
"url": {
"path": "/_tasks",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"Test cat tasks output":

- do:
cat.tasks: {}

- match:
$body: |
/ # action task_id parent_task_id type start_time timestamp running_time ip node
^( \S+\s+ \S+\:\d+\s+ (?:\-|\S+\:\d+)\s+ \S+\s+ \d+\s+ \d\d\:\d\d\:\d\d\s+ \S+\s+ \d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}\s+ \S+(?:\s\S+)*\n)+$/

0 comments on commit 81c59ca

Please sign in to comment.