diff --git a/docs/reference/rest-api/info.asciidoc b/docs/reference/rest-api/info.asciidoc
index bfd7cad1389fe..586188bd5b762 100644
--- a/docs/reference/rest-api/info.asciidoc
+++ b/docs/reference/rest-api/info.asciidoc
@@ -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:
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java
index 4a532ccbf6765..6e70cfad543c6 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java
@@ -706,6 +706,15 @@ public boolean isEnrichAllowed() {
return localStatus.active;
}
+ /**
+ * Determine if EQL support should be enabled.
+ *
+ * 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.
*
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java
index 7bc7ea9c42df7..d948d1c7199fc 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java
@@ -44,6 +44,7 @@
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.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 List getNamedWriteables() {
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
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java
index 931a55db35038..4c9237d7f7192 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java
@@ -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. */
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/eql/EqlFeatureSetUsage.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/eql/EqlFeatureSetUsage.java
new file mode 100644
index 0000000000000..55b0ff3d051d7
--- /dev/null
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/eql/EqlFeatureSetUsage.java
@@ -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 stats;
+
+ public EqlFeatureSetUsage(StreamInput in) throws IOException {
+ super(in);
+ stats = in.readMap();
+ }
+
+ public EqlFeatureSetUsage(boolean available, boolean enabled, Map stats) {
+ super(XPackField.EQL, available, enabled);
+ this.stats = stats;
+ }
+
+ public Map stats() {
+ return stats;
+ }
+
+ @Override
+ protected void innerXContent(XContentBuilder builder, Params params) throws IOException {
+ super.innerXContent(builder, params);
+ if (enabled) {
+ for (Map.Entry entry : stats.entrySet()) {
+ builder.field(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ super.writeTo(out);
+ out.writeMap(stats);
+ }
+}
diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/EqlFeatureSet.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/EqlFeatureSet.java
new file mode 100644
index 0000000000000..d01cbac5a7480
--- /dev/null
+++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/EqlFeatureSet.java
@@ -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 nativeCodeInfo() {
+ return null;
+ }
+
+ @Override
+ public void usage(ActionListener listener) {
+ if (enabled) {
+ EqlStatsRequest request = new EqlStatsRequest();
+ request.includeStats(true);
+ client.execute(EqlStatsAction.INSTANCE, request, ActionListener.wrap(r -> {
+ List 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()));
+ }
+ }
+
+}
diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java
index 14cbae5e1a346..375789761d7df 100644
--- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java
+++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java
@@ -12,6 +12,7 @@
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.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 @@ private Collection