|
| 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(true); |
| 63 | + |
| 64 | + return channel -> client.execute( |
| 65 | + GetDataFrameAnalyticsAction.INSTANCE, getRequest, new RestActionListener<GetDataFrameAnalyticsAction.Response>(channel) { |
| 66 | + @Override |
| 67 | + public void processResponse(GetDataFrameAnalyticsAction.Response getResponse) { |
| 68 | + client.execute( |
| 69 | + GetDataFrameAnalyticsStatsAction.INSTANCE, |
| 70 | + getStatsRequest, |
| 71 | + new RestResponseListener<GetDataFrameAnalyticsStatsAction.Response>(channel) { |
| 72 | + @Override |
| 73 | + public RestResponse buildResponse(GetDataFrameAnalyticsStatsAction.Response getStatsResponse) throws Exception { |
| 74 | + return RestTable.buildResponse(buildTable(getResponse, getStatsResponse), channel); |
| 75 | + } |
| 76 | + }); |
| 77 | + } |
| 78 | + }); |
| 79 | + } |
| 80 | + |
| 81 | + @Override |
| 82 | + protected void documentation(StringBuilder sb) { |
| 83 | + sb.append("/_cat/ml/data_frame/analytics\n"); |
| 84 | + sb.append("/_cat/ml/data_frame/analytics/{").append(DataFrameAnalyticsConfig.ID.getPreferredName()).append("}\n"); |
| 85 | + } |
| 86 | + |
| 87 | + @Override |
| 88 | + protected Table getTableWithHeader(RestRequest unused) { |
| 89 | + return getTableWithHeader(); |
| 90 | + } |
| 91 | + |
| 92 | + private static Table getTableWithHeader() { |
| 93 | + return new Table() |
| 94 | + .startHeaders() |
| 95 | + // DFA config info |
| 96 | + .addCell("id", TableColumnAttributeBuilder.builder("the id").build()) |
| 97 | + .addCell("type", |
| 98 | + TableColumnAttributeBuilder.builder("analysis type") |
| 99 | + .setAliases("t") |
| 100 | + .build()) |
| 101 | + .addCell("create_time", |
| 102 | + TableColumnAttributeBuilder.builder("job creation time") |
| 103 | + .setAliases("ct", "createTime") |
| 104 | + .build()) |
| 105 | + .addCell("version", |
| 106 | + TableColumnAttributeBuilder.builder("the version of Elasticsearch when the analytics was created", false) |
| 107 | + .setAliases("v") |
| 108 | + .build()) |
| 109 | + .addCell("source_index", |
| 110 | + TableColumnAttributeBuilder.builder("source index", false) |
| 111 | + .setAliases("si", "sourceIndex") |
| 112 | + .build()) |
| 113 | + .addCell("dest_index", |
| 114 | + TableColumnAttributeBuilder.builder("destination index", false) |
| 115 | + .setAliases("di", "destIndex") |
| 116 | + .build()) |
| 117 | + .addCell("description", |
| 118 | + TableColumnAttributeBuilder.builder("description", false) |
| 119 | + .setAliases("d") |
| 120 | + .build()) |
| 121 | + .addCell("model_memory_limit", |
| 122 | + TableColumnAttributeBuilder.builder("model memory limit", false) |
| 123 | + .setAliases("mml", "modelMemoryLimit") |
| 124 | + .build()) |
| 125 | + // DFA stats info |
| 126 | + .addCell("state", |
| 127 | + TableColumnAttributeBuilder.builder("job state") |
| 128 | + .setAliases("s") |
| 129 | + .setTextAlignment(TableColumnAttributeBuilder.TextAlign.RIGHT) |
| 130 | + .build()) |
| 131 | + .addCell("failure_reason", |
| 132 | + TableColumnAttributeBuilder.builder("failure reason", false) |
| 133 | + .setAliases("fr", "failureReason") |
| 134 | + .build()) |
| 135 | + .addCell("progress", |
| 136 | + TableColumnAttributeBuilder.builder("progress", false) |
| 137 | + .setAliases("p") |
| 138 | + .build()) |
| 139 | + .addCell("assignment_explanation", |
| 140 | + TableColumnAttributeBuilder.builder("why the job is or is not assigned to a node", false) |
| 141 | + .setAliases("ae", "assignmentExplanation") |
| 142 | + .build()) |
| 143 | + // Node info |
| 144 | + .addCell("node.id", |
| 145 | + TableColumnAttributeBuilder.builder("id of the assigned node", false) |
| 146 | + .setAliases("ni", "nodeId") |
| 147 | + .build()) |
| 148 | + .addCell("node.name", |
| 149 | + TableColumnAttributeBuilder.builder("name of the assigned node", false) |
| 150 | + .setAliases("nn", "nodeName") |
| 151 | + .build()) |
| 152 | + .addCell("node.ephemeral_id", |
| 153 | + TableColumnAttributeBuilder.builder("ephemeral id of the assigned node", false) |
| 154 | + .setAliases("ne", "nodeEphemeralId") |
| 155 | + .build()) |
| 156 | + .addCell("node.address", |
| 157 | + TableColumnAttributeBuilder.builder("network address of the assigned node", false) |
| 158 | + .setAliases("na", "nodeAddress") |
| 159 | + .build()) |
| 160 | + .endHeaders(); |
| 161 | + } |
| 162 | + |
| 163 | + private static Table buildTable(GetDataFrameAnalyticsAction.Response getResponse, |
| 164 | + GetDataFrameAnalyticsStatsAction.Response getStatsResponse) { |
| 165 | + Map<String, Stats> statsById = getStatsResponse.getResponse().results().stream().collect(toMap(Stats::getId, Function.identity())); |
| 166 | + Table table = getTableWithHeader(); |
| 167 | + for (DataFrameAnalyticsConfig config : getResponse.getResources().results()) { |
| 168 | + Stats stats = statsById.get(config.getId()); |
| 169 | + DiscoveryNode node = stats == null ? null : stats.getNode(); |
| 170 | + table |
| 171 | + .startRow() |
| 172 | + .addCell(config.getId()) |
| 173 | + .addCell(config.getAnalysis().getWriteableName()) |
| 174 | + .addCell(config.getCreateTime()) |
| 175 | + .addCell(config.getVersion()) |
| 176 | + .addCell(String.join(",", config.getSource().getIndex())) |
| 177 | + .addCell(config.getDest().getIndex()) |
| 178 | + .addCell(config.getDescription()) |
| 179 | + .addCell(config.getModelMemoryLimit()) |
| 180 | + .addCell(stats == null ? null : stats.getState()) |
| 181 | + .addCell(stats == null ? null : stats.getFailureReason()) |
| 182 | + .addCell(stats == null ? null : progressToString(stats.getProgress())) |
| 183 | + .addCell(stats == null ? null : stats.getAssignmentExplanation()) |
| 184 | + .addCell(node == null ? null : node.getId()) |
| 185 | + .addCell(node == null ? null : node.getName()) |
| 186 | + .addCell(node == null ? null : node.getEphemeralId()) |
| 187 | + .addCell(node == null ? null : node.getAddress().toString()) |
| 188 | + .endRow(); |
| 189 | + } |
| 190 | + return table; |
| 191 | + } |
| 192 | + |
| 193 | + private static String progressToString(List<PhaseProgress> phases) { |
| 194 | + return phases.stream().map(p -> p.getPhase() + ":" + p.getProgressPercent()).collect(joining(",")); |
| 195 | + } |
| 196 | +} |
0 commit comments