1
+ """Transformation classes around the JDL format."""
2
+
1
3
from diraccfg import CFG
4
+ from pydantic import ValidationError
5
+
2
6
from DIRAC import S_OK , S_ERROR
3
7
from DIRAC .Core .Utilities import List
8
+ from DIRAC .Core .Utilities .ClassAd .ClassAdLight import ClassAd
9
+ from DIRAC .WorkloadManagementSystem .Utilities .JobModel import BaseJobDescriptionModel
10
+
11
+ ARGUMENTS = "Arguments"
12
+ BANNED_SITES = "BannedSites"
13
+ CPU_TIME = "CPUTime"
14
+ EXECUTABLE = "Executable"
15
+ EXECUTION_ENVIRONMENT = "ExecutionEnvironment"
16
+ GRID_CE = "GridCE"
17
+ INPUT_DATA = "InputData"
18
+ INPUT_DATA_POLICY = "InputDataPolicy"
19
+ INPUT_SANDBOX = "InputSandbox"
20
+ JOB_CONFIG_ARGS = "JobConfigArgs"
21
+ JOB_TYPE = "JobType"
22
+ JOB_GROUP = "JobGroup"
23
+ LOG_LEVEL = "LogLevel"
24
+ NUMBER_OF_PROCESSORS = "NumberOfProcessors"
25
+ MAX_NUMBER_OF_PROCESSORS = "MaxNumberOfProcessors"
26
+ MIN_NUMBER_OF_PROCESSORS = "MinNumberOfProcessors"
27
+ OUTPUT_DATA = "OutputData"
28
+ OUTPUT_PATH = "OutputPath"
29
+ OUTPUT_SE = "OutputSE"
30
+ PLATFORM = "Platform"
31
+ PRIORITY = "Priority"
32
+ STD_ERROR = "StdError"
33
+ STD_OUTPUT = "StdOutput"
34
+ OUTPUT_SANDBOX = "OutputSandbox"
35
+ JOB_NAME = "JobName"
36
+ SITE = "Site"
37
+ TAGS = "Tags"
38
+
39
+ OWNER = "Owner"
40
+ OWNER_GROUP = "OwnerGroup"
41
+ VO = "VirtualOrganization"
42
+
43
+ CREDENTIALS_FIELDS = {OWNER , OWNER_GROUP , VO }
4
44
5
45
6
46
def loadJDLAsCFG (jdl ):
@@ -138,7 +178,7 @@ def dumpCFGAsJDL(cfg, level=1, tab=" "):
138
178
else :
139
179
val = List .fromChar (cfg [key ])
140
180
# Some attributes are never lists
141
- if len (val ) < 2 or key in ["Arguments" , "Executable" , "StdOutput" , "StdError" ]:
181
+ if len (val ) < 2 or key in [ARGUMENTS , EXECUTABLE , STD_OUTPUT , STD_ERROR ]:
142
182
value = cfg [key ]
143
183
try :
144
184
try_value = float (value )
@@ -157,3 +197,163 @@ def dumpCFGAsJDL(cfg, level=1, tab=" "):
157
197
contents .append ("%s};" % indent )
158
198
contents .append (f"{ tab * (level - 1 )} ]" )
159
199
return "\n " .join (contents )
200
+
201
+
202
+ def jdlToBaseJobDescriptionModel (classAd : ClassAd ):
203
+ """
204
+ Converts a JDL string into a JSON string for data validation from the BaseJob model
205
+ This method allows compatibility with older Client versions that used the _toJDL method
206
+ """
207
+ try :
208
+ jobDescription = BaseJobDescriptionModel (
209
+ executable = classAd .getAttributeString (EXECUTABLE ),
210
+ )
211
+ if classAd .lookupAttribute (ARGUMENTS ):
212
+ jobDescription .arguments = classAd .getAttributeString (ARGUMENTS )
213
+ classAd .deleteAttribute (ARGUMENTS )
214
+
215
+ if classAd .lookupAttribute (BANNED_SITES ):
216
+ jobDescription .bannedSites = classAd .getListFromExpression (BANNED_SITES )
217
+ classAd .deleteAttribute (BANNED_SITES )
218
+
219
+ if classAd .lookupAttribute (CPU_TIME ):
220
+ jobDescription .cpuTime = classAd .getAttributeInt (CPU_TIME )
221
+ classAd .deleteAttribute (CPU_TIME )
222
+
223
+ if classAd .lookupAttribute (EXECUTABLE ):
224
+ jobDescription .executable = classAd .getAttributeString (EXECUTABLE )
225
+ classAd .deleteAttribute (EXECUTABLE )
226
+
227
+ if classAd .lookupAttribute (EXECUTION_ENVIRONMENT ):
228
+ executionEnvironment = classAd .getListFromExpression (EXECUTION_ENVIRONMENT )
229
+ if executionEnvironment :
230
+ jobDescription .executionEnvironment = {}
231
+ for element in executionEnvironment :
232
+ key , value = element .split ("=" )
233
+ if value .isdigit ():
234
+ value = int (value )
235
+ else :
236
+ try :
237
+ value = float (value )
238
+ except ValueError :
239
+ pass
240
+ jobDescription .executionEnvironment [key ] = value
241
+ classAd .deleteAttribute (EXECUTION_ENVIRONMENT )
242
+
243
+ if classAd .lookupAttribute (GRID_CE ):
244
+ jobDescription .gridCE = classAd .getAttributeString (GRID_CE )
245
+ classAd .deleteAttribute (GRID_CE )
246
+
247
+ if classAd .lookupAttribute (INPUT_DATA ):
248
+ jobDescription .inputData = classAd .getListFromExpression (INPUT_DATA )
249
+ classAd .deleteAttribute (INPUT_DATA )
250
+
251
+ if classAd .lookupAttribute (INPUT_DATA_POLICY ):
252
+ jobDescription .inputDataPolicy = classAd .getAttributeString (INPUT_DATA_POLICY )
253
+ classAd .deleteAttribute (INPUT_DATA_POLICY )
254
+
255
+ if classAd .lookupAttribute (INPUT_SANDBOX ):
256
+ jobDescription .inputSandbox = classAd .getListFromExpression (INPUT_SANDBOX )
257
+ classAd .deleteAttribute (INPUT_SANDBOX )
258
+
259
+ if classAd .lookupAttribute (JOB_CONFIG_ARGS ):
260
+ jobDescription .jobConfigArgs = classAd .getAttributeString (JOB_CONFIG_ARGS )
261
+ classAd .deleteAttribute (JOB_CONFIG_ARGS )
262
+
263
+ if classAd .lookupAttribute (JOB_GROUP ):
264
+ jobDescription .jobGroup = classAd .getAttributeString (JOB_GROUP )
265
+ classAd .deleteAttribute (JOB_GROUP )
266
+
267
+ if classAd .lookupAttribute (JOB_NAME ):
268
+ jobDescription .jobName = classAd .getAttributeString (JOB_NAME )
269
+ classAd .deleteAttribute (JOB_NAME )
270
+
271
+ if classAd .lookupAttribute (JOB_TYPE ):
272
+ jobDescription .jobType = classAd .getAttributeString (JOB_TYPE )
273
+ classAd .deleteAttribute (JOB_TYPE )
274
+
275
+ if classAd .lookupAttribute (LOG_LEVEL ):
276
+ jobDescription .logLevel = classAd .getAttributeString (LOG_LEVEL )
277
+ classAd .deleteAttribute (LOG_LEVEL )
278
+
279
+ if classAd .lookupAttribute (NUMBER_OF_PROCESSORS ):
280
+ jobDescription .maxNumberOfProcessors = classAd .getAttributeInt (NUMBER_OF_PROCESSORS )
281
+ jobDescription .minNumberOfProcessors = classAd .getAttributeInt (NUMBER_OF_PROCESSORS )
282
+ classAd .deleteAttribute (NUMBER_OF_PROCESSORS )
283
+ classAd .deleteAttribute (MAX_NUMBER_OF_PROCESSORS )
284
+ classAd .deleteAttribute (MIN_NUMBER_OF_PROCESSORS )
285
+ else :
286
+ if classAd .lookupAttribute (MAX_NUMBER_OF_PROCESSORS ):
287
+ jobDescription .maxNumberOfProcessors = classAd .getAttributeInt (MAX_NUMBER_OF_PROCESSORS )
288
+ classAd .deleteAttribute (MAX_NUMBER_OF_PROCESSORS )
289
+ if classAd .lookupAttribute (MIN_NUMBER_OF_PROCESSORS ):
290
+ jobDescription .minNumberOfProcessors = classAd .getAttributeInt (MIN_NUMBER_OF_PROCESSORS )
291
+ classAd .deleteAttribute (MIN_NUMBER_OF_PROCESSORS )
292
+
293
+ if classAd .lookupAttribute (OUTPUT_DATA ):
294
+ jobDescription .outputData = set (classAd .getListFromExpression (OUTPUT_DATA ))
295
+ classAd .deleteAttribute (OUTPUT_DATA )
296
+
297
+ if classAd .lookupAttribute (OUTPUT_SANDBOX ):
298
+ jobDescription .outputSandbox = set (classAd .getListFromExpression (OUTPUT_SANDBOX ))
299
+ classAd .deleteAttribute (OUTPUT_SANDBOX )
300
+
301
+ if classAd .lookupAttribute (OUTPUT_PATH ):
302
+ jobDescription .outputPath = classAd .getAttributeString (OUTPUT_PATH )
303
+ classAd .deleteAttribute (OUTPUT_PATH )
304
+
305
+ if classAd .lookupAttribute (OUTPUT_SE ):
306
+ jobDescription .outputSE = classAd .getAttributeString (OUTPUT_SE )
307
+ classAd .deleteAttribute (OUTPUT_SE )
308
+
309
+ if classAd .lookupAttribute (SITE ):
310
+ jobDescription .sites = classAd .getListFromExpression (SITE )
311
+ classAd .deleteAttribute (SITE )
312
+
313
+ if classAd .lookupAttribute (PLATFORM ):
314
+ jobDescription .platform = classAd .getAttributeString (PLATFORM )
315
+ classAd .deleteAttribute (PLATFORM )
316
+
317
+ if classAd .lookupAttribute (PRIORITY ):
318
+ jobDescription .priority = classAd .getAttributeInt (PRIORITY )
319
+ classAd .deleteAttribute (PRIORITY )
320
+
321
+ if classAd .lookupAttribute (STD_OUTPUT ):
322
+ jobDescription .stdout = classAd .getAttributeString (STD_OUTPUT )
323
+ classAd .deleteAttribute (STD_OUTPUT )
324
+
325
+ if classAd .lookupAttribute (STD_ERROR ):
326
+ jobDescription .stderr = classAd .getAttributeString (STD_ERROR )
327
+ classAd .deleteAttribute (STD_ERROR )
328
+
329
+ if classAd .lookupAttribute (TAGS ):
330
+ jobDescription .tags = classAd .getListFromExpression (TAGS )
331
+ classAd .deleteAttribute (TAGS )
332
+
333
+ # Remove credentials
334
+ for attribute in CREDENTIALS_FIELDS :
335
+ classAd .deleteAttribute (attribute )
336
+
337
+ # Remove legacy attributes
338
+ for attribute in {"DIRACSetup" , "OwnerDN" }:
339
+ classAd .deleteAttribute (attribute )
340
+
341
+ for attribute in classAd .getAttributes ():
342
+ if not jobDescription .extraFields :
343
+ jobDescription .extraFields = {}
344
+
345
+ value = classAd .getAttributeString (attribute )
346
+ if value .isdigit ():
347
+ value = int (value )
348
+ else :
349
+ try :
350
+ value = float (value )
351
+ except ValueError :
352
+ pass
353
+
354
+ jobDescription .extraFields [attribute ] = value
355
+
356
+ except ValidationError as e :
357
+ return S_ERROR (f"Invalid JDL: { e } " )
358
+
359
+ return S_OK (jobDescription )
0 commit comments