Skip to content

Commit 7fa69df

Browse files
committed
[ML] Add _cat/ml/data_frame/analytics API
1 parent 78a1185 commit 7fa69df

File tree

7 files changed

+383
-21
lines changed

7 files changed

+383
-21
lines changed

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@
257257
import org.elasticsearch.xpack.ml.rest.calendar.RestPostCalendarEventAction;
258258
import org.elasticsearch.xpack.ml.rest.calendar.RestPutCalendarAction;
259259
import org.elasticsearch.xpack.ml.rest.calendar.RestPutCalendarJobAction;
260+
import org.elasticsearch.xpack.ml.rest.cat.RestCatDataFrameAnalyticsAction;
260261
import org.elasticsearch.xpack.ml.rest.cat.RestCatDatafeedsAction;
261262
import org.elasticsearch.xpack.ml.rest.cat.RestCatJobsAction;
262263
import org.elasticsearch.xpack.ml.rest.cat.RestCatTrainedModelsAction;
@@ -776,7 +777,8 @@ public List<RestHandler> getRestHandlers(Settings settings, RestController restC
776777
// CAT Handlers
777778
new RestCatJobsAction(),
778779
new RestCatTrainedModelsAction(),
779-
new RestCatDatafeedsAction()
780+
new RestCatDatafeedsAction(),
781+
new RestCatDataFrameAnalyticsAction()
780782
);
781783
}
782784

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.ml.rest.cat;
7+
8+
import org.elasticsearch.client.node.NodeClient;
9+
import org.elasticsearch.cluster.metadata.MetaData;
10+
import org.elasticsearch.cluster.node.DiscoveryNode;
11+
import org.elasticsearch.common.Strings;
12+
import org.elasticsearch.common.Table;
13+
import org.elasticsearch.rest.RestRequest;
14+
import org.elasticsearch.rest.RestResponse;
15+
import org.elasticsearch.rest.action.RestActionListener;
16+
import org.elasticsearch.rest.action.RestResponseListener;
17+
import org.elasticsearch.rest.action.cat.AbstractCatAction;
18+
import org.elasticsearch.rest.action.cat.RestTable;
19+
import org.elasticsearch.xpack.core.ml.action.GetDataFrameAnalyticsAction;
20+
import org.elasticsearch.xpack.core.ml.action.GetDataFrameAnalyticsStatsAction;
21+
import org.elasticsearch.xpack.core.ml.action.GetDataFrameAnalyticsStatsAction.Response.Stats;
22+
import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsConfig;
23+
import org.elasticsearch.xpack.core.ml.utils.PhaseProgress;
24+
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.function.Function;
28+
29+
import static java.util.Arrays.asList;
30+
import static java.util.Collections.unmodifiableList;
31+
import static java.util.stream.Collectors.joining;
32+
import static java.util.stream.Collectors.toMap;
33+
import static org.elasticsearch.rest.RestRequest.Method.GET;
34+
35+
public class RestCatDataFrameAnalyticsAction extends AbstractCatAction {
36+
37+
@Override
38+
public List<Route> routes() {
39+
return unmodifiableList(asList(
40+
new Route(GET, "_cat/ml/data_frame/analytics/{" + DataFrameAnalyticsConfig.ID.getPreferredName() + "}"),
41+
new Route(GET, "_cat/ml/data_frame/analytics")));
42+
}
43+
44+
@Override
45+
public String getName() {
46+
return "cat_ml_get_data_frame_analytics_action";
47+
}
48+
49+
@Override
50+
protected RestChannelConsumer doCatRequest(RestRequest restRequest, NodeClient client) {
51+
String dataFrameAnalyticsId = restRequest.param(DataFrameAnalyticsConfig.ID.getPreferredName());
52+
if (Strings.isNullOrEmpty(dataFrameAnalyticsId)) {
53+
dataFrameAnalyticsId = MetaData.ALL;
54+
}
55+
56+
GetDataFrameAnalyticsAction.Request getRequest = new GetDataFrameAnalyticsAction.Request(dataFrameAnalyticsId);
57+
getRequest.setAllowNoResources(
58+
restRequest.paramAsBoolean(
59+
GetDataFrameAnalyticsAction.Request.ALLOW_NO_MATCH.getPreferredName(), getRequest.isAllowNoResources()));
60+
61+
GetDataFrameAnalyticsStatsAction.Request getStatsRequest = new GetDataFrameAnalyticsStatsAction.Request(dataFrameAnalyticsId);
62+
getStatsRequest.setAllowNoMatch(
63+
restRequest.paramAsBoolean(
64+
GetDataFrameAnalyticsStatsAction.Request.ALLOW_NO_MATCH.getPreferredName(), getStatsRequest.isAllowNoMatch()));
65+
66+
return channel -> client.execute(GetDataFrameAnalyticsAction.INSTANCE, getRequest, new RestActionListener<>(channel) {
67+
@Override
68+
public void processResponse(GetDataFrameAnalyticsAction.Response getResponse) {
69+
client.execute(GetDataFrameAnalyticsStatsAction.INSTANCE, getStatsRequest, new RestResponseListener<>(channel) {
70+
@Override
71+
public RestResponse buildResponse(GetDataFrameAnalyticsStatsAction.Response getStatsResponse) throws Exception {
72+
return RestTable.buildResponse(buildTable(getResponse, getStatsResponse), channel);
73+
}
74+
});
75+
}
76+
});
77+
}
78+
79+
@Override
80+
protected void documentation(StringBuilder sb) {
81+
sb.append("/_cat/ml/data_frame/analytics\n");
82+
sb.append("/_cat/ml/data_frame/analytics/{").append(DataFrameAnalyticsConfig.ID.getPreferredName()).append("}\n");
83+
}
84+
85+
@Override
86+
protected Table getTableWithHeader(RestRequest unused) {
87+
return getTableWithHeader();
88+
}
89+
90+
private static Table getTableWithHeader() {
91+
return new Table()
92+
.startHeaders()
93+
// DFA config info
94+
.addCell("id", TableColumnAttributeBuilder.builder("the id").build())
95+
.addCell("create_time",
96+
TableColumnAttributeBuilder.builder("job creation time")
97+
.setAliases("ct", "createTime")
98+
.build())
99+
.addCell("type",
100+
TableColumnAttributeBuilder.builder("analysis type")
101+
.setAliases("t")
102+
.build())
103+
.addCell("source_index",
104+
TableColumnAttributeBuilder.builder("source index", false)
105+
.setAliases("si", "sourceIndex")
106+
.build())
107+
.addCell("dest_index",
108+
TableColumnAttributeBuilder.builder("destination index", false)
109+
.setAliases("di", "destIndex")
110+
.build())
111+
.addCell("description",
112+
TableColumnAttributeBuilder.builder("description", false)
113+
.setAliases("d")
114+
.build())
115+
.addCell("model_memory_limit",
116+
TableColumnAttributeBuilder.builder("model memory limit", false)
117+
.setAliases("mml", "modelMemoryLimit")
118+
.build())
119+
// DFA stats info
120+
.addCell("state",
121+
TableColumnAttributeBuilder.builder("job state")
122+
.setAliases("s")
123+
.setTextAlignment(TableColumnAttributeBuilder.TextAlign.RIGHT)
124+
.build())
125+
.addCell("failure_reason",
126+
TableColumnAttributeBuilder.builder("failure reason", false)
127+
.setAliases("fr", "failureReason")
128+
.build())
129+
.addCell("progress",
130+
TableColumnAttributeBuilder.builder("progress", false)
131+
.setAliases("p")
132+
.build())
133+
.addCell("assignment_explanation",
134+
TableColumnAttributeBuilder.builder("why the job is or is not assigned to a node", false)
135+
.setAliases("ae", "assignmentExplanation")
136+
.build())
137+
// Node info
138+
.addCell("node.id",
139+
TableColumnAttributeBuilder.builder("id of the assigned node", false)
140+
.setAliases("ni", "nodeId")
141+
.build())
142+
.addCell("node.name",
143+
TableColumnAttributeBuilder.builder("name of the assigned node", false)
144+
.setAliases("nn", "nodeName")
145+
.build())
146+
.addCell("node.ephemeral_id",
147+
TableColumnAttributeBuilder.builder("ephemeral id of the assigned node", false)
148+
.setAliases("ne", "nodeEphemeralId")
149+
.build())
150+
.addCell("node.address",
151+
TableColumnAttributeBuilder.builder("network address of the assigned node", false)
152+
.setAliases("na", "nodeAddress")
153+
.build())
154+
.endHeaders();
155+
}
156+
157+
private static Table buildTable(GetDataFrameAnalyticsAction.Response getResponse,
158+
GetDataFrameAnalyticsStatsAction.Response getStatsResponse) {
159+
Map<String, Stats> statsById = getStatsResponse.getResponse().results().stream().collect(toMap(Stats::getId, Function.identity()));
160+
Table table = getTableWithHeader();
161+
for (DataFrameAnalyticsConfig config : getResponse.getResources().results()) {
162+
Stats stats = statsById.get(config.getId());
163+
DiscoveryNode node = stats == null ? null : stats.getNode();
164+
table
165+
.startRow()
166+
.addCell(config.getId())
167+
.addCell(config.getCreateTime())
168+
.addCell(config.getAnalysis().getWriteableName())
169+
.addCell(String.join(",", config.getSource().getIndex()))
170+
.addCell(config.getDest().getIndex())
171+
.addCell(config.getDescription())
172+
.addCell(config.getModelMemoryLimit())
173+
.addCell(stats == null ? null : stats.getState())
174+
.addCell(stats == null ? null : stats.getFailureReason())
175+
.addCell(stats == null ? null : progressToString(stats.getProgress()))
176+
.addCell(stats == null ? null : stats.getAssignmentExplanation())
177+
.addCell(node == null ? null : node.getId())
178+
.addCell(node == null ? null : node.getName())
179+
.addCell(node == null ? null : node.getEphemeralId())
180+
.addCell(node == null ? null : node.getAddress().toString())
181+
.endRow();
182+
}
183+
return table;
184+
}
185+
186+
private static String progressToString(List<PhaseProgress> phases) {
187+
return phases.stream().map(p -> p.getPhase() + ":" + p.getProgressPercent()).collect(joining(","));
188+
}
189+
}

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/rest/cat/RestCatDatafeedsAction.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ protected Table getTableWithHeader(RestRequest request) {
6868
table.startHeaders();
6969

7070
// Datafeed Info
71-
table.addCell("id", TableColumnAttributeBuilder.builder().setDescription("the datafeed_id").build());
72-
table.addCell("state", TableColumnAttributeBuilder.builder()
73-
.setDescription("the datafeed state")
74-
.setAliases("s")
75-
.setTextAlignment(TableColumnAttributeBuilder.TextAlign.RIGHT)
76-
.build());
71+
table.addCell("id", TableColumnAttributeBuilder.builder("the datafeed_id").build());
72+
table.addCell("state",
73+
TableColumnAttributeBuilder.builder("the datafeed state")
74+
.setAliases("s")
75+
.setTextAlignment(TableColumnAttributeBuilder.TextAlign.RIGHT)
76+
.build());
7777
table.addCell("assignment_explanation",
7878
TableColumnAttributeBuilder.builder("why the datafeed is or is not assigned to a node", false)
7979
.setAliases("ae")

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/rest/cat/RestCatJobsAction.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,15 @@ protected Table getTableWithHeader(RestRequest request) {
7373
table.startHeaders();
7474

7575
// Job Info
76-
table.addCell("id", TableColumnAttributeBuilder.builder().setDescription("the job_id").build());
77-
table.addCell("state", TableColumnAttributeBuilder.builder()
78-
.setDescription("the job state")
79-
.setAliases("s")
80-
.setTextAlignment(TableColumnAttributeBuilder.TextAlign.RIGHT)
81-
.build());
76+
table.addCell("id", TableColumnAttributeBuilder.builder("the job_id").build());
77+
table.addCell("state",
78+
TableColumnAttributeBuilder.builder("the job state")
79+
.setAliases("s")
80+
.setTextAlignment(TableColumnAttributeBuilder.TextAlign.RIGHT)
81+
.build());
8282
table.addCell("opened_time",
83-
TableColumnAttributeBuilder.builder()
84-
.setDescription("the amount of time the job has been opened")
83+
TableColumnAttributeBuilder.builder("the amount of time the job has been opened", false)
8584
.setAliases("ot")
86-
.setDisplayByDefault(false)
8785
.build());
8886
table.addCell("assignment_explanation",
8987
TableColumnAttributeBuilder.builder("why the job is or is not assigned to a node", false)

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/rest/cat/RestCatTrainedModelsAction.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,15 @@ protected Table getTableWithHeader(RestRequest request) {
130130
table.startHeaders();
131131

132132
// Trained Model Info
133-
table.addCell("id", TableColumnAttributeBuilder.builder().setDescription("the trained model id").build());
133+
table.addCell("id", TableColumnAttributeBuilder.builder("the trained model id").build());
134134
table.addCell("created_by", TableColumnAttributeBuilder.builder("who created the model", false)
135135
.setAliases("c", "createdBy")
136136
.setTextAlignment(TableColumnAttributeBuilder.TextAlign.RIGHT)
137137
.build());
138-
table.addCell("heap_size", TableColumnAttributeBuilder.builder()
139-
.setDescription("the estimated heap size to keep the model in memory")
138+
table.addCell("heap_size", TableColumnAttributeBuilder.builder("the estimated heap size to keep the model in memory")
140139
.setAliases("hs","modelHeapSize")
141140
.build());
142-
table.addCell("operations", TableColumnAttributeBuilder.builder()
143-
.setDescription("the estimated number of operations to use the model")
141+
table.addCell("operations", TableColumnAttributeBuilder.builder("the estimated number of operations to use the model")
144142
.setAliases("o", "modelOperations")
145143
.build());
146144
table.addCell("license", TableColumnAttributeBuilder.builder("The license level of the model", false)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"cat.ml_data_frame_analytics":{
3+
"documentation":{
4+
"url":"http://www.elastic.co/guide/en/elasticsearch/reference/current/get-dfanalytics-stats.html"
5+
},
6+
"stability":"stable",
7+
"url":{
8+
"paths":[
9+
{
10+
"path":"/_cat/ml/data_frame/analytics",
11+
"methods":[
12+
"GET"
13+
]
14+
},
15+
{
16+
"path":"/_cat/ml/data_frame/analytics/{id}",
17+
"methods":[
18+
"GET"
19+
],
20+
"parts":{
21+
"id":{
22+
"type":"string",
23+
"description":"The ID of the data frame analytics to fetch"
24+
}
25+
}
26+
}
27+
]
28+
},
29+
"params":{
30+
"allow_no_match":{
31+
"type":"boolean",
32+
"required":false,
33+
"description":"Whether to ignore if a wildcard expression matches no configs. (This includes `_all` string or when no configs have been specified)"
34+
},
35+
"bytes":{
36+
"type":"enum",
37+
"description":"The unit in which to display byte values",
38+
"options":[
39+
"b",
40+
"k",
41+
"kb",
42+
"m",
43+
"mb",
44+
"g",
45+
"gb",
46+
"t",
47+
"tb",
48+
"p",
49+
"pb"
50+
]
51+
},
52+
"format":{
53+
"type":"string",
54+
"description":"a short version of the Accept header, e.g. json, yaml"
55+
},
56+
"h":{
57+
"type":"list",
58+
"description":"Comma-separated list of column names to display"
59+
},
60+
"help":{
61+
"type":"boolean",
62+
"description":"Return help information",
63+
"default":false
64+
},
65+
"s":{
66+
"type":"list",
67+
"description":"Comma-separated list of column names or column aliases to sort by"
68+
},
69+
"time":{
70+
"type":"enum",
71+
"description":"The unit in which to display time values",
72+
"options":[
73+
"d (Days)",
74+
"h (Hours)",
75+
"m (Minutes)",
76+
"s (Seconds)",
77+
"ms (Milliseconds)",
78+
"micros (Microseconds)",
79+
"nanos (Nanoseconds)"
80+
]
81+
},
82+
"v":{
83+
"type":"boolean",
84+
"description":"Verbose mode. Display column headers",
85+
"default":false
86+
}
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)