Browse Source

Add _cat/tasks

Adds new _cat endpoint that lists all tasks
Igor Motov 9 years ago
parent
commit
81c59cae18

+ 2 - 0
core/src/main/java/org/elasticsearch/common/network/NetworkModule.java

@@ -111,6 +111,7 @@ import org.elasticsearch.rest.action.cat.RestRepositoriesAction;
 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;
@@ -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

+ 8 - 4
core/src/main/java/org/elasticsearch/rest/action/admin/cluster/node/tasks/RestListTasksAction.java

@@ -44,11 +44,10 @@ public class RestListTasksAction extends BaseRestHandler {
         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);
@@ -62,6 +61,11 @@ public class RestListTasksAction extends BaseRestHandler {
         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));
     }
 }

+ 157 - 0
core/src/main/java/org/elasticsearch/rest/action/cat/RestTasksAction.java

@@ -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 - 0
docs/reference/cluster/tasks.asciidoc

@@ -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

+ 53 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/cat.tasks.json

@@ -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
+  }
+}

+ 1 - 1
rest-api-spec/src/main/resources/rest-api-spec/api/tasks.list.json

@@ -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",

+ 10 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/cat.tasks/10_basic.yaml

@@ -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)+$/