(cherry picked from commit f1d1cceacaacf226fcd2459f34689843b822fe4b)
This commit is contained in:
parent
c4525f8cca
commit
2f1631d9d0
|
@ -123,6 +123,10 @@ Example response:
|
|||
"available" : true,
|
||||
"enabled" : true
|
||||
},
|
||||
"eql" : {
|
||||
"available" : true,
|
||||
"enabled" : true
|
||||
},
|
||||
"sql" : {
|
||||
"available" : true,
|
||||
"enabled" : true
|
||||
|
@ -153,6 +157,8 @@ Example response:
|
|||
// TESTRESPONSE[s/"expiry_date_in_millis" : 1542665112332/"expiry_date_in_millis" : "$body.license.expiry_date_in_millis"/]
|
||||
// TESTRESPONSE[s/"version" : "7.0.0-alpha1-SNAPSHOT",/"version": "$body.features.ml.native_code_info.version",/]
|
||||
// TESTRESPONSE[s/"build_hash" : "99a07c016d5a73"/"build_hash": "$body.features.ml.native_code_info.build_hash"/]
|
||||
// TESTRESPONSE[s/"eql" : \{[^\}]*\},/"eql": $body.$_path,/]
|
||||
// eql is disabled by default on release builds and enabled everywhere else during the initial implementation phase until its release
|
||||
// So much s/// but at least we test that the layout is close to matching....
|
||||
|
||||
The following example only returns the build and features information:
|
||||
|
|
|
@ -706,6 +706,15 @@ public class XPackLicenseState {
|
|||
return localStatus.active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if EQL support should be enabled.
|
||||
* <p>
|
||||
* EQL is available for all license types except {@link OperationMode#MISSING}
|
||||
*/
|
||||
public synchronized boolean isEqlAllowed() {
|
||||
return status.active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if SQL support should be enabled.
|
||||
* <p>
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.elasticsearch.xpack.core.ccr.AutoFollowMetadata;
|
|||
import org.elasticsearch.xpack.core.ccr.CCRFeatureSet;
|
||||
import org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction;
|
||||
import org.elasticsearch.xpack.core.enrich.EnrichFeatureSet;
|
||||
import org.elasticsearch.xpack.core.eql.EqlFeatureSetUsage;
|
||||
import org.elasticsearch.xpack.core.enrich.action.DeleteEnrichPolicyAction;
|
||||
import org.elasticsearch.xpack.core.enrich.action.ExecuteEnrichPolicyAction;
|
||||
import org.elasticsearch.xpack.core.enrich.action.GetEnrichPolicyAction;
|
||||
|
@ -65,9 +66,9 @@ import org.elasticsearch.xpack.core.ilm.ReadOnlyAction;
|
|||
import org.elasticsearch.xpack.core.ilm.RolloverAction;
|
||||
import org.elasticsearch.xpack.core.ilm.SetPriorityAction;
|
||||
import org.elasticsearch.xpack.core.ilm.ShrinkAction;
|
||||
import org.elasticsearch.xpack.core.ilm.WaitForSnapshotAction;
|
||||
import org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType;
|
||||
import org.elasticsearch.xpack.core.ilm.UnfollowAction;
|
||||
import org.elasticsearch.xpack.core.ilm.WaitForSnapshotAction;
|
||||
import org.elasticsearch.xpack.core.ilm.action.DeleteLifecycleAction;
|
||||
import org.elasticsearch.xpack.core.ilm.action.ExplainLifecycleAction;
|
||||
import org.elasticsearch.xpack.core.ilm.action.GetLifecycleAction;
|
||||
|
@ -545,6 +546,8 @@ public class XPackClientPlugin extends Plugin implements ActionPlugin, NetworkPl
|
|||
new NamedWriteableRegistry.Entry(RoleMapperExpression.class, AnyExpression.NAME, AnyExpression::new),
|
||||
new NamedWriteableRegistry.Entry(RoleMapperExpression.class, FieldExpression.NAME, FieldExpression::new),
|
||||
new NamedWriteableRegistry.Entry(RoleMapperExpression.class, ExceptExpression.NAME, ExceptExpression::new),
|
||||
// eql
|
||||
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.EQL, EqlFeatureSetUsage::new),
|
||||
// sql
|
||||
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.SQL, SqlFeatureSetUsage::new),
|
||||
// watcher
|
||||
|
|
|
@ -27,6 +27,8 @@ public final class XPackField {
|
|||
public static final String UPGRADE = "upgrade";
|
||||
// inside of YAML settings we still use xpack do not having handle issues with dashes
|
||||
public static final String SETTINGS_NAME = "xpack";
|
||||
/** Name constant for the eql feature. */
|
||||
public static final String EQL = "eql";
|
||||
/** Name constant for the sql feature. */
|
||||
public static final String SQL = "sql";
|
||||
/** Name constant for the rollup feature. */
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.core.eql;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xpack.core.XPackFeatureSet;
|
||||
import org.elasticsearch.xpack.core.XPackField;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class EqlFeatureSetUsage extends XPackFeatureSet.Usage {
|
||||
|
||||
private final Map<String, Object> stats;
|
||||
|
||||
public EqlFeatureSetUsage(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
stats = in.readMap();
|
||||
}
|
||||
|
||||
public EqlFeatureSetUsage(boolean available, boolean enabled, Map<String, Object> stats) {
|
||||
super(XPackField.EQL, available, enabled);
|
||||
this.stats = stats;
|
||||
}
|
||||
|
||||
public Map<String, Object> stats() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
super.innerXContent(builder, params);
|
||||
if (enabled) {
|
||||
for (Map.Entry<String, Object> entry : stats.entrySet()) {
|
||||
builder.field(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeMap(stats);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.eql;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.xpack.core.XPackFeatureSet;
|
||||
import org.elasticsearch.xpack.core.XPackField;
|
||||
import org.elasticsearch.xpack.core.eql.EqlFeatureSetUsage;
|
||||
import org.elasticsearch.xpack.core.watcher.common.stats.Counters;
|
||||
import org.elasticsearch.xpack.eql.plugin.EqlPlugin;
|
||||
import org.elasticsearch.xpack.eql.plugin.EqlStatsAction;
|
||||
import org.elasticsearch.xpack.eql.plugin.EqlStatsRequest;
|
||||
import org.elasticsearch.xpack.eql.plugin.EqlStatsResponse;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class EqlFeatureSet implements XPackFeatureSet {
|
||||
|
||||
private final boolean enabled;
|
||||
private final XPackLicenseState licenseState;
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
public EqlFeatureSet(Settings settings, @Nullable XPackLicenseState licenseState, Client client) {
|
||||
this.enabled = EqlPlugin.isEnabled(settings);
|
||||
this.licenseState = licenseState;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return XPackField.EQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean available() {
|
||||
return licenseState.isEqlAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> nativeCodeInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void usage(ActionListener<XPackFeatureSet.Usage> listener) {
|
||||
if (enabled) {
|
||||
EqlStatsRequest request = new EqlStatsRequest();
|
||||
request.includeStats(true);
|
||||
client.execute(EqlStatsAction.INSTANCE, request, ActionListener.wrap(r -> {
|
||||
List<Counters> countersPerNode = r.getNodes()
|
||||
.stream()
|
||||
.map(EqlStatsResponse.NodeStatsResponse::getStats)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
Counters mergedCounters = Counters.merge(countersPerNode);
|
||||
listener.onResponse(new EqlFeatureSetUsage(available(), enabled(), mergedCounters.toNestedMap()));
|
||||
}, listener::onFailure));
|
||||
} else {
|
||||
listener.onResponse(new EqlFeatureSetUsage(available(), enabled(), Collections.emptyMap()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.client.Client;
|
|||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.settings.ClusterSettings;
|
||||
import org.elasticsearch.common.settings.IndexScopedSettings;
|
||||
|
@ -28,11 +29,14 @@ import org.elasticsearch.rest.RestHandler;
|
|||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.core.XPackPlugin;
|
||||
import org.elasticsearch.xpack.eql.EqlFeatureSet;
|
||||
import org.elasticsearch.xpack.eql.action.EqlSearchAction;
|
||||
import org.elasticsearch.xpack.eql.execution.PlanExecutor;
|
||||
import org.elasticsearch.xpack.ql.index.IndexResolver;
|
||||
import org.elasticsearch.xpack.ql.type.DefaultDataTypeRegistry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -61,11 +65,18 @@ public class EqlPlugin extends Plugin implements ActionPlugin {
|
|||
return Arrays.asList(planExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Module> createGuiceModules() {
|
||||
List<Module> modules = new ArrayList<>();
|
||||
modules.add(b -> XPackPlugin.bindFeatureSet(b, EqlFeatureSet.class));
|
||||
return modules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
|
||||
return Arrays.asList(
|
||||
new ActionHandler<>(EqlSearchAction.INSTANCE, TransportEqlSearchAction.class)
|
||||
new ActionHandler<>(EqlSearchAction.INSTANCE, TransportEqlSearchAction.class),
|
||||
new ActionHandler<>(EqlStatsAction.INSTANCE, TransportEqlStatsAction.class)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -88,7 +99,7 @@ public class EqlPlugin extends Plugin implements ActionPlugin {
|
|||
}
|
||||
|
||||
// TODO: this needs to be used by all plugin methods - including getActions and createComponents
|
||||
private boolean isEnabled(Settings settings) {
|
||||
public static boolean isEnabled(Settings settings) {
|
||||
return EQL_ENABLED_SETTING.get(settings);
|
||||
}
|
||||
|
||||
|
@ -104,6 +115,6 @@ public class EqlPlugin extends Plugin implements ActionPlugin {
|
|||
if (isEnabled(settings) == false) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList(new RestEqlSearchAction());
|
||||
return Arrays.asList(new RestEqlSearchAction(), new RestEqlStatsAction());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.eql.plugin;
|
||||
|
||||
import org.elasticsearch.action.ActionType;
|
||||
|
||||
public class EqlStatsAction extends ActionType<EqlStatsResponse> {
|
||||
|
||||
public static final EqlStatsAction INSTANCE = new EqlStatsAction();
|
||||
public static final String NAME = "cluster:monitor/xpack/eql/stats/dist";
|
||||
|
||||
private EqlStatsAction() {
|
||||
super(NAME, EqlStatsResponse::new);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.eql.plugin;
|
||||
|
||||
import org.elasticsearch.action.support.nodes.BaseNodeRequest;
|
||||
import org.elasticsearch.action.support.nodes.BaseNodesRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Request to gather usage statistics
|
||||
*/
|
||||
public class EqlStatsRequest extends BaseNodesRequest<EqlStatsRequest> {
|
||||
|
||||
private boolean includeStats;
|
||||
|
||||
public EqlStatsRequest() {
|
||||
super((String[]) null);
|
||||
}
|
||||
|
||||
public EqlStatsRequest(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
includeStats = in.readBoolean();
|
||||
}
|
||||
|
||||
public boolean includeStats() {
|
||||
return includeStats;
|
||||
}
|
||||
|
||||
public void includeStats(boolean includeStats) {
|
||||
this.includeStats = includeStats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeBoolean(includeStats);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "eql_stats";
|
||||
}
|
||||
|
||||
static class NodeStatsRequest extends BaseNodeRequest {
|
||||
boolean includeStats;
|
||||
|
||||
NodeStatsRequest(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
includeStats = in.readBoolean();
|
||||
}
|
||||
|
||||
NodeStatsRequest(EqlStatsRequest request) {
|
||||
includeStats = request.includeStats();
|
||||
}
|
||||
|
||||
public boolean includeStats() {
|
||||
return includeStats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeBoolean(includeStats);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.eql.plugin;
|
||||
|
||||
import org.elasticsearch.action.FailedNodeException;
|
||||
import org.elasticsearch.action.support.nodes.BaseNodeResponse;
|
||||
import org.elasticsearch.action.support.nodes.BaseNodesResponse;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xpack.core.watcher.common.stats.Counters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class EqlStatsResponse extends BaseNodesResponse<EqlStatsResponse.NodeStatsResponse> implements ToXContentObject {
|
||||
|
||||
public EqlStatsResponse(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public EqlStatsResponse(ClusterName clusterName, List<NodeStatsResponse> nodes, List<FailedNodeException> failures) {
|
||||
super(clusterName, nodes, failures);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<NodeStatsResponse> readNodesFrom(StreamInput in) throws IOException {
|
||||
return in.readList(NodeStatsResponse::readNodeResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeNodesTo(StreamOutput out, List<NodeStatsResponse> nodes) throws IOException {
|
||||
out.writeList(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startArray("stats");
|
||||
for (NodeStatsResponse node : getNodes()) {
|
||||
node.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static class NodeStatsResponse extends BaseNodeResponse implements ToXContentObject {
|
||||
|
||||
private Counters stats;
|
||||
|
||||
public NodeStatsResponse(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
if (in.readBoolean()) {
|
||||
stats = new Counters(in);
|
||||
}
|
||||
}
|
||||
|
||||
public NodeStatsResponse(DiscoveryNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
public Counters getStats() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
public void setStats(Counters stats) {
|
||||
this.stats = stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeBoolean(stats != null);
|
||||
if (stats != null) {
|
||||
stats.writeTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
if (stats != null && stats.hasCounters()) {
|
||||
builder.field("stats", stats.toNestedMap());
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
static EqlStatsResponse.NodeStatsResponse readNodeResponse(StreamInput in) throws IOException {
|
||||
return new EqlStatsResponse.NodeStatsResponse(in);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.eql.plugin;
|
||||
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.action.RestActions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
|
||||
public class RestEqlStatsAction extends BaseRestHandler {
|
||||
|
||||
@Override
|
||||
public List<Route> routes() {
|
||||
return singletonList(new Route(GET, "/_eql/stats"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "eql_stats";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) {
|
||||
EqlStatsRequest request = new EqlStatsRequest();
|
||||
return channel -> client.execute(EqlStatsAction.INSTANCE, request, new RestActions.NodesResponseRestListener<>(channel));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.eql.plugin;
|
||||
|
||||
import org.elasticsearch.action.FailedNodeException;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.nodes.TransportNodesAction;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Performs the stats operation.
|
||||
*/
|
||||
public class TransportEqlStatsAction extends TransportNodesAction<EqlStatsRequest, EqlStatsResponse,
|
||||
EqlStatsRequest.NodeStatsRequest, EqlStatsResponse.NodeStatsResponse> {
|
||||
|
||||
// the plan executor holds the metrics
|
||||
//private final PlanExecutor planExecutor;
|
||||
|
||||
@Inject
|
||||
public TransportEqlStatsAction(TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters/* , PlanExecutor planExecutor */) {
|
||||
super(EqlStatsAction.NAME, threadPool, clusterService, transportService, actionFilters,
|
||||
EqlStatsRequest::new, EqlStatsRequest.NodeStatsRequest::new, ThreadPool.Names.MANAGEMENT,
|
||||
EqlStatsResponse.NodeStatsResponse.class);
|
||||
//this.planExecutor = planExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EqlStatsResponse newResponse(EqlStatsRequest request, List<EqlStatsResponse.NodeStatsResponse> nodes,
|
||||
List<FailedNodeException> failures) {
|
||||
return new EqlStatsResponse(clusterService.getClusterName(), nodes, failures);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EqlStatsRequest.NodeStatsRequest newNodeRequest(EqlStatsRequest request) {
|
||||
return new EqlStatsRequest.NodeStatsRequest(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EqlStatsResponse.NodeStatsResponse newNodeResponse(StreamInput in) throws IOException {
|
||||
return new EqlStatsResponse.NodeStatsResponse(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EqlStatsResponse.NodeStatsResponse nodeOperation(EqlStatsRequest.NodeStatsRequest request) {
|
||||
EqlStatsResponse.NodeStatsResponse statsResponse = new EqlStatsResponse.NodeStatsResponse(clusterService.localNode());
|
||||
//statsResponse.setStats(planExecutor.metrics().stats());
|
||||
|
||||
return statsResponse;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.eql.stats;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum FeatureMetric {
|
||||
SEQUENCE,
|
||||
JOIN,
|
||||
PIPE;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.eql.stats;
|
||||
|
||||
import org.elasticsearch.common.metrics.CounterMetric;
|
||||
import org.elasticsearch.xpack.core.watcher.common.stats.Counters;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Class encapsulating the metrics collected for EQL
|
||||
*/
|
||||
public class Metrics {
|
||||
private enum OperationType {
|
||||
FAILED, TOTAL;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
// map that holds total/failed counters for each eql "feature" (join, pipe, sequence...)
|
||||
private final Map<FeatureMetric, Map<OperationType, CounterMetric>> featuresMetrics;
|
||||
protected static String FPREFIX = "features.";
|
||||
|
||||
public Metrics() {
|
||||
Map<FeatureMetric, Map<OperationType, CounterMetric>> fMap = new LinkedHashMap<>();
|
||||
for (FeatureMetric metric : FeatureMetric.values()) {
|
||||
Map<OperationType, CounterMetric> metricsMap = new LinkedHashMap<>(OperationType.values().length);
|
||||
for (OperationType type : OperationType.values()) {
|
||||
metricsMap.put(type, new CounterMetric());
|
||||
}
|
||||
|
||||
fMap.put(metric, Collections.unmodifiableMap(metricsMap));
|
||||
}
|
||||
featuresMetrics = Collections.unmodifiableMap(fMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the "total" counter for a metric
|
||||
* This method should be called only once per query.
|
||||
*/
|
||||
public void total(FeatureMetric metric) {
|
||||
inc(metric, OperationType.TOTAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the "failed" counter for a metric
|
||||
*/
|
||||
public void failed(FeatureMetric metric) {
|
||||
inc(metric, OperationType.FAILED);
|
||||
}
|
||||
|
||||
private void inc(FeatureMetric metric, OperationType op) {
|
||||
this.featuresMetrics.get(metric).get(op).inc();
|
||||
}
|
||||
|
||||
public Counters stats() {
|
||||
Counters counters = new Counters();
|
||||
|
||||
// queries metrics
|
||||
for (Entry<FeatureMetric, Map<OperationType, CounterMetric>> entry : featuresMetrics.entrySet()) {
|
||||
String metricName = entry.getKey().toString();
|
||||
|
||||
for (OperationType type : OperationType.values()) {
|
||||
long metricCounter = entry.getValue().get(type).count();
|
||||
String operationTypeName = type.toString();
|
||||
|
||||
counters.inc(FPREFIX + metricName + "." + operationTypeName, metricCounter);
|
||||
counters.inc(FPREFIX + "_all." + operationTypeName, metricCounter);
|
||||
}
|
||||
}
|
||||
|
||||
return counters;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.eql;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.PlainActionFuture;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.common.xcontent.ObjectPath;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.xpack.core.eql.EqlFeatureSetUsage;
|
||||
import org.elasticsearch.xpack.core.watcher.common.stats.Counters;
|
||||
import org.elasticsearch.xpack.eql.plugin.EqlStatsAction;
|
||||
import org.elasticsearch.xpack.eql.plugin.EqlStatsResponse;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class EqlFeatureSetTests extends ESTestCase {
|
||||
|
||||
private XPackLicenseState licenseState;
|
||||
private Client client;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
licenseState = mock(XPackLicenseState.class);
|
||||
client = mock(Client.class);
|
||||
ThreadPool threadPool = mock(ThreadPool.class);
|
||||
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
|
||||
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
||||
when(client.threadPool()).thenReturn(threadPool);
|
||||
}
|
||||
|
||||
public void testAvailable() {
|
||||
EqlFeatureSet featureSet = new EqlFeatureSet(Settings.EMPTY, licenseState, client);
|
||||
boolean available = randomBoolean();
|
||||
when(licenseState.isEqlAllowed()).thenReturn(available);
|
||||
assertThat(featureSet.available(), is(available));
|
||||
}
|
||||
|
||||
public void testEnabled() {
|
||||
boolean enabled = randomBoolean();
|
||||
Settings.Builder settings = Settings.builder();
|
||||
settings.put("xpack.eql.enabled", enabled);
|
||||
|
||||
EqlFeatureSet featureSet = new EqlFeatureSet(settings.build(), licenseState, client);
|
||||
assertThat(featureSet.enabled(), is(enabled));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUsageStats() throws Exception {
|
||||
doAnswer(mock -> {
|
||||
ActionListener<EqlStatsResponse> listener =
|
||||
(ActionListener<EqlStatsResponse>) mock.getArguments()[2];
|
||||
|
||||
List<EqlStatsResponse.NodeStatsResponse> nodes = new ArrayList<>();
|
||||
DiscoveryNode first = new DiscoveryNode("first", buildNewFakeTransportAddress(), Version.CURRENT);
|
||||
EqlStatsResponse.NodeStatsResponse firstNode = new EqlStatsResponse.NodeStatsResponse(first);
|
||||
Counters firstCounters = new Counters();
|
||||
firstCounters.inc("foo.foo", 1);
|
||||
firstCounters.inc("foo.bar.baz", 1);
|
||||
firstNode.setStats(firstCounters);
|
||||
nodes.add(firstNode);
|
||||
|
||||
DiscoveryNode second = new DiscoveryNode("second", buildNewFakeTransportAddress(), Version.CURRENT);
|
||||
EqlStatsResponse.NodeStatsResponse secondNode = new EqlStatsResponse.NodeStatsResponse(second);
|
||||
Counters secondCounters = new Counters();
|
||||
secondCounters.inc("spam", 1);
|
||||
secondCounters.inc("foo.bar.baz", 4);
|
||||
secondNode.setStats(secondCounters);
|
||||
nodes.add(secondNode);
|
||||
|
||||
listener.onResponse(new EqlStatsResponse(new ClusterName("whatever"), nodes, Collections.emptyList()));
|
||||
return null;
|
||||
}).when(client).execute(eq(EqlStatsAction.INSTANCE), any(), any());
|
||||
|
||||
PlainActionFuture<EqlFeatureSet.Usage> future = new PlainActionFuture<>();
|
||||
new EqlFeatureSet(Settings.builder().put("xpack.eql.enabled", true).build(), licenseState, client).usage(future);
|
||||
EqlFeatureSetUsage eqlUsage = (EqlFeatureSetUsage) future.get();
|
||||
|
||||
long fooBarBaz = ObjectPath.eval("foo.bar.baz", eqlUsage.stats());
|
||||
long fooFoo = ObjectPath.eval("foo.foo", eqlUsage.stats());
|
||||
long spam = ObjectPath.eval("spam", eqlUsage.stats());
|
||||
|
||||
assertThat(eqlUsage.stats().keySet(), containsInAnyOrder("foo", "spam"));
|
||||
assertThat(fooBarBaz, is(5L));
|
||||
assertThat(fooFoo, is(1L));
|
||||
assertThat(spam, is(1L));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue