43
43
import org .elasticsearch .persistent .PersistentTasksExecutor ;
44
44
import org .elasticsearch .persistent .PersistentTasksService ;
45
45
import org .elasticsearch .xpack .ml .MachineLearning ;
46
+ import org .elasticsearch .xpack .ml .datafeed .MlRemoteLicenseChecker ;
46
47
import org .elasticsearch .xpack .ml .datafeed .DatafeedManager ;
47
48
import org .elasticsearch .xpack .ml .datafeed .DatafeedNodeSelector ;
48
49
import org .elasticsearch .xpack .ml .datafeed .extractor .DataExtractorFactory ;
49
50
51
+ import java .util .List ;
50
52
import java .util .Map ;
51
53
import java .util .function .Predicate ;
52
54
@@ -111,40 +113,65 @@ protected void masterOperation(StartDatafeedAction.Request request, ClusterState
111
113
ActionListener <StartDatafeedAction .Response > listener ) {
112
114
StartDatafeedAction .DatafeedParams params = request .getParams ();
113
115
if (licenseState .isMachineLearningAllowed ()) {
114
- ActionListener <PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams >> finalListener =
116
+
117
+ ActionListener <PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams >> waitForTaskListener =
115
118
new ActionListener <PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams >>() {
116
- @ Override
117
- public void onResponse (PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams > persistentTask ) {
118
- waitForDatafeedStarted (persistentTask .getId (), params , listener );
119
- }
119
+ @ Override
120
+ public void onResponse (PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams >
121
+ persistentTask ) {
122
+ waitForDatafeedStarted (persistentTask .getId (), params , listener );
123
+ }
120
124
121
- @ Override
122
- public void onFailure (Exception e ) {
123
- if (e instanceof ResourceAlreadyExistsException ) {
124
- logger .debug ("datafeed already started" , e );
125
- e = new ElasticsearchStatusException ("cannot start datafeed [" + params .getDatafeedId () +
126
- "] because it has already been started" , RestStatus .CONFLICT );
127
- }
128
- listener .onFailure (e );
129
- }
130
- };
125
+ @ Override
126
+ public void onFailure (Exception e ) {
127
+ if (e instanceof ResourceAlreadyExistsException ) {
128
+ logger .debug ("datafeed already started" , e );
129
+ e = new ElasticsearchStatusException ("cannot start datafeed [" + params .getDatafeedId () +
130
+ "] because it has already been started" , RestStatus .CONFLICT );
131
+ }
132
+ listener .onFailure (e );
133
+ }
134
+ };
131
135
132
136
// Verify data extractor factory can be created, then start persistent task
133
137
MlMetadata mlMetadata = MlMetadata .getMlMetadata (state );
134
138
PersistentTasksCustomMetaData tasks = state .getMetaData ().custom (PersistentTasksCustomMetaData .TYPE );
135
139
validate (params .getDatafeedId (), mlMetadata , tasks );
136
140
DatafeedConfig datafeed = mlMetadata .getDatafeed (params .getDatafeedId ());
137
141
Job job = mlMetadata .getJobs ().get (datafeed .getJobId ());
138
- DataExtractorFactory .create (client , datafeed , job , ActionListener .wrap (
139
- dataExtractorFactory ->
140
- persistentTasksService .sendStartRequest (MLMetadataField .datafeedTaskId (params .getDatafeedId ()),
141
- StartDatafeedAction .TASK_NAME , params , finalListener )
142
- , listener ::onFailure ));
142
+
143
+ if (MlRemoteLicenseChecker .containsRemoteIndex (datafeed .getIndices ())) {
144
+ MlRemoteLicenseChecker remoteLicenseChecker = new MlRemoteLicenseChecker (client );
145
+ remoteLicenseChecker .checkRemoteClusterLicenses (MlRemoteLicenseChecker .remoteClusterNames (datafeed .getIndices ()),
146
+ ActionListener .wrap (
147
+ response -> {
148
+ if (response .isViolated ()) {
149
+ listener .onFailure (createUnlicensedError (datafeed .getId (), response ));
150
+ } else {
151
+ createDataExtractor (job , datafeed , params , waitForTaskListener );
152
+ }
153
+ },
154
+ e -> listener .onFailure (createUnknownLicenseError (datafeed .getId (),
155
+ MlRemoteLicenseChecker .remoteIndices (datafeed .getIndices ()), e ))
156
+ ));
157
+ } else {
158
+ createDataExtractor (job , datafeed , params , waitForTaskListener );
159
+ }
143
160
} else {
144
161
listener .onFailure (LicenseUtils .newComplianceException (XPackField .MACHINE_LEARNING ));
145
162
}
146
163
}
147
164
165
+ private void createDataExtractor (Job job , DatafeedConfig datafeed , StartDatafeedAction .DatafeedParams params ,
166
+ ActionListener <PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams >>
167
+ listener ) {
168
+ DataExtractorFactory .create (client , datafeed , job , ActionListener .wrap (
169
+ dataExtractorFactory ->
170
+ persistentTasksService .sendStartRequest (MLMetadataField .datafeedTaskId (params .getDatafeedId ()),
171
+ StartDatafeedAction .TASK_NAME , params , listener )
172
+ , listener ::onFailure ));
173
+ }
174
+
148
175
@ Override
149
176
protected ClusterBlockException checkBlock (StartDatafeedAction .Request request , ClusterState state ) {
150
177
// We only delegate here to PersistentTasksService, but if there is a metadata writeblock,
@@ -158,28 +185,29 @@ private void waitForDatafeedStarted(String taskId, StartDatafeedAction.DatafeedP
158
185
DatafeedPredicate predicate = new DatafeedPredicate ();
159
186
persistentTasksService .waitForPersistentTaskCondition (taskId , predicate , params .getTimeout (),
160
187
new PersistentTasksService .WaitForPersistentTaskListener <StartDatafeedAction .DatafeedParams >() {
161
- @ Override
162
- public void onResponse (PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams > persistentTask ) {
163
- if (predicate .exception != null ) {
164
- // We want to return to the caller without leaving an unassigned persistent task, to match
165
- // what would have happened if the error had been detected in the "fast fail" validation
166
- cancelDatafeedStart (persistentTask , predicate .exception , listener );
167
- } else {
168
- listener .onResponse (new StartDatafeedAction .Response (true ));
169
- }
170
- }
188
+ @ Override
189
+ public void onResponse (PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams >
190
+ persistentTask ) {
191
+ if (predicate .exception != null ) {
192
+ // We want to return to the caller without leaving an unassigned persistent task, to match
193
+ // what would have happened if the error had been detected in the "fast fail" validation
194
+ cancelDatafeedStart (persistentTask , predicate .exception , listener );
195
+ } else {
196
+ listener .onResponse (new StartDatafeedAction .Response (true ));
197
+ }
198
+ }
171
199
172
- @ Override
173
- public void onFailure (Exception e ) {
174
- listener .onFailure (e );
175
- }
200
+ @ Override
201
+ public void onFailure (Exception e ) {
202
+ listener .onFailure (e );
203
+ }
176
204
177
- @ Override
178
- public void onTimeout (TimeValue timeout ) {
179
- listener .onFailure (new ElasticsearchException ("Starting datafeed ["
180
- + params .getDatafeedId () + "] timed out after [" + timeout + "]" ));
181
- }
182
- });
205
+ @ Override
206
+ public void onTimeout (TimeValue timeout ) {
207
+ listener .onFailure (new ElasticsearchException ("Starting datafeed ["
208
+ + params .getDatafeedId () + "] timed out after [" + timeout + "]" ));
209
+ }
210
+ });
183
211
}
184
212
185
213
private void cancelDatafeedStart (PersistentTasksCustomMetaData .PersistentTask <StartDatafeedAction .DatafeedParams > persistentTask ,
@@ -203,6 +231,25 @@ public void onFailure(Exception e) {
203
231
);
204
232
}
205
233
234
+ private ElasticsearchStatusException createUnlicensedError (String datafeedId ,
235
+ MlRemoteLicenseChecker .LicenseViolation licenseViolation ) {
236
+ String message = "Cannot start datafeed [" + datafeedId + "] as it is configured to use "
237
+ + "indices on a remote cluster [" + licenseViolation .get ().getClusterName ()
238
+ + "] that is not licensed for Machine Learning. "
239
+ + MlRemoteLicenseChecker .buildErrorMessage (licenseViolation .get ());
240
+
241
+ return new ElasticsearchStatusException (message , RestStatus .BAD_REQUEST );
242
+ }
243
+
244
+ private ElasticsearchStatusException createUnknownLicenseError (String datafeedId , List <String > remoteIndices ,
245
+ Exception cause ) {
246
+ String message = "Cannot start datafeed [" + datafeedId + "] as it is configured to use"
247
+ + " indices on a remote cluster " + remoteIndices
248
+ + " but the license type could not be verified" ;
249
+
250
+ return new ElasticsearchStatusException (message , RestStatus .BAD_REQUEST , new Exception (cause .getMessage ()));
251
+ }
252
+
206
253
public static class StartDatafeedPersistentTasksExecutor extends PersistentTasksExecutor <StartDatafeedAction .DatafeedParams > {
207
254
private final DatafeedManager datafeedManager ;
208
255
private final IndexNameExpressionResolver resolver ;
0 commit comments