panda-server-source 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) hide show
  1. panda_server_source-0.8.0.dist-info/METADATA +55 -0
  2. panda_server_source-0.8.0.dist-info/RECORD +375 -0
  3. panda_server_source-0.8.0.dist-info/WHEEL +4 -0
  4. panda_server_source-0.8.0.dist-info/licenses/LICENSE.txt +202 -0
  5. pandajedi/__init__.py +0 -0
  6. pandajedi/jedibrokerage/AtlasAnalJobBroker.py +2343 -0
  7. pandajedi/jedibrokerage/AtlasBrokerUtils.py +1292 -0
  8. pandajedi/jedibrokerage/AtlasProdJobBroker.py +2064 -0
  9. pandajedi/jedibrokerage/AtlasProdTaskBroker.py +629 -0
  10. pandajedi/jedibrokerage/GenJobBroker.py +381 -0
  11. pandajedi/jedibrokerage/JobBrokerBase.py +133 -0
  12. pandajedi/jedibrokerage/TaskBrokerBase.py +15 -0
  13. pandajedi/jedibrokerage/__init__.py +0 -0
  14. pandajedi/jediconfig/__init__.py +0 -0
  15. pandajedi/jediconfig/jedi_config.py +40 -0
  16. pandajedi/jedicore/FactoryBase.py +182 -0
  17. pandajedi/jedicore/Interaction.py +433 -0
  18. pandajedi/jedicore/JediDBProxy.py +56 -0
  19. pandajedi/jedicore/JediDBProxyPool.py +37 -0
  20. pandajedi/jedicore/JediDBSchemaInfo.py +17 -0
  21. pandajedi/jedicore/JediException.py +13 -0
  22. pandajedi/jedicore/JediTaskBuffer.py +984 -0
  23. pandajedi/jedicore/JediTaskBufferInterface.py +50 -0
  24. pandajedi/jedicore/MsgWrapper.py +124 -0
  25. pandajedi/jedicore/ProcessUtils.py +34 -0
  26. pandajedi/jedicore/SiteCandidate.py +90 -0
  27. pandajedi/jedicore/ThreadUtils.py +205 -0
  28. pandajedi/jedicore/__init__.py +0 -0
  29. pandajedi/jediddm/AtlasDDMClient.py +1708 -0
  30. pandajedi/jediddm/DDMClientBase.py +16 -0
  31. pandajedi/jediddm/DDMInterface.py +83 -0
  32. pandajedi/jediddm/GenDDMClient.py +17 -0
  33. pandajedi/jediddm/__init__.py +0 -0
  34. pandajedi/jedidog/AtlasAnalWatchDog.py +648 -0
  35. pandajedi/jedidog/AtlasDataCarouselWatchDog.py +189 -0
  36. pandajedi/jedidog/AtlasDataLocalityUpdaterWatchDog.py +215 -0
  37. pandajedi/jedidog/AtlasProdWatchDog.py +443 -0
  38. pandajedi/jedidog/AtlasQueueFillerWatchDog.py +800 -0
  39. pandajedi/jedidog/AtlasTaskWithholderWatchDog.py +245 -0
  40. pandajedi/jedidog/AtlasWorkflowManagerWatchDog.py +63 -0
  41. pandajedi/jedidog/GenWatchDog.py +21 -0
  42. pandajedi/jedidog/JumboWatchDog.py +147 -0
  43. pandajedi/jedidog/TypicalWatchDogBase.py +122 -0
  44. pandajedi/jedidog/WatchDogBase.py +53 -0
  45. pandajedi/jedidog/__init__.py +0 -0
  46. pandajedi/jediexec/reniceJEDI.py +25 -0
  47. pandajedi/jedigen/AtlasTaskGenerator.py +110 -0
  48. pandajedi/jedigen/TaskGeneratorBase.py +15 -0
  49. pandajedi/jedigen/__init__.py +0 -0
  50. pandajedi/jedimsgprocessor/__init__.py +0 -0
  51. pandajedi/jedimsgprocessor/ai_parameter_adjuster_msg_processor +71 -0
  52. pandajedi/jedimsgprocessor/atlas_idds_msg_processor.py +66 -0
  53. pandajedi/jedimsgprocessor/base_msg_processor.py +33 -0
  54. pandajedi/jedimsgprocessor/forwarding_msg_processor.py +29 -0
  55. pandajedi/jedimsgprocessor/hpo_msg_processor.py +93 -0
  56. pandajedi/jedimsgprocessor/jedi_contents_feeder_msg_processor.py +63 -0
  57. pandajedi/jedimsgprocessor/jedi_job_generator_msg_processor.py +185 -0
  58. pandajedi/jedimsgprocessor/jedi_post_processor_msg_processor.py +82 -0
  59. pandajedi/jedimsgprocessor/kafka_msg_processor.py +44 -0
  60. pandajedi/jedimsgprocessor/panda_callback_msg_processor.py +182 -0
  61. pandajedi/jedimsgprocessor/panda_to_jedi_msg_processor.py +74 -0
  62. pandajedi/jedimsgprocessor/processing_msg_processor.py +134 -0
  63. pandajedi/jedimsgprocessor/status_report_msg_processor.py +129 -0
  64. pandajedi/jedimsgprocessor/tape_carousel_msg_processor.py +118 -0
  65. pandajedi/jedimsgprocessor/workflow_manager_msg_processor.py +89 -0
  66. pandajedi/jediorder/ContentsFeeder.py +785 -0
  67. pandajedi/jediorder/JediDaemon.py +27 -0
  68. pandajedi/jediorder/JediKnight.py +55 -0
  69. pandajedi/jediorder/JediMaster.py +238 -0
  70. pandajedi/jediorder/JediMsgProcessor.py +32 -0
  71. pandajedi/jediorder/JobBroker.py +33 -0
  72. pandajedi/jediorder/JobGenerator.py +2763 -0
  73. pandajedi/jediorder/JobSplitter.py +308 -0
  74. pandajedi/jediorder/JobThrottler.py +28 -0
  75. pandajedi/jediorder/PostProcessor.py +166 -0
  76. pandajedi/jediorder/TaskBroker.py +268 -0
  77. pandajedi/jediorder/TaskCommando.py +334 -0
  78. pandajedi/jediorder/TaskGenerator.py +17 -0
  79. pandajedi/jediorder/TaskRefiner.py +611 -0
  80. pandajedi/jediorder/TaskSetupper.py +17 -0
  81. pandajedi/jediorder/WatchDog.py +79 -0
  82. pandajedi/jediorder/__init__.py +0 -0
  83. pandajedi/jedipprocess/AtlasAnalPostProcessor.py +404 -0
  84. pandajedi/jedipprocess/AtlasPostProcessorUtils.py +50 -0
  85. pandajedi/jedipprocess/AtlasProdPostProcessor.py +178 -0
  86. pandajedi/jedipprocess/GenPostProcessor.py +35 -0
  87. pandajedi/jedipprocess/MailTemplates.py +185 -0
  88. pandajedi/jedipprocess/PostProcessorBase.py +269 -0
  89. pandajedi/jedipprocess/__init__.py +0 -0
  90. pandajedi/jedirefine/AtlasAnalTaskRefiner.py +259 -0
  91. pandajedi/jedirefine/AtlasProdTaskRefiner.py +162 -0
  92. pandajedi/jedirefine/GenTaskRefiner.py +61 -0
  93. pandajedi/jedirefine/RefinerUtils.py +154 -0
  94. pandajedi/jedirefine/TaskRefinerBase.py +996 -0
  95. pandajedi/jedirefine/__init__.py +0 -0
  96. pandajedi/jedisetup/AtlasTaskSetupper.py +309 -0
  97. pandajedi/jedisetup/GenTaskSetupper.py +16 -0
  98. pandajedi/jedisetup/SimpleTaskSetupper.py +152 -0
  99. pandajedi/jedisetup/TaskSetupperBase.py +15 -0
  100. pandajedi/jedisetup/__init__.py +0 -0
  101. pandajedi/jeditest/SchemaChecker.py +41 -0
  102. pandajedi/jeditest/addContWoSoTask.py +54 -0
  103. pandajedi/jeditest/addDataCarouselRequest.py +47 -0
  104. pandajedi/jeditest/addEventServiceTask.py +101 -0
  105. pandajedi/jeditest/addHpoTask.py +81 -0
  106. pandajedi/jeditest/addIDDSStageInRequest.py +56 -0
  107. pandajedi/jeditest/addNonAtlasTask.py +71 -0
  108. pandajedi/jeditest/addNonAtlasTaskEnc.py +64 -0
  109. pandajedi/jeditest/addTestTask.py +124 -0
  110. pandajedi/jeditest/addTestTaskParamToDEFT.py +70 -0
  111. pandajedi/jeditest/addTestTaskWithMerger.py +97 -0
  112. pandajedi/jeditest/brokerTest.py +78 -0
  113. pandajedi/jeditest/cancelDataCarouselRequest.py +35 -0
  114. pandajedi/jeditest/contentsFeederTest.py +28 -0
  115. pandajedi/jeditest/contentsFeederTestWithTask.py +18 -0
  116. pandajedi/jeditest/ddmTest.py +4 -0
  117. pandajedi/jeditest/jobGenForOneTask.py +38 -0
  118. pandajedi/jeditest/jobGeneratorTest.py +43 -0
  119. pandajedi/jeditest/jobSplitterTest.py +104 -0
  120. pandajedi/jeditest/jobThrottlerTest.py +22 -0
  121. pandajedi/jeditest/msgProcessorTest.py +9 -0
  122. pandajedi/jeditest/postProcessorTest.py +18 -0
  123. pandajedi/jeditest/resubmitDataCarouselRequest.py +35 -0
  124. pandajedi/jeditest/resubmitTaskParam.py +20 -0
  125. pandajedi/jeditest/taskBrokerTest.py +22 -0
  126. pandajedi/jeditest/taskBrokerTestWithTask.py +38 -0
  127. pandajedi/jeditest/taskBufferTest.py +5 -0
  128. pandajedi/jeditest/taskCommandoTest.py +28 -0
  129. pandajedi/jeditest/taskRefinerTest.py +30 -0
  130. pandajedi/jeditest/testOneTaskToRefine.py +49 -0
  131. pandajedi/jeditest/testOneTaskToTaskBroker.py +42 -0
  132. pandajedi/jeditest/testPandaDDMMap.py +62 -0
  133. pandajedi/jeditest/updateDataCarouselRequestParams.py +50 -0
  134. pandajedi/jeditest/watchDogTest.py +25 -0
  135. pandajedi/jeditest/workQueueTest.py +22 -0
  136. pandajedi/jedithrottle/AtlasAnalJobThrottler.py +19 -0
  137. pandajedi/jedithrottle/AtlasProdJobThrottler.py +18 -0
  138. pandajedi/jedithrottle/GenJobThrottler.py +26 -0
  139. pandajedi/jedithrottle/JobThrottlerBase.py +496 -0
  140. pandajedi/jedithrottle/__init__.py +0 -0
  141. pandaserver/__init__.py +0 -0
  142. pandaserver/api/__init__.py +0 -0
  143. pandaserver/api/v1/__init__.py +0 -0
  144. pandaserver/api/v1/common.py +339 -0
  145. pandaserver/api/v1/credential_management_api.py +372 -0
  146. pandaserver/api/v1/data_carousel_api.py +264 -0
  147. pandaserver/api/v1/event_api.py +269 -0
  148. pandaserver/api/v1/file_server_api.py +942 -0
  149. pandaserver/api/v1/harvester_api.py +445 -0
  150. pandaserver/api/v1/http_client.py +166 -0
  151. pandaserver/api/v1/idds_api.py +180 -0
  152. pandaserver/api/v1/job_api.py +463 -0
  153. pandaserver/api/v1/metaconfig_api.py +122 -0
  154. pandaserver/api/v1/pilot_api.py +895 -0
  155. pandaserver/api/v1/statistics_api.py +207 -0
  156. pandaserver/api/v1/system_api.py +186 -0
  157. pandaserver/api/v1/task_api.py +1520 -0
  158. pandaserver/api/v1/tests/__init__.py +0 -0
  159. pandaserver/api/v1/tests/credential_management_api_tests.py +116 -0
  160. pandaserver/api/v1/tests/data_carousel_api_tests.py +82 -0
  161. pandaserver/api/v1/tests/harvester_api_tests.py +188 -0
  162. pandaserver/api/v1/tests/idds_api_tests.py +45 -0
  163. pandaserver/api/v1/tests/job_api_tests.py +112 -0
  164. pandaserver/api/v1/tests/pilot_api_tests.py +154 -0
  165. pandaserver/api/v1/tests/statistics_api_tests.py +104 -0
  166. pandaserver/api/v1/tests/system_api_tests.py +58 -0
  167. pandaserver/api/v1/tests/task_api_tests.py +590 -0
  168. pandaserver/api/v1/version.py +2 -0
  169. pandaserver/api/v1/workflow_api.py +136 -0
  170. pandaserver/brokerage/SiteMapper.py +406 -0
  171. pandaserver/brokerage/__init__.py +0 -0
  172. pandaserver/brokerage/broker.py +272 -0
  173. pandaserver/config/__init__.py +0 -0
  174. pandaserver/config/config_utils.py +15 -0
  175. pandaserver/config/daemon_config.py +29 -0
  176. pandaserver/config/mcp_config.py +38 -0
  177. pandaserver/config/panda_config.py +233 -0
  178. pandaserver/configurator/Carbon.py +65 -0
  179. pandaserver/configurator/Configurator.py +908 -0
  180. pandaserver/configurator/__init__.py +0 -0
  181. pandaserver/configurator/aux.py +73 -0
  182. pandaserver/daemons/__init__.py +0 -0
  183. pandaserver/daemons/master.py +110 -0
  184. pandaserver/daemons/master_systemd.py +79 -0
  185. pandaserver/daemons/scripts/__init__.py +0 -0
  186. pandaserver/daemons/scripts/add_main.py +264 -0
  187. pandaserver/daemons/scripts/add_sub.py +221 -0
  188. pandaserver/daemons/scripts/cache_pilots.py +47 -0
  189. pandaserver/daemons/scripts/cache_schedconfig.py +286 -0
  190. pandaserver/daemons/scripts/carbon.py +50 -0
  191. pandaserver/daemons/scripts/configurator.py +94 -0
  192. pandaserver/daemons/scripts/copyArchive.py +1387 -0
  193. pandaserver/daemons/scripts/datasetManager.py +1154 -0
  194. pandaserver/daemons/scripts/dummy_test.py +25 -0
  195. pandaserver/daemons/scripts/evpPD2P.py +144 -0
  196. pandaserver/daemons/scripts/hs_scrapers.py +264 -0
  197. pandaserver/daemons/scripts/metric_collector.py +927 -0
  198. pandaserver/daemons/scripts/panda_activeusers_query.py +80 -0
  199. pandaserver/daemons/scripts/pilotStreaming.py +109 -0
  200. pandaserver/daemons/scripts/process_workflow_files_daemon.py +166 -0
  201. pandaserver/daemons/scripts/recover_lost_files_daemon.py +177 -0
  202. pandaserver/daemons/scripts/task_evaluator.py +405 -0
  203. pandaserver/daemons/scripts/tmpwatch.py +70 -0
  204. pandaserver/daemons/scripts/worker_synchronization.py +97 -0
  205. pandaserver/daemons/utils.py +753 -0
  206. pandaserver/dataservice/DataServiceUtils.py +323 -0
  207. pandaserver/dataservice/ErrorCode.py +25 -0
  208. pandaserver/dataservice/RecoverLostFilesCore.py +322 -0
  209. pandaserver/dataservice/__init__.py +0 -0
  210. pandaserver/dataservice/activator.py +79 -0
  211. pandaserver/dataservice/adder_atlas_plugin.py +1106 -0
  212. pandaserver/dataservice/adder_dummy_plugin.py +31 -0
  213. pandaserver/dataservice/adder_gen.py +954 -0
  214. pandaserver/dataservice/adder_plugin_base.py +27 -0
  215. pandaserver/dataservice/adder_result.py +75 -0
  216. pandaserver/dataservice/adder_simple_plugin.py +160 -0
  217. pandaserver/dataservice/closer.py +247 -0
  218. pandaserver/dataservice/closer_atlas_plugin.py +90 -0
  219. pandaserver/dataservice/ddm.py +1381 -0
  220. pandaserver/dataservice/ddm_handler.py +95 -0
  221. pandaserver/dataservice/dyn_data_distributer.py +824 -0
  222. pandaserver/dataservice/event_lookup_client_ei.py +77 -0
  223. pandaserver/dataservice/event_picker.py +268 -0
  224. pandaserver/dataservice/finisher.py +194 -0
  225. pandaserver/dataservice/setupper.py +225 -0
  226. pandaserver/dataservice/setupper_atlas_plugin.py +1851 -0
  227. pandaserver/dataservice/setupper_dummy_plugin.py +47 -0
  228. pandaserver/dataservice/setupper_plugin_base.py +67 -0
  229. pandaserver/jobdispatcher/DispatcherUtils.py +35 -0
  230. pandaserver/jobdispatcher/ErrorCode.py +19 -0
  231. pandaserver/jobdispatcher/JobDispatcher.py +1625 -0
  232. pandaserver/jobdispatcher/Protocol.py +443 -0
  233. pandaserver/jobdispatcher/Watcher.py +186 -0
  234. pandaserver/jobdispatcher/__init__.py +0 -0
  235. pandaserver/pandamcp/__init__.py +0 -0
  236. pandaserver/pandamcp/mcp_main.py +56 -0
  237. pandaserver/pandamcp/mcp_test_client.py +95 -0
  238. pandaserver/pandamcp/mcp_utils.py +74 -0
  239. pandaserver/proxycache/__init__.py +0 -0
  240. pandaserver/proxycache/panda_proxy_cache.py +240 -0
  241. pandaserver/proxycache/token_cache.py +141 -0
  242. pandaserver/server/.gacl +6 -0
  243. pandaserver/server/panda.py +539 -0
  244. pandaserver/srvcore/CoreUtils.py +572 -0
  245. pandaserver/srvcore/MailUtils.py +106 -0
  246. pandaserver/srvcore/__init__.py +0 -0
  247. pandaserver/srvcore/allowed_methods.py +100 -0
  248. pandaserver/srvcore/oidc_utils.py +152 -0
  249. pandaserver/srvcore/panda_request.py +196 -0
  250. pandaserver/srvcore/srv_msg_utils.py +42 -0
  251. pandaserver/taskbuffer/ConBridge.py +513 -0
  252. pandaserver/taskbuffer/DBProxyPool.py +95 -0
  253. pandaserver/taskbuffer/DataCarousel.py +3053 -0
  254. pandaserver/taskbuffer/DatasetSpec.py +137 -0
  255. pandaserver/taskbuffer/DdmSpec.py +201 -0
  256. pandaserver/taskbuffer/ErrorCode.py +87 -0
  257. pandaserver/taskbuffer/EventServiceUtils.py +396 -0
  258. pandaserver/taskbuffer/FileSpec.py +290 -0
  259. pandaserver/taskbuffer/GlobalShares.py +244 -0
  260. pandaserver/taskbuffer/HarvesterMetricsSpec.py +92 -0
  261. pandaserver/taskbuffer/Initializer.py +82 -0
  262. pandaserver/taskbuffer/InputChunk.py +1080 -0
  263. pandaserver/taskbuffer/JediCacheSpec.py +57 -0
  264. pandaserver/taskbuffer/JediDatasetSpec.py +588 -0
  265. pandaserver/taskbuffer/JediFileSpec.py +242 -0
  266. pandaserver/taskbuffer/JediTaskSpec.py +1687 -0
  267. pandaserver/taskbuffer/JobSpec.py +1006 -0
  268. pandaserver/taskbuffer/JobUtils.py +232 -0
  269. pandaserver/taskbuffer/NucleusSpec.py +116 -0
  270. pandaserver/taskbuffer/OraDBProxy.py +174 -0
  271. pandaserver/taskbuffer/PanDAMsgProcessor.py +8 -0
  272. pandaserver/taskbuffer/PandaDBSchemaInfo.py +18 -0
  273. pandaserver/taskbuffer/ParseJobXML.py +350 -0
  274. pandaserver/taskbuffer/PickleFileSpec.py +18 -0
  275. pandaserver/taskbuffer/PickleJobSpec.py +25 -0
  276. pandaserver/taskbuffer/PrioUtil.py +40 -0
  277. pandaserver/taskbuffer/ProcessGroups.py +111 -0
  278. pandaserver/taskbuffer/ResourceSpec.py +175 -0
  279. pandaserver/taskbuffer/SQLDumper.py +37 -0
  280. pandaserver/taskbuffer/SQLManager.py +60 -0
  281. pandaserver/taskbuffer/SiteSpec.py +304 -0
  282. pandaserver/taskbuffer/SupErrors.py +10 -0
  283. pandaserver/taskbuffer/TaskBuffer.py +2882 -0
  284. pandaserver/taskbuffer/TaskBufferInterface.py +163 -0
  285. pandaserver/taskbuffer/Utils.py +848 -0
  286. pandaserver/taskbuffer/WorkQueue.py +227 -0
  287. pandaserver/taskbuffer/WorkQueueMapper.py +203 -0
  288. pandaserver/taskbuffer/WorkerSpec.py +143 -0
  289. pandaserver/taskbuffer/WrappedCursor.py +487 -0
  290. pandaserver/taskbuffer/WrappedPickle.py +88 -0
  291. pandaserver/taskbuffer/WrappedPostgresConn.py +24 -0
  292. pandaserver/taskbuffer/__init__.py +0 -0
  293. pandaserver/taskbuffer/db_proxy_mods/OraDBProxy_structure.png +0 -0
  294. pandaserver/taskbuffer/db_proxy_mods/__init__.py +7 -0
  295. pandaserver/taskbuffer/db_proxy_mods/base_module.py +841 -0
  296. pandaserver/taskbuffer/db_proxy_mods/data_carousel_module.py +772 -0
  297. pandaserver/taskbuffer/db_proxy_mods/entity_module.py +3804 -0
  298. pandaserver/taskbuffer/db_proxy_mods/job_complex_module.py +5661 -0
  299. pandaserver/taskbuffer/db_proxy_mods/job_standalone_module.py +2640 -0
  300. pandaserver/taskbuffer/db_proxy_mods/metrics_module.py +874 -0
  301. pandaserver/taskbuffer/db_proxy_mods/misc_standalone_module.py +4272 -0
  302. pandaserver/taskbuffer/db_proxy_mods/task_complex_module.py +6172 -0
  303. pandaserver/taskbuffer/db_proxy_mods/task_event_module.py +4953 -0
  304. pandaserver/taskbuffer/db_proxy_mods/task_standalone_module.py +4999 -0
  305. pandaserver/taskbuffer/db_proxy_mods/task_utils_module.py +2063 -0
  306. pandaserver/taskbuffer/db_proxy_mods/worker_module.py +1944 -0
  307. pandaserver/taskbuffer/db_proxy_mods/workflow_module.py +832 -0
  308. pandaserver/taskbuffer/retryModule.py +552 -0
  309. pandaserver/taskbuffer/task_split_rules.py +308 -0
  310. pandaserver/taskbuffer/workflow_processor.py +249 -0
  311. pandaserver/taskbuffer/wrapped_oracle_conn.py +18 -0
  312. pandaserver/test/SchemaChecker.py +40 -0
  313. pandaserver/test/__init__.py +0 -0
  314. pandaserver/test/banUser.py +43 -0
  315. pandaserver/test/boostPrio.py +19 -0
  316. pandaserver/test/boostUser.py +35 -0
  317. pandaserver/test/callbackDDM.py +11 -0
  318. pandaserver/test/daod_on_demand.py +95 -0
  319. pandaserver/test/finishJob.py +184 -0
  320. pandaserver/test/finishTaskJEDI.py +11 -0
  321. pandaserver/test/getJobs.py +53 -0
  322. pandaserver/test/killJob.py +79 -0
  323. pandaserver/test/killJobLowPrio.py +99 -0
  324. pandaserver/test/killJobsInTask.py +56 -0
  325. pandaserver/test/killTask.py +80 -0
  326. pandaserver/test/killTaskJEDI.py +11 -0
  327. pandaserver/test/killUser.py +83 -0
  328. pandaserver/test/lsst/README.txt +7 -0
  329. pandaserver/test/lsst/lsstSubmit.py +139 -0
  330. pandaserver/test/lsst/lsstSubmitMERGEtest.py +141 -0
  331. pandaserver/test/lsst/lsstSubmitPhosim332.py +53 -0
  332. pandaserver/test/lsst/prepare-client-tarball-from-bigpanda-server.sh +41 -0
  333. pandaserver/test/reassignJobs.py +17 -0
  334. pandaserver/test/reassignSite.py +82 -0
  335. pandaserver/test/reassignTask.py +117 -0
  336. pandaserver/test/reassignWaiting.py +39 -0
  337. pandaserver/test/recoverLostFiles.py +7 -0
  338. pandaserver/test/reloadInputDS.py +51 -0
  339. pandaserver/test/sendCommandToJob.py +26 -0
  340. pandaserver/test/setDebugMode.py +38 -0
  341. pandaserver/test/setPriority.py +28 -0
  342. pandaserver/test/testEvgen.py +60 -0
  343. pandaserver/test/testEvgen17.py +58 -0
  344. pandaserver/test/testG4sim.py +81 -0
  345. pandaserver/test/testG4sim17.py +90 -0
  346. pandaserver/test/testGlobalShares.py +131 -0
  347. pandaserver/test/testReco.py +103 -0
  348. pandaserver/test/testSimulReco14.py +98 -0
  349. pandaserver/test/testSiteMap.py +26 -0
  350. pandaserver/test/testUpdateWorkerPilotStatus.py +9 -0
  351. pandaserver/userinterface/Client.py +1686 -0
  352. pandaserver/userinterface/UserIF.py +1799 -0
  353. pandaserver/userinterface/__init__.py +0 -0
  354. pandaserver/workflow/__init__.py +0 -0
  355. pandaserver/workflow/data_handler_plugins/base_data_handler.py +48 -0
  356. pandaserver/workflow/data_handler_plugins/ddm_collection_data_handler.py +100 -0
  357. pandaserver/workflow/data_handler_plugins/panda_task_data_handler.py +140 -0
  358. pandaserver/workflow/pcwl_test.py +122 -0
  359. pandaserver/workflow/pcwl_utils.py +384 -0
  360. pandaserver/workflow/psnakemake_container.json +68 -0
  361. pandaserver/workflow/psnakemake_task.json +62 -0
  362. pandaserver/workflow/psnakemake_test.py +60 -0
  363. pandaserver/workflow/snakeparser/__init__.py +3 -0
  364. pandaserver/workflow/snakeparser/extensions.py +74 -0
  365. pandaserver/workflow/snakeparser/log.py +53 -0
  366. pandaserver/workflow/snakeparser/names.py +3 -0
  367. pandaserver/workflow/snakeparser/parser.py +332 -0
  368. pandaserver/workflow/snakeparser/utils.py +29 -0
  369. pandaserver/workflow/step_handler_plugins/base_step_handler.py +85 -0
  370. pandaserver/workflow/step_handler_plugins/panda_task_step_handler.py +280 -0
  371. pandaserver/workflow/workflow_base.py +531 -0
  372. pandaserver/workflow/workflow_core.py +1974 -0
  373. pandaserver/workflow/workflow_core_smoketest.py +388 -0
  374. pandaserver/workflow/workflow_parser.py +222 -0
  375. pandaserver/workflow/workflow_utils.py +1047 -0
@@ -0,0 +1,1687 @@
1
+ import enum
2
+ import json
3
+ import math
4
+ import re
5
+
6
+ from pandaserver.taskbuffer import task_split_rules
7
+
8
+ """
9
+ task specification for JEDI
10
+
11
+ """
12
+
13
+
14
+ class JediTaskSpec(object):
15
+ # attributes
16
+ attributes = (
17
+ "jediTaskID",
18
+ "taskName",
19
+ "status",
20
+ "userName",
21
+ "creationDate",
22
+ "modificationTime",
23
+ "startTime",
24
+ "endTime",
25
+ "frozenTime",
26
+ "prodSourceLabel",
27
+ "workingGroup",
28
+ "vo",
29
+ "coreCount",
30
+ "taskType",
31
+ "processingType",
32
+ "taskPriority",
33
+ "currentPriority",
34
+ "architecture",
35
+ "transUses",
36
+ "transHome",
37
+ "transPath",
38
+ "lockedBy",
39
+ "lockedTime",
40
+ "termCondition",
41
+ "splitRule",
42
+ "walltime",
43
+ "walltimeUnit",
44
+ "outDiskCount",
45
+ "outDiskUnit",
46
+ "workDiskCount",
47
+ "workDiskUnit",
48
+ "ramCount",
49
+ "ramUnit",
50
+ "ioIntensity",
51
+ "ioIntensityUnit",
52
+ "workQueue_ID",
53
+ "progress",
54
+ "failureRate",
55
+ "errorDialog",
56
+ "reqID",
57
+ "oldStatus",
58
+ "cloud",
59
+ "site",
60
+ "countryGroup",
61
+ "parent_tid",
62
+ "eventService",
63
+ "ticketID",
64
+ "ticketSystemType",
65
+ "stateChangeTime",
66
+ "superStatus",
67
+ "campaign",
68
+ "mergeRamCount",
69
+ "mergeRamUnit",
70
+ "mergeWalltime",
71
+ "mergeWalltimeUnit",
72
+ "throttledTime",
73
+ "numThrottled",
74
+ "mergeCoreCount",
75
+ "goal",
76
+ "assessmentTime",
77
+ "cpuTime",
78
+ "cpuTimeUnit",
79
+ "cpuEfficiency",
80
+ "baseWalltime",
81
+ "nucleus",
82
+ "baseRamCount",
83
+ "ttcRequested",
84
+ "ttcPredicted",
85
+ "ttcPredictionDate",
86
+ "rescueTime",
87
+ "requestType",
88
+ "gshare",
89
+ "resource_type",
90
+ "useJumbo",
91
+ "diskIO",
92
+ "diskIOUnit",
93
+ "memory_leak_core",
94
+ "memory_leak_x2",
95
+ "attemptNr",
96
+ "container_name",
97
+ "framework",
98
+ "activatedTime",
99
+ "queuedTime",
100
+ "actionTime",
101
+ )
102
+ # attributes which have 0 by default
103
+ _zeroAttrs = ()
104
+ # attributes to force update
105
+ _forceUpdateAttrs = ("lockedBy", "lockedTime")
106
+ # mapping between sequence and attr
107
+ _seqAttrMap = {}
108
+ # limit length
109
+ _limitLength = {"errorDialog": 510}
110
+ # attribute length
111
+ _attrLength = {"workingGroup": 32}
112
+
113
+ # tokens for split rule
114
+ splitRuleToken = task_split_rules.split_rule_dict
115
+
116
+ # enum for preprocessing
117
+ enum_toPreProcess = "1"
118
+ enum_preProcessed = "2"
119
+ enum_postPProcess = "3"
120
+ # enum for limited sites
121
+ enum_limitedSites = {"1": "inc", "2": "exc", "3": "incexc"}
122
+ # enum for scout
123
+ enum_noScout = "1"
124
+ enum_useScout = "2"
125
+ enum_postScout = "3"
126
+ # enum for dataset registration
127
+ enum_toRegisterDS = "1"
128
+ enum_registeredDS = "2"
129
+ # enum for IP connectivity
130
+ enum_ipConnectivity = {"1": "full", "2": "http"}
131
+ # enum for IP stack
132
+ enum_ipStack = {"4": "IPv4", "6": "IPv6"}
133
+ # enum for alternative stage-out
134
+ enum_altStageOut = {"1": "on", "2": "off", "3": "force"}
135
+ # enum for local direct access
136
+ enum_inputLAN = {"1": "use", "2": "only"}
137
+ # world cloud name
138
+ worldCloudName = "WORLD"
139
+
140
+ # enum for contents feeder
141
+ class FirstContentsFeed(enum.Enum):
142
+ TRUE = "1"
143
+ FALSE = "0"
144
+
145
+ # enum for useJumbo
146
+ enum_useJumbo = {"waiting": "W", "running": "R", "pending": "P", "lack": "L", "disabled": "D"}
147
+
148
+ # enum for input prestaging
149
+ enum_inputPreStaging = {"use": "1", "notUse": "0"}
150
+
151
+ # enum for full chain
152
+ class FullChain(str, enum.Enum):
153
+ Only = "1"
154
+ Require = "2"
155
+ Capable = "3"
156
+
157
+ # enum for order input by
158
+ class OrderInputBy(str, enum.Enum):
159
+ eventsAlignment = "1"
160
+
161
+ # constructor
162
+ def __init__(self):
163
+ # install attributes
164
+ for attr in self.attributes:
165
+ if attr in self._zeroAttrs:
166
+ object.__setattr__(self, attr, 0)
167
+ else:
168
+ object.__setattr__(self, attr, None)
169
+ # map of changed attributes
170
+ object.__setattr__(self, "_changedAttrs", {})
171
+ # template to generate job parameters
172
+ object.__setattr__(self, "jobParamsTemplate", "")
173
+ # associated datasets
174
+ object.__setattr__(self, "datasetSpecList", [])
175
+ # original error dialog
176
+ object.__setattr__(self, "origErrorDialog", None)
177
+ # original user name
178
+ object.__setattr__(self, "origUserName", None)
179
+
180
+ # override __setattr__ to collect the changed attributes
181
+ def __setattr__(self, name, value):
182
+ oldVal = getattr(self, name)
183
+ if name in self._limitLength and value is not None:
184
+ # keep original dialog
185
+ if name == "errorDialog":
186
+ object.__setattr__(self, "origErrorDialog", value)
187
+ value = value[: self._limitLength[name]]
188
+ object.__setattr__(self, name, value)
189
+ newVal = getattr(self, name)
190
+ # collect changed attributes
191
+ if oldVal != newVal or name in self._forceUpdateAttrs:
192
+ self._changedAttrs[name] = value
193
+
194
+ # copy old attributes
195
+ def copyAttributes(self, oldTaskSpec):
196
+ for attr in self.attributes + ("jobParamsTemplate",):
197
+ if "Time" in attr:
198
+ continue
199
+ if "Date" in attr:
200
+ continue
201
+ if attr in ["progress", "failureRate", "errorDialog", "status", "oldStatus", "lockedBy"]:
202
+ continue
203
+ self.__setattr__(attr, getattr(oldTaskSpec, attr))
204
+
205
+ # reset changed attribute list
206
+ def resetChangedList(self):
207
+ object.__setattr__(self, "_changedAttrs", {})
208
+
209
+ # reset changed attribute
210
+ def resetChangedAttr(self, name):
211
+ try:
212
+ del self._changedAttrs[name]
213
+ except Exception:
214
+ pass
215
+
216
+ # reserve old attributes
217
+ def reserve_old_attributes(self):
218
+ for attName in ["ramCount", "walltime", "cpuTime", "startTime", "cpuTimeUnit", "outDiskCount", "workDiskCount", "ioIntensity", "diskIO"]:
219
+ self.resetChangedAttr(attName)
220
+
221
+ # force update
222
+ def forceUpdate(self, name):
223
+ if name in self.attributes:
224
+ self._changedAttrs[name] = getattr(self, name)
225
+
226
+ # return map of values
227
+ def valuesMap(self, useSeq=False, onlyChanged=False):
228
+ ret = {}
229
+ for attr in self.attributes:
230
+ # use sequence
231
+ if useSeq and attr in self._seqAttrMap:
232
+ continue
233
+ # only changed attributes
234
+ if onlyChanged:
235
+ if attr not in self._changedAttrs:
236
+ continue
237
+ val = getattr(self, attr)
238
+ if val is None:
239
+ if attr in self._zeroAttrs:
240
+ val = 0
241
+ else:
242
+ val = None
243
+ # truncate too long values
244
+ if attr in self._limitLength:
245
+ if val is not None:
246
+ val = val[: self._limitLength[attr]]
247
+ ret[f":{attr}"] = val
248
+ return ret
249
+
250
+ # pack tuple into TaskSpec
251
+ def pack(self, values):
252
+ for i in range(len(self.attributes)):
253
+ attr = self.attributes[i]
254
+ val = values[i]
255
+ object.__setattr__(self, attr, val)
256
+
257
+ # return column names for INSERT
258
+ def columnNames(cls, prefix=None):
259
+ ret = ""
260
+ for attr in cls.attributes:
261
+ if prefix is not None:
262
+ ret += f"{prefix}."
263
+ ret += f"{attr},"
264
+ ret = ret[:-1]
265
+ return ret
266
+
267
+ columnNames = classmethod(columnNames)
268
+
269
+ # return expression of bind variables for INSERT
270
+ def bindValuesExpression(cls, useSeq=True):
271
+ ret = "VALUES("
272
+ for attr in cls.attributes:
273
+ if useSeq and attr in cls._seqAttrMap:
274
+ ret += f"{cls._seqAttrMap[attr]},"
275
+ else:
276
+ ret += f":{attr},"
277
+ ret = ret[:-1]
278
+ ret += ")"
279
+ return ret
280
+
281
+ bindValuesExpression = classmethod(bindValuesExpression)
282
+
283
+ # return an expression of bind variables for UPDATE to update only changed attributes
284
+ def bindUpdateChangesExpression(self):
285
+ ret = ""
286
+ for attr in self.attributes:
287
+ if attr in self._changedAttrs:
288
+ ret += f"{attr}=:{attr},"
289
+ ret = ret[:-1]
290
+ ret += " "
291
+ return ret
292
+
293
+ # check split rule
294
+ def check_split_rule(self, key):
295
+ if self.splitRule is not None:
296
+ if re.search(self.splitRuleToken[key] + r"=(\d+)", self.splitRule):
297
+ return True
298
+ return False
299
+
300
+ # get the max size per job if defined
301
+ def getMaxSizePerJob(self):
302
+ if self.splitRule is not None:
303
+ tmpMatch = re.search(self.splitRuleToken["nGBPerJob"] + "=(\d+)", self.splitRule)
304
+ if tmpMatch is not None:
305
+ nGBPerJob = int(tmpMatch.group(1)) * 1024 * 1024 * 1024
306
+ return nGBPerJob
307
+ return None
308
+
309
+ # remove nGBPerJob
310
+ def removeMaxSizePerJob(self):
311
+ self.removeSplitRule(self.splitRuleToken["nGBPerJob"])
312
+
313
+ # get the max size per merge job if defined
314
+ def getMaxSizePerMergeJob(self):
315
+ if self.splitRule is not None:
316
+ tmpMatch = re.search(self.splitRuleToken["nGBPerMergeJob"] + "=(\d+)", self.splitRule)
317
+ if tmpMatch is not None:
318
+ nGBPerJob = int(tmpMatch.group(1)) * 1024 * 1024 * 1024
319
+ return nGBPerJob
320
+ return None
321
+
322
+ # get the maxnumber of files per job if defined
323
+ def getMaxNumFilesPerJob(self):
324
+ if self.splitRule is not None:
325
+ tmpMatch = re.search(self.splitRuleToken["nMaxFilesPerJob"] + "=(\d+)", self.splitRule)
326
+ if tmpMatch is not None:
327
+ return int(tmpMatch.group(1))
328
+ return None
329
+
330
+ # set MaxNumFilesPerJob
331
+ def setMaxNumFilesPerJob(self, value):
332
+ self.setSplitRule("nMaxFilesPerJob", value)
333
+
334
+ # get the maxnumber of files per merge job if defined
335
+ def getMaxNumFilesPerMergeJob(self):
336
+ if self.splitRule is not None:
337
+ tmpMatch = re.search(self.splitRuleToken["nMaxFilesPerMergeJob"] + "=(\d+)", self.splitRule)
338
+ if tmpMatch is not None:
339
+ return int(tmpMatch.group(1))
340
+ return 50
341
+
342
+ # get the number of events per merge job if defined
343
+ def getNumEventsPerMergeJob(self):
344
+ if self.splitRule is not None:
345
+ tmpMatch = re.search(self.splitRuleToken["nEventsPerMergeJob"] + "=(\d+)", self.splitRule)
346
+ if tmpMatch is not None:
347
+ return int(tmpMatch.group(1))
348
+ return None
349
+
350
+ # check if using jumbo
351
+ def usingJumboJobs(self):
352
+ if self.useJumbo in self.enum_useJumbo.values() and self.useJumbo != self.enum_useJumbo["disabled"]:
353
+ return True
354
+ return False
355
+
356
+ # get the number of jumbo jobs if defined
357
+ def getNumJumboJobs(self):
358
+ if self.usingJumboJobs() and self.splitRule is not None:
359
+ tmpMatch = re.search(self.splitRuleToken["nJumboJobs"] + "=(\d+)", self.splitRule)
360
+ if tmpMatch is not None:
361
+ return int(tmpMatch.group(1))
362
+ return None
363
+
364
+ # get the max number of jumbo jobs per site if defined
365
+ def getMaxJumboPerSite(self):
366
+ if self.usingJumboJobs() and self.splitRule is not None:
367
+ tmpMatch = re.search(self.splitRuleToken["maxJumboPerSite"] + "=(\d+)", self.splitRule)
368
+ if tmpMatch is not None:
369
+ return int(tmpMatch.group(1))
370
+ return 1
371
+
372
+ # get the number of sites per job
373
+ def getNumSitesPerJob(self):
374
+ if not self.useEventService():
375
+ return 1
376
+ if self.splitRule is not None:
377
+ tmpMatch = re.search(self.splitRuleToken["nSitesPerJob"] + "=(\d+)", self.splitRule)
378
+ if tmpMatch is not None:
379
+ return int(tmpMatch.group(1))
380
+ return 1
381
+
382
+ # get the number of files per job if defined
383
+ def getNumFilesPerJob(self):
384
+ if self.splitRule is not None:
385
+ tmpMatch = re.search(self.splitRuleToken["nFilesPerJob"] + "=(\d+)", self.splitRule)
386
+ if tmpMatch is not None:
387
+ n = int(tmpMatch.group(1))
388
+ if self.dynamicNumEvents():
389
+ inn = self.get_num_events_per_input()
390
+ dyn = self.get_min_granularity()
391
+ if inn and dyn and inn > dyn:
392
+ n *= inn // dyn
393
+ return n
394
+ return None
395
+
396
+ # remove nFilesPerJob
397
+ def removeNumFilesPerJob(self):
398
+ self.removeSplitRule(self.splitRuleToken["nFilesPerJob"])
399
+
400
+ # get the number of files per merge job if defined
401
+ def getNumFilesPerMergeJob(self):
402
+ if self.splitRule is not None:
403
+ tmpMatch = re.search(self.splitRuleToken["nFilesPerMergeJob"] + "=(\d+)", self.splitRule)
404
+ if tmpMatch is not None:
405
+ return int(tmpMatch.group(1))
406
+ return None
407
+
408
+ # get the number of events per job if defined
409
+ def getNumEventsPerJob(self):
410
+ if self.splitRule is not None:
411
+ tmpMatch = re.search(self.splitRuleToken["nEventsPerJob"] + "=(\d+)", self.splitRule)
412
+ if tmpMatch is not None:
413
+ return int(tmpMatch.group(1))
414
+ return None
415
+
416
+ # get offset for random seed
417
+ def getRndmSeedOffset(self):
418
+ if self.splitRule is not None:
419
+ tmpMatch = re.search(self.splitRuleToken["randomSeed"] + "=(\d+)", self.splitRule)
420
+ if tmpMatch is not None:
421
+ return int(tmpMatch.group(1))
422
+ return 0
423
+
424
+ # get offset for first event
425
+ def getFirstEventOffset(self):
426
+ if self.splitRule is not None:
427
+ tmpMatch = re.search(self.splitRuleToken["firstEvent"] + "=(\d+)", self.splitRule)
428
+ if tmpMatch is not None:
429
+ return int(tmpMatch.group(1))
430
+ return 0
431
+
432
+ # grouping with boundaryID
433
+ def useGroupWithBoundaryID(self):
434
+ if self.splitRule is not None:
435
+ tmpMatch = re.search(self.splitRuleToken["groupBoundaryID"] + "=(\d+)", self.splitRule)
436
+ if tmpMatch is not None:
437
+ gbID = int(tmpMatch.group(1))
438
+ # 1 : input - can split, output - free
439
+ # 2 : input - can split, output - mapped with provenanceID
440
+ # 3 : input - cannot split, output - free
441
+ # 4 : input - cannot split, output - mapped with provenanceID
442
+ #
443
+ # * rule for master
444
+ # 1 : can split. one boundayID per sub chunk
445
+ # 2 : cannot split. one boundayID per sub chunk
446
+ # 3 : cannot split. multiple boundayIDs per sub chunk
447
+ #
448
+ # * rule for secodary
449
+ # 1 : must have same boundayID. cannot split
450
+ #
451
+ retMap = {}
452
+ if gbID in [1, 2]:
453
+ retMap["inSplit"] = 1
454
+ else:
455
+ retMap["inSplit"] = 2
456
+ if gbID in [1, 3]:
457
+ retMap["outMap"] = False
458
+ else:
459
+ retMap["outMap"] = True
460
+ retMap["secSplit"] = None
461
+ return retMap
462
+ return None
463
+
464
+ # use build
465
+ def useBuild(self):
466
+ return self.check_split_rule("useBuild")
467
+
468
+ # use sjob cloning
469
+ def useJobCloning(self):
470
+ return self.check_split_rule("useJobCloning")
471
+
472
+ # get job cloning type
473
+ def getJobCloningType(self):
474
+ if self.splitRule is not None:
475
+ tmpMatch = re.search(self.splitRuleToken["useJobCloning"] + "=(\d+)", self.splitRule)
476
+ if tmpMatch is not None:
477
+ return tmpMatch.group(1)
478
+ return ""
479
+
480
+ # reuse secondary on demand
481
+ def reuseSecOnDemand(self):
482
+ return self.check_split_rule("reuseSecOnDemand")
483
+
484
+ # not wait for completion of parent
485
+ def noWaitParent(self):
486
+ return self.check_split_rule("noWaitParent")
487
+
488
+ # check splitRule if not wait for completion of parent
489
+ def noWaitParentSL(cls, splitRule):
490
+ if splitRule is not None:
491
+ tmpMatch = re.search(cls.splitRuleToken["noWaitParent"] + "=(\d+)", splitRule)
492
+ if tmpMatch is not None:
493
+ return True
494
+ return False
495
+
496
+ noWaitParentSL = classmethod(noWaitParentSL)
497
+
498
+ # use only limited sites
499
+ def useLimitedSites(self):
500
+ return self.check_split_rule("limitedSites")
501
+
502
+ # set limited sites
503
+ def setLimitedSites(self, policy):
504
+ tag = None
505
+ for tmpIdx, tmpPolicy in self.enum_limitedSites.items():
506
+ if policy == tmpPolicy:
507
+ tag = tmpIdx
508
+ break
509
+ # not found
510
+ if tag is None:
511
+ return
512
+ # set
513
+ if self.splitRule is None:
514
+ # new
515
+ self.splitRule = self.splitRuleToken["limitedSites"] + "=" + tag
516
+ else:
517
+ tmpMatch = re.search(self.splitRuleToken["limitedSites"] + "=(\d+)", self.splitRule)
518
+ if tmpMatch is None:
519
+ # append
520
+ self.splitRule += "," + self.splitRuleToken["limitedSites"] + "=" + tag
521
+ else:
522
+ # replace
523
+ self.splitRule = re.sub(self.splitRuleToken["limitedSites"] + "=(\d+)", self.splitRuleToken["limitedSites"] + "=" + tag, self.splitRule)
524
+
525
+ # use local IO
526
+ def useLocalIO(self):
527
+ if self.splitRule is not None:
528
+ tmpMatch = re.search(self.splitRuleToken["useLocalIO"] + "=(\d+)", self.splitRule)
529
+ if tmpMatch is not None and int(tmpMatch.group(1)):
530
+ return True
531
+ return False
532
+
533
+ # use Event Service
534
+ def useEventService(self, siteSpec=None):
535
+ if self.eventService in [1, 2]:
536
+ # check site if ES is disabled
537
+ if self.switchEStoNormal() and siteSpec is not None and siteSpec.getJobSeed() in ["all"]:
538
+ return False
539
+ return True
540
+ return False
541
+
542
+ # get the number of events per worker for Event Service
543
+ def getNumEventsPerWorker(self):
544
+ if self.splitRule is not None:
545
+ tmpMatch = re.search(self.splitRuleToken["nEventsPerWorker"] + "=(\d+)", self.splitRule)
546
+ if tmpMatch is not None:
547
+ return int(tmpMatch.group(1))
548
+ return None
549
+
550
+ # get the number of event service consumers
551
+ def getNumEventServiceConsumer(self):
552
+ if not self.useEventService():
553
+ return None
554
+ if self.splitRule is not None:
555
+ tmpMatch = re.search(self.splitRuleToken["nEsConsumers"] + "=(\d+)", self.splitRule)
556
+ if tmpMatch is not None:
557
+ return int(tmpMatch.group(1))
558
+ return None
559
+
560
+ # disable automatic retry
561
+ def disableAutoRetry(self):
562
+ return self.check_split_rule("disableAutoRetry")
563
+
564
+ # disable reassign
565
+ def disableReassign(self):
566
+ return self.check_split_rule("disableReassign")
567
+
568
+ # allow empty input
569
+ def allowEmptyInput(self):
570
+ return self.check_split_rule("allowEmptyInput")
571
+
572
+ # use PFN list
573
+ def useListPFN(self):
574
+ return self.check_split_rule("pfnList")
575
+
576
+ # set preprocessing
577
+ def setPrePro(self):
578
+ if self.splitRule is None:
579
+ # new
580
+ self.splitRule = self.splitRuleToken["usePrePro"] + "=" + self.enum_toPreProcess
581
+ else:
582
+ # append
583
+ self.splitRule += "," + self.splitRuleToken["usePrePro"] + "=" + self.enum_toPreProcess
584
+
585
+ # use preprocessing
586
+ def usePrePro(self):
587
+ if self.splitRule is not None:
588
+ tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
589
+ if tmpMatch is not None and tmpMatch.group(1) == self.enum_toPreProcess:
590
+ return True
591
+ return False
592
+
593
+ # set preprocessed
594
+ def setPreProcessed(self):
595
+ if self.splitRule is None:
596
+ # new
597
+ self.splitRule = self.splitRuleToken["usePrePro"] + "=" + self.enum_preProcessed
598
+ else:
599
+ tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
600
+ if tmpMatch is None:
601
+ # append
602
+ self.splitRule += "," + self.splitRuleToken["usePrePro"] + "=" + self.enum_preProcessed
603
+ else:
604
+ # replace
605
+ self.splitRule = re.sub(
606
+ self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRuleToken["usePrePro"] + "=" + self.enum_preProcessed, self.splitRule
607
+ )
608
+ return
609
+
610
+ # check preprocessed
611
+ def checkPreProcessed(self):
612
+ if self.splitRule is not None:
613
+ tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
614
+ if tmpMatch is not None and tmpMatch.group(1) == self.enum_preProcessed:
615
+ return True
616
+ return False
617
+
618
+ # set post preprocess
619
+ def setPostPreProcess(self):
620
+ if self.splitRule is None:
621
+ # new
622
+ self.splitRule = self.splitRuleToken["usePrePro"] + "=" + self.enum_postPProcess
623
+ else:
624
+ tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
625
+ if tmpMatch is None:
626
+ # append
627
+ self.splitRule += "," + self.splitRuleToken["usePrePro"] + "=" + self.enum_postPProcess
628
+ else:
629
+ # replace
630
+ self.splitRule = re.sub(
631
+ self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRuleToken["usePrePro"] + "=" + self.enum_postPProcess, self.splitRule
632
+ )
633
+ return
634
+
635
+ # instantiate template datasets
636
+ def instantiateTmpl(self):
637
+ return self.check_split_rule("instantiateTmpl")
638
+
639
+ # instantiate template datasets at site
640
+ def instantiateTmplSite(self):
641
+ return self.check_split_rule("instantiateTmplSite")
642
+
643
+ # merge output
644
+ def mergeOutput(self):
645
+ return self.check_split_rule("mergeOutput")
646
+
647
+ # use random seed
648
+ def useRandomSeed(self):
649
+ return self.check_split_rule("randomSeed")
650
+
651
+ # get the size of workDisk in bytes
652
+ def getWorkDiskSize(self):
653
+ safetyMargin = 300 * 1024 * 1024
654
+ tmpSize = self.workDiskCount
655
+ if tmpSize is None:
656
+ return 0
657
+ if self.workDiskUnit == "GB":
658
+ tmpSize = tmpSize * 1024 * 1024 * 1024
659
+ elif self.workDiskUnit == "MB":
660
+ tmpSize = tmpSize * 1024 * 1024
661
+ elif self.workDiskUnit == "kB":
662
+ tmpSize = tmpSize * 1024
663
+ tmpSize += safetyMargin
664
+ return tmpSize
665
+
666
+ # get the size of outDisk in bytes
667
+ def getOutDiskSize(self):
668
+ tmpSize = self.outDiskCount
669
+ if tmpSize is None or tmpSize < 0:
670
+ return 0
671
+ if self.outDiskUnit is not None:
672
+ if self.outDiskUnit.startswith("GB"):
673
+ tmpSize = tmpSize * 1024 * 1024 * 1024
674
+ elif self.outDiskUnit.startswith("MB"):
675
+ tmpSize = tmpSize * 1024 * 1024
676
+ elif self.outDiskUnit.startswith("kB"):
677
+ tmpSize = tmpSize * 1024
678
+ return tmpSize
679
+
680
+ # output scales with the number of events
681
+ def outputScaleWithEvents(self):
682
+ if self.outDiskUnit is not None and "PerEvent" in self.outDiskUnit:
683
+ return True
684
+ return False
685
+
686
+ # return list of status to update contents
687
+ def statusToUpdateContents(cls):
688
+ return ["defined"]
689
+
690
+ statusToUpdateContents = classmethod(statusToUpdateContents)
691
+
692
+ # set task status on hold
693
+ def setOnHold(self):
694
+ # change status
695
+ if self.status in ["ready", "running", "merging", "scouting", "defined", "topreprocess", "preprocessing", "registered", "prepared", "rerefine"]:
696
+ self.oldStatus = self.status
697
+ self.status = "pending"
698
+
699
+ # return list of status to reject external changes
700
+ def statusToRejectExtChange(cls):
701
+ return ["finished", "done", "prepared", "broken", "tobroken", "aborted", "toabort", "aborting", "failed", "passed"]
702
+
703
+ statusToRejectExtChange = classmethod(statusToRejectExtChange)
704
+
705
+ # return list of status for retry
706
+ def statusToRetry(cls):
707
+ return ["finished", "failed", "aborted", "exhausted"]
708
+
709
+ statusToRetry = classmethod(statusToRetry)
710
+
711
+ # return list of status for incexec
712
+ def statusToIncexec(cls):
713
+ return ["done"] + cls.statusToRetry()
714
+
715
+ statusToIncexec = classmethod(statusToIncexec)
716
+
717
+ # return list of status for reassign
718
+ def statusToReassign(cls):
719
+ return ["registered", "defined", "ready", "running", "scouting", "scouted", "pending", "assigning", "exhausted"]
720
+
721
+ statusToReassign = classmethod(statusToReassign)
722
+
723
+ # return list of status for Job Generator
724
+ def statusForJobGenerator(cls):
725
+ return ["ready", "running", "scouting", "topreprocess", "preprocessing"]
726
+
727
+ statusForJobGenerator = classmethod(statusForJobGenerator)
728
+
729
+ # return list of status to not pause
730
+ def statusNotToPause(cls):
731
+ return ["finished", "failed", "done", "aborted", "broken", "paused"]
732
+
733
+ statusNotToPause = classmethod(statusNotToPause)
734
+
735
+ # return mapping of command and status
736
+ def commandStatusMap(cls):
737
+ return {
738
+ "kill": {"doing": "aborting", "done": "toabort"},
739
+ "finish": {"doing": "finishing", "done": "passed"},
740
+ "retry": {"doing": "toretry", "done": "ready"},
741
+ "incexec": {"doing": "toincexec", "done": "rerefine"},
742
+ "reassign": {"doing": "toreassign", "done": "reassigning"},
743
+ "pause": {"doing": "paused", "done": "dummy"},
744
+ "resume": {"doing": "dummy", "done": "dummy"},
745
+ "avalanche": {"doing": "dummy", "done": "dummy"},
746
+ "release": {"doing": "dummy", "done": "dummy"},
747
+ }
748
+
749
+ commandStatusMap = classmethod(commandStatusMap)
750
+
751
+ # qualifiers for retry command
752
+ def get_retry_command_qualifiers(
753
+ cls,
754
+ no_child_retry: bool = False,
755
+ discard_events: bool = False,
756
+ disable_staging_mode: bool = False,
757
+ keep_gshare_priority: bool = False,
758
+ ignore_hard_exhausted: bool = False,
759
+ ) -> list:
760
+ """
761
+ Get the list of qualifiers for the retry command.
762
+ :param no_child_retry: If True, retry will not be attempted on child tasks.
763
+ :param discard_events: If True, events will be discarded.
764
+ :param disable_staging_mode: If True, staging mode will be disabled.
765
+ :param keep_gshare_priority: If True, current gshare and priority will be kept.
766
+ :param ignore_hard_exhausted: If True, the limits for hard exhausted will be ignored.
767
+ :return: A list of qualifiers.
768
+ """
769
+ qualifiers = []
770
+ if no_child_retry:
771
+ qualifiers.append("sole")
772
+ if discard_events:
773
+ qualifiers.append("discard")
774
+ if disable_staging_mode:
775
+ qualifiers.append("staged")
776
+ if keep_gshare_priority:
777
+ qualifiers.append("keep")
778
+ if ignore_hard_exhausted:
779
+ qualifiers.append("transcend")
780
+ return qualifiers
781
+
782
+ get_retry_command_qualifiers = classmethod(get_retry_command_qualifiers)
783
+
784
+ # set error dialog
785
+ def setErrDiag(self, diag, append=False, prepend=False):
786
+ # check if message can be encoded with UTF-8
787
+ if diag:
788
+ try:
789
+ diag.encode()
790
+ except UnicodeEncodeError:
791
+ # remove non-ascii chars
792
+ diag = re.sub(r"[^\x00-\x7F]+", "<non-ASCII char>", diag)
793
+ # set error dialog
794
+ if append is True and self.errorDialog is not None:
795
+ if not prepend:
796
+ self.errorDialog = f"{self.errorDialog} {diag}"
797
+ else:
798
+ self.errorDialog = f"{diag} {self.errorDialog}"
799
+ elif append is None:
800
+ # keep old log
801
+ if self.errorDialog is None:
802
+ self.errorDialog = diag
803
+ else:
804
+ self.errorDialog = diag
805
+
806
+ # use loadXML
807
+ def useLoadXML(self):
808
+ return self.check_split_rule("loadXML")
809
+
810
+ # make VOMS FQANs
811
+ def makeFQANs(self):
812
+ # no working group
813
+ if self.workingGroup is not None:
814
+ fqan = f"/{self.vo}/{self.workingGroup}/Role=production"
815
+ else:
816
+ if self.vo is not None:
817
+ fqan = f"/{self.vo}/Role=NULL"
818
+ else:
819
+ return []
820
+ # return
821
+ return [fqan]
822
+
823
+ # set split rule
824
+ def setSplitRule(self, ruleName, ruleValue):
825
+ if self.splitRule is None:
826
+ # new
827
+ self.splitRule = self.splitRuleToken[ruleName] + "=" + ruleValue
828
+ else:
829
+ tmpMatch = re.search(self.splitRuleToken[ruleName] + "=(\d+)", self.splitRule)
830
+ if tmpMatch is None:
831
+ # append
832
+ self.splitRule += "," + self.splitRuleToken[ruleName] + "=" + ruleValue
833
+ else:
834
+ # replace
835
+ self.splitRule = re.sub(self.splitRuleToken[ruleName] + "=(\d+)", self.splitRuleToken[ruleName] + "=" + ruleValue, self.splitRule)
836
+
837
+ # remove split rule
838
+ def removeSplitRule(self, ruleName):
839
+ if self.splitRule is not None:
840
+ items = self.splitRule.split(",")
841
+ newItems = []
842
+ for item in items:
843
+ # remove rule
844
+ tmpRuleName = item.split("=")[0]
845
+ if ruleName == tmpRuleName:
846
+ continue
847
+ newItems.append(item)
848
+ self.splitRule = ",".join(newItems)
849
+
850
+ # set to use scout
851
+ def setUseScout(self, useFlag):
852
+ if useFlag:
853
+ self.setSplitRule("useScout", self.enum_useScout)
854
+ else:
855
+ self.setSplitRule("useScout", self.enum_noScout)
856
+
857
+ # set post scout
858
+ def setPostScout(self):
859
+ self.setSplitRule("useScout", self.enum_postScout)
860
+
861
+ # use scout
862
+ def useScout(self, splitRule=None):
863
+ if splitRule is None:
864
+ splitRule = self.splitRule
865
+ if splitRule is not None:
866
+ tmpMatch = re.search(self.splitRuleToken["useScout"] + "=(\d+)", splitRule)
867
+ if tmpMatch is not None and tmpMatch.group(1) == self.enum_useScout:
868
+ return True
869
+ return False
870
+
871
+ # use exhausted
872
+ def useExhausted(self):
873
+ return self.check_split_rule("useExhausted")
874
+
875
+ # use real number of events
876
+ def useRealNumEvents(self):
877
+ return self.check_split_rule("useRealNumEvents")
878
+
879
+ # use input LFN as source for output LFN
880
+ def useFileAsSourceLFN(self):
881
+ return self.check_split_rule("useFileAsSourceLFN")
882
+
883
+ # post scout
884
+ def isPostScout(self):
885
+ if self.splitRule is not None:
886
+ tmpMatch = re.search(self.splitRuleToken["useScout"] + "=(\d+)", self.splitRule)
887
+ if tmpMatch is not None and tmpMatch.group(1) == self.enum_postScout:
888
+ return True
889
+ return False
890
+
891
+ # wait until input shows up
892
+ def waitInput(self):
893
+ return self.check_split_rule("waitInput")
894
+
895
+ # input prestaging
896
+ def inputPreStaging(self):
897
+ if self.splitRule is not None:
898
+ tmpMatch = re.search(self.splitRuleToken["inputPreStaging"] + "=" + self.enum_inputPreStaging["use"], self.splitRule)
899
+ if tmpMatch is not None:
900
+ return True
901
+ return False
902
+
903
+ # set DDM backend
904
+ def setDdmBackEnd(self, backEnd):
905
+ if self.splitRule is None:
906
+ # new
907
+ self.splitRule = self.splitRuleToken["ddmBackEnd"] + "=" + backEnd
908
+ else:
909
+ tmpMatch = re.search(self.splitRuleToken["ddmBackEnd"] + "=([^,$]+)", self.splitRule)
910
+ if tmpMatch is None:
911
+ # append
912
+ self.splitRule += "," + self.splitRuleToken["ddmBackEnd"] + "=" + backEnd
913
+ else:
914
+ # replace
915
+ self.splitRule = re.sub(self.splitRuleToken["ddmBackEnd"] + "=([^,$]+)", self.splitRuleToken["ddmBackEnd"] + "=" + backEnd, self.splitRule)
916
+
917
+ # get DDM backend
918
+ def getDdmBackEnd(self):
919
+ if self.splitRule is not None:
920
+ tmpMatch = re.search(self.splitRuleToken["ddmBackEnd"] + "=([^,$]+)", self.splitRule)
921
+ if tmpMatch is not None:
922
+ return tmpMatch.group(1)
923
+ return None
924
+
925
+ # get field number to add middle name to LFN
926
+ def getFieldNumToLFN(self):
927
+ try:
928
+ if self.splitRule is not None:
929
+ tmpMatch = re.search(self.splitRuleToken["addNthFieldToLFN"] + "=([,\d]+)", self.splitRule)
930
+ if tmpMatch is not None:
931
+ tmpList = tmpMatch.group(1).split(",")
932
+ try:
933
+ tmpList.remove("")
934
+ except Exception:
935
+ pass
936
+ return list(map(int, tmpList))
937
+ except Exception:
938
+ pass
939
+ return None
940
+
941
+ # get required success rate for scout jobs
942
+ def getScoutSuccessRate(self):
943
+ if self.splitRule is not None:
944
+ tmpMatch = re.search(self.splitRuleToken["scoutSuccessRate"] + "=(\d+)", self.splitRule)
945
+ if tmpMatch is not None:
946
+ return int(tmpMatch.group(1))
947
+ return None
948
+
949
+ # get T1 weight
950
+ def getT1Weight(self):
951
+ if self.splitRule is not None:
952
+ tmpMatch = re.search(self.splitRuleToken["t1Weight"] + "=(-*\d+)", self.splitRule)
953
+ if tmpMatch is not None:
954
+ return int(tmpMatch.group(1))
955
+ return 0
956
+
957
+ # respect Lumiblock boundaries
958
+ def respectLumiblock(self):
959
+ return self.check_split_rule("respectLB")
960
+
961
+ # release files per Lumiblock
962
+ def releasePerLumiblock(self):
963
+ return self.check_split_rule("releasePerLB")
964
+
965
+ # order by Lumiblock numbers
966
+ def orderByLB(self):
967
+ return self.check_split_rule("orderByLB")
968
+
969
+ # respect split rule
970
+ def respectSplitRule(self):
971
+ return self.check_split_rule("respectSplitRule")
972
+
973
+ # allow partial finish
974
+ def allowPartialFinish(self):
975
+ return self.check_split_rule("allowPartialFinish")
976
+
977
+ # check if datasets should be registered
978
+ def toRegisterDatasets(self):
979
+ if self.splitRule is not None:
980
+ tmpMatch = re.search(self.splitRuleToken["registerDatasets"] + "=(\d+)", self.splitRule)
981
+ if tmpMatch is not None and tmpMatch.group(1) == self.enum_toRegisterDS:
982
+ return True
983
+ return False
984
+
985
+ # datasets were registered
986
+ def registeredDatasets(self):
987
+ self.setSplitRule("registerDatasets", self.enum_registeredDS)
988
+
989
+ # set datasets to be registered
990
+ def setToRegisterDatasets(self):
991
+ self.setSplitRule("registerDatasets", self.enum_toRegisterDS)
992
+
993
+ # get the max number of attempts for ES events
994
+ def getMaxAttemptES(self):
995
+ if self.splitRule is not None:
996
+ tmpMatch = re.search(self.splitRuleToken["maxAttemptES"] + "=(\d+)", self.splitRule)
997
+ if tmpMatch is not None:
998
+ return int(tmpMatch.group(1))
999
+ return None
1000
+
1001
+ # get the max number of attempts for ES jobs
1002
+ def getMaxAttemptEsJob(self):
1003
+ if self.splitRule is not None:
1004
+ tmpMatch = re.search(self.splitRuleToken["maxAttemptEsJob"] + "=(\d+)", self.splitRule)
1005
+ if tmpMatch is not None:
1006
+ return int(tmpMatch.group(1))
1007
+ return self.getMaxAttemptES()
1008
+
1009
+ # check attribute length
1010
+ def checkAttrLength(self):
1011
+ for attrName, attrLength in self._attrLength.items():
1012
+ attrVal = getattr(self, attrName)
1013
+ if attrVal is None:
1014
+ continue
1015
+ if len(attrVal) > attrLength:
1016
+ setattr(self, attrName, None)
1017
+ self.errorDialog = f"{attrName} is too long (actual: {len(attrVal)}, maximum: {attrLength})"
1018
+ return False
1019
+ return True
1020
+
1021
+ # set IP connectivity and stack
1022
+ def setIpConnectivity(self, value):
1023
+ if not value:
1024
+ return
1025
+ values = value.split("#")
1026
+ if not values:
1027
+ return
1028
+ ipConnectivity = values[0]
1029
+ if ipConnectivity in self.enum_ipConnectivity.values():
1030
+ for tmpKey, tmpVal in self.enum_ipConnectivity.items():
1031
+ if ipConnectivity == tmpVal:
1032
+ self.setSplitRule("ipConnectivity", tmpKey)
1033
+ break
1034
+ if len(values) > 1:
1035
+ ipStack = values[1]
1036
+ if ipStack in self.enum_ipStack.values():
1037
+ for tmpKey, tmpVal in self.enum_ipStack.items():
1038
+ if ipStack == tmpVal:
1039
+ self.setSplitRule("ipStack", tmpKey)
1040
+ break
1041
+
1042
+ # get IP connectivity
1043
+ def getIpConnectivity(self):
1044
+ if self.splitRule is not None:
1045
+ tmpMatch = re.search(self.splitRuleToken["ipConnectivity"] + "=(\d+)", self.splitRule)
1046
+ if tmpMatch is not None:
1047
+ return self.enum_ipConnectivity[tmpMatch.group(1)]
1048
+ return None
1049
+
1050
+ # get IP connectivity
1051
+ def getIpStack(self):
1052
+ if self.splitRule is not None:
1053
+ tmpMatch = re.search(self.splitRuleToken["ipStack"] + "=(\d+)", self.splitRule)
1054
+ if tmpMatch is not None:
1055
+ return self.enum_ipStack[tmpMatch.group(1)]
1056
+ return None
1057
+
1058
+ # use HS06 for walltime estimation
1059
+ def useHS06(self):
1060
+ return self.cpuTimeUnit in ["HS06sPerEvent", "HS06sPerEventFixed", "mHS06sPerEvent", "mHS06sPerEventFixed"]
1061
+
1062
+ # get CPU time in sec
1063
+ def getCpuTime(self):
1064
+ if not self.useHS06():
1065
+ return None
1066
+ try:
1067
+ if self.cpuTimeUnit.startswith("m"):
1068
+ return float(self.cpuTime) / 1000.0
1069
+ except Exception:
1070
+ pass
1071
+ return self.cpuTime
1072
+
1073
+ # RAM scales with nCores
1074
+ def ramPerCore(self):
1075
+ return self.ramUnit in ["MBPerCore", "MBPerCoreFixed"]
1076
+
1077
+ # run until input is closed
1078
+ def runUntilClosed(self):
1079
+ return self.check_split_rule("runUntilClosed")
1080
+
1081
+ # stay output on site
1082
+ def stayOutputOnSite(self):
1083
+ return self.check_split_rule("stayOutputOnSite")
1084
+
1085
+ # fail when goal unreached
1086
+ def failGoalUnreached(self):
1087
+ return self.check_split_rule("failGoalUnreached")
1088
+
1089
+ # unset fail when goal unreached
1090
+ def unsetFailGoalUnreached(self):
1091
+ self.removeSplitRule(self.splitRuleToken["failGoalUnreached"])
1092
+
1093
+ # switch ES to normal when jobs land at normal sites
1094
+ def switchEStoNormal(self):
1095
+ return self.check_split_rule("switchEStoNormal")
1096
+
1097
+ # use world cloud
1098
+ def useWorldCloud(self):
1099
+ return self.cloud == self.worldCloudName
1100
+
1101
+ # dynamic number of events
1102
+ def dynamicNumEvents(self):
1103
+ if self.splitRule is not None:
1104
+ tmpMatch = re.search(self.splitRuleToken["dynamicNumEvents"] + "=(\d+)", self.splitRule)
1105
+ if tmpMatch is not None:
1106
+ return True
1107
+ return False
1108
+
1109
+ # get min granularity for dynamic number of events
1110
+ def get_min_granularity(self):
1111
+ if self.splitRule is not None:
1112
+ tmpMatch = re.search(self.splitRuleToken["dynamicNumEvents"] + "=(\d+)", self.splitRule)
1113
+ if tmpMatch is not None:
1114
+ return int(tmpMatch.group(1))
1115
+ return None
1116
+
1117
+ # set alternative stage-out
1118
+ def setAltStageOut(self, value):
1119
+ if value in self.enum_altStageOut.values():
1120
+ for tmpKey, tmpVal in self.enum_altStageOut.items():
1121
+ if value == tmpVal:
1122
+ self.setSplitRule("altStageOut", tmpKey)
1123
+ break
1124
+
1125
+ # get alternative stage-out
1126
+ def getAltStageOut(self):
1127
+ if self.splitRule is not None:
1128
+ tmpMatch = re.search(self.splitRuleToken["altStageOut"] + "=(\d+)", self.splitRule)
1129
+ if tmpMatch is not None:
1130
+ return self.enum_altStageOut[tmpMatch.group(1)]
1131
+ return None
1132
+
1133
+ # allow WAN for input access
1134
+ def allowInputWAN(self):
1135
+ return self.check_split_rule("allowInputWAN")
1136
+
1137
+ # set mode for input LAN access
1138
+ def setAllowInputLAN(self, value):
1139
+ if value in self.enum_inputLAN.values():
1140
+ for tmpKey, tmpVal in self.enum_inputLAN.items():
1141
+ if value == tmpVal:
1142
+ self.setSplitRule("allowInputLAN", tmpKey)
1143
+ break
1144
+
1145
+ # check if LAN is used for input access
1146
+ def allowInputLAN(self):
1147
+ if self.splitRule is not None:
1148
+ tmpMatch = re.search(self.splitRuleToken["allowInputLAN"] + "=(\d+)", self.splitRule)
1149
+ if tmpMatch is not None:
1150
+ return self.enum_inputLAN[tmpMatch.group(1)]
1151
+ return None
1152
+
1153
+ # put log files to OS
1154
+ def putLogToOS(self):
1155
+ return self.check_split_rule("putLogToOS")
1156
+
1157
+ # merge ES on Object Store
1158
+ def mergeEsOnOS(self):
1159
+ return self.check_split_rule("mergeEsOnOS")
1160
+
1161
+ # write input to file
1162
+ def writeInputToFile(self):
1163
+ return self.check_split_rule("writeInputToFile")
1164
+
1165
+ # ignore missing input datasets
1166
+ def ignoreMissingInDS(self):
1167
+ return self.check_split_rule("ignoreMissingInDS")
1168
+
1169
+ # suppress execute string conversion
1170
+ def noExecStrCnv(self):
1171
+ return self.check_split_rule("noExecStrCnv")
1172
+
1173
+ # in-file positional event number
1174
+ def inFilePosEvtNum(self):
1175
+ return self.check_split_rule("inFilePosEvtNum")
1176
+
1177
+ # register event service files
1178
+ def registerEsFiles(self):
1179
+ return self.check_split_rule("registerEsFiles")
1180
+
1181
+ # disable auto finish
1182
+ def disableAutoFinish(self):
1183
+ return self.check_split_rule("disableAutoFinish")
1184
+
1185
+ # reset refined attributes which may confuse they system
1186
+ def resetRefinedAttrs(self):
1187
+ self.resetChangedAttr("splitRule")
1188
+ self.resetChangedAttr("eventService")
1189
+ self.reserve_old_attributes()
1190
+
1191
+ # resurrect consumers
1192
+ def resurrectConsumers(self):
1193
+ return self.check_split_rule("resurrectConsumers")
1194
+
1195
+ # use prefetcher
1196
+ def usePrefetcher(self):
1197
+ return self.check_split_rule("usePrefetcher")
1198
+
1199
+ # no input pooling
1200
+ def noInputPooling(self):
1201
+ return self.check_split_rule("noInputPooling")
1202
+
1203
+ # get num of input chunks to wait
1204
+ def nChunksToWait(self):
1205
+ if self.splitRule is not None:
1206
+ tmpMatch = re.search(self.splitRuleToken["nChunksToWait"] + "=(\d+)", self.splitRule)
1207
+ if tmpMatch is not None:
1208
+ return int(tmpMatch.group(1))
1209
+ return None
1210
+
1211
+ # get max walltime
1212
+ def getMaxWalltime(self):
1213
+ if self.splitRule is not None:
1214
+ tmpMatch = re.search(self.splitRuleToken["maxWalltime"] + "=(\d+)", self.splitRule)
1215
+ if tmpMatch is not None:
1216
+ return int(tmpMatch.group(1)) * 60 * 60
1217
+ return None
1218
+
1219
+ # set max walltime
1220
+ def set_max_walltime(self, value):
1221
+ self.setSplitRule("maxWalltime", str(value))
1222
+
1223
+ # get target size of the largest output to reset NG
1224
+ def getTgtMaxOutputForNG(self):
1225
+ if self.splitRule is not None:
1226
+ tmpMatch = re.search(self.splitRuleToken["tgtMaxOutputForNG"] + "=(\d+)", self.splitRule)
1227
+ if tmpMatch is not None:
1228
+ return int(tmpMatch.group(1))
1229
+ return None
1230
+
1231
+ # not discard events
1232
+ def notDiscardEvents(self):
1233
+ return self.check_split_rule("notDiscardEvents")
1234
+
1235
+ # get min CPU efficiency
1236
+ def getMinCpuEfficiency(self):
1237
+ if self.splitRule is not None:
1238
+ tmpMatch = re.search(self.splitRuleToken["minCpuEfficiency"] + "=(\d+)", self.splitRule)
1239
+ if tmpMatch is not None:
1240
+ return int(tmpMatch.group(1))
1241
+ return None
1242
+
1243
+ # decrement attemptNr of events only when failed
1244
+ def decAttOnFailedES(self):
1245
+ return self.check_split_rule("decAttOnFailedES")
1246
+
1247
+ # use zip files to pin input files
1248
+ def useZipToPin(self):
1249
+ return self.check_split_rule("useZipToPin")
1250
+
1251
+ # architecture:
1252
+ # old format: sw_platform<@base_platform><#host_cpu_spec><&host_gpu_spec>
1253
+ # host_cpu_spec: architecture<-vendor<-instruction_set>>
1254
+ # host_gpu_spec: vendor<-model>
1255
+ #
1256
+ # new format: a json dict with keys of sw_platform, base_platform, cpu_specs, and gpu_spec
1257
+ # cpu_specs: a list of json dicts with keys of arch, vendor, and instr
1258
+ # gpu_spec: a json dict with keys of vendor and model
1259
+
1260
+ # reformat architecture into JSON
1261
+ def reformat_architecture(self):
1262
+ if self.architecture is None:
1263
+ return
1264
+ encoded_platform = ""
1265
+ try:
1266
+ # JSON format without encoded platform
1267
+ tmp_dict = json.loads(self.architecture)
1268
+ # skip if encoded platform is not present
1269
+ if "encoded_platform" not in tmp_dict:
1270
+ return
1271
+ encoded_platform = tmp_dict["encoded_platform"]
1272
+ except Exception:
1273
+ pass
1274
+ # convert to new format
1275
+ new_dict = {}
1276
+ val = self.get_sw_platform()
1277
+ if val:
1278
+ new_dict["sw_platform"] = val
1279
+ val = self.get_base_platform(encoded_platform)
1280
+ if val:
1281
+ new_dict["base_platform"] = val
1282
+ val = self.get_host_cpu_spec(encoded_platform)
1283
+ if val:
1284
+ # remove wildcard entries and empty specs
1285
+ l = [x for x in [{k: v for k, v in d.items() if v != "*"} for d in val] if x]
1286
+ if l:
1287
+ new_dict["cpu_specs"] = l
1288
+ val = self.get_host_gpu_spec()
1289
+ if val:
1290
+ # remove wildcard entries and empty specs
1291
+ l = {k: v for k, v in val.items() if v != "*"}
1292
+ if l:
1293
+ new_dict["gpu_spec"] = l
1294
+ self.architecture = json.dumps(new_dict)
1295
+
1296
+ # get SW platform
1297
+ def get_sw_platform(self):
1298
+ try:
1299
+ d = json.loads(self.architecture)
1300
+ return d.get("sw_platform", "")
1301
+ except Exception:
1302
+ pass
1303
+ if self.architecture is not None:
1304
+ m = re.search("^([^@#&]*)", self.architecture)
1305
+ if m:
1306
+ return m.group(1)
1307
+ return self.architecture
1308
+
1309
+ # get base platform
1310
+ def get_base_platform(self, encoded_platform=None):
1311
+ try:
1312
+ d = json.loads(self.architecture)
1313
+ val = d.get("base_platform", None)
1314
+ if val is not None or encoded_platform is None:
1315
+ return val
1316
+ except Exception:
1317
+ pass
1318
+ if encoded_platform:
1319
+ architecture = encoded_platform
1320
+ else:
1321
+ architecture = self.architecture
1322
+ if architecture is None or "@" not in architecture:
1323
+ return None
1324
+ m = re.search("@([^#&]*)", architecture)
1325
+ img = m.group(1)
1326
+ if img == "":
1327
+ img = None
1328
+ return img
1329
+
1330
+ # get platforms
1331
+ def get_platforms(self):
1332
+ if self.architecture is not None:
1333
+ platform = self.get_sw_platform()
1334
+ base = self.get_base_platform()
1335
+ if base:
1336
+ platform += "@" + base
1337
+ return platform
1338
+ return self.architecture
1339
+
1340
+ # get host CPU spec
1341
+ def get_host_cpu_spec(self, encoded_platform=None):
1342
+ try:
1343
+ d = json.loads(self.architecture)
1344
+ specs = d.get("cpu_specs", None)
1345
+ if not specs and encoded_platform is None:
1346
+ return None
1347
+ else:
1348
+ for spec in specs:
1349
+ spec.setdefault("vendor", "*")
1350
+ spec.setdefault("instr", "*")
1351
+ return specs
1352
+ except Exception:
1353
+ pass
1354
+ if encoded_platform:
1355
+ architecture = encoded_platform
1356
+ else:
1357
+ architecture = self.architecture
1358
+ try:
1359
+ if not architecture:
1360
+ return None
1361
+ if "#" not in architecture:
1362
+ if re.search(r"^[\^@&]", architecture):
1363
+ return None
1364
+ arch = architecture.split("-")[0]
1365
+ if arch:
1366
+ return [{"arch": arch, "vendor": "*", "instr": "*"}]
1367
+ return None
1368
+ m = re.search(r"#([^\^@&]*)", architecture)
1369
+ spec_strs = m.group(1)
1370
+ if not spec_strs:
1371
+ return None
1372
+ # remove ()
1373
+ if spec_strs.startswith("("):
1374
+ spec_strs = spec_strs[1:]
1375
+ if spec_strs.endswith(")"):
1376
+ spec_strs = spec_strs[:-1]
1377
+ specs = []
1378
+ for spec_str in spec_strs.split("|"):
1379
+ spec_str += "-*" * (2 - spec_str.count("-"))
1380
+ if "-" not in spec_str:
1381
+ spec_str += "-*"
1382
+ items = spec_str.split("-")
1383
+ spec = {"arch": items[0], "vendor": items[1], "instr": items[2]}
1384
+ specs.append(spec)
1385
+ return specs
1386
+ except Exception:
1387
+ return None
1388
+
1389
+ def get_host_cpu_preference(self):
1390
+ try:
1391
+ d = json.loads(self.architecture)
1392
+ cpu_pref = d.get("cpu_pref", None)
1393
+ return cpu_pref
1394
+ except Exception:
1395
+ return None
1396
+
1397
+ # get host GPU spec
1398
+ def get_host_gpu_spec(self):
1399
+ try:
1400
+ d = json.loads(self.architecture)
1401
+ spec = d.get("gpu_spec", None)
1402
+ spec.setdefault("vendor", "*")
1403
+ spec.setdefault("model", "*")
1404
+ return spec
1405
+ except Exception:
1406
+ pass
1407
+ try:
1408
+ if self.architecture is None or "&" not in self.architecture:
1409
+ return None
1410
+ m = re.search(r"&([^\^@#]*)", self.architecture)
1411
+ spec_str = m.group(1)
1412
+ if not spec_str:
1413
+ return None
1414
+ spec_str += "-*" * (1 - spec_str.count("-"))
1415
+ items = spec_str.split("-")
1416
+ spec = {"vendor": items[0], "model": items[1]}
1417
+ return spec
1418
+ except Exception:
1419
+ return None
1420
+
1421
+ # HPO workflow
1422
+ def is_hpo_workflow(self):
1423
+ return self.check_split_rule("hpoWorkflow")
1424
+
1425
+ # debug mode
1426
+ def is_debug_mode(self):
1427
+ return self.check_split_rule("debugMode")
1428
+
1429
+ # multi-step execution
1430
+ def is_multi_step_exec(self):
1431
+ return self.check_split_rule("multiStepExec")
1432
+
1433
+ # get max number of jobs
1434
+ def get_max_num_jobs(self):
1435
+ if self.splitRule is not None:
1436
+ tmpMatch = re.search(self.splitRuleToken["maxNumJobs"] + "=(\d+)", self.splitRule)
1437
+ if tmpMatch is not None:
1438
+ return int(tmpMatch.group(1))
1439
+ return None
1440
+
1441
+ # get total number of jobs
1442
+ def get_total_num_jobs(self):
1443
+ if self.splitRule is not None:
1444
+ tmpMatch = re.search(self.splitRuleToken["totNumJobs"] + "=(\d+)", self.splitRule)
1445
+ if tmpMatch is not None:
1446
+ return int(tmpMatch.group(1))
1447
+ return None
1448
+
1449
+ # use only tags for fat container
1450
+ def use_only_tags_fc(self):
1451
+ return self.check_split_rule("onlyTagsForFC")
1452
+
1453
+ # avoid VP
1454
+ def avoid_vp(self):
1455
+ return self.check_split_rule("avoidVP")
1456
+
1457
+ # set first contents feed
1458
+ def set_first_contents_feed(self, is_first):
1459
+ if is_first:
1460
+ self.setSplitRule("firstContentsFeed", self.FirstContentsFeed.TRUE.value)
1461
+ else:
1462
+ self.setSplitRule("firstContentsFeed", self.FirstContentsFeed.FALSE.value)
1463
+
1464
+ # check if first contents feed
1465
+ def is_first_contents_feed(self):
1466
+ if self.splitRule is not None:
1467
+ tmpMatch = re.search(self.splitRuleToken["firstContentsFeed"] + "=(\d+)", self.splitRule)
1468
+ if tmpMatch is not None and tmpMatch.group(1) == self.FirstContentsFeed.TRUE.value:
1469
+ return True
1470
+ return False
1471
+
1472
+ # check if work is segmented
1473
+ def is_work_segmented(self):
1474
+ return self.check_split_rule("segmentedWork")
1475
+
1476
+ # check if looping check is disabled
1477
+ def no_looping_check(self):
1478
+ return self.check_split_rule("noLoopingCheck")
1479
+
1480
+ # encode job parameters
1481
+ def encode_job_params(self):
1482
+ return self.check_split_rule("encJobParams")
1483
+
1484
+ # get original error dialog
1485
+ def get_original_error_dialog(self):
1486
+ if not self.origErrorDialog:
1487
+ return ""
1488
+ # remove log URL
1489
+ tmpStr = re.sub("<a href.+</a> : ", "", self.origErrorDialog)
1490
+ return tmpStr.split(". ")[-1]
1491
+
1492
+ # check if secrets are used
1493
+ def use_secrets(self):
1494
+ return self.check_split_rule("useSecrets")
1495
+
1496
+ # get max core count
1497
+ def get_max_core_count(self):
1498
+ if self.splitRule is not None:
1499
+ tmpMatch = re.search(self.splitRuleToken["maxCoreCount"] + "=(\d+)", self.splitRule)
1500
+ if tmpMatch is not None:
1501
+ return int(tmpMatch.group(1))
1502
+ return None
1503
+
1504
+ # push status changes
1505
+ def push_status_changes(self):
1506
+ return push_status_changes(self.splitRule)
1507
+
1508
+ # use cloud as VO
1509
+ def cloud_as_vo(self):
1510
+ return self.check_split_rule("cloudAsVO")
1511
+
1512
+ # push job
1513
+ def push_job(self):
1514
+ return self.check_split_rule("pushJob")
1515
+
1516
+ # fine-grained process
1517
+ def is_fine_grained_process(self):
1518
+ return self.check_split_rule("fineGrainedProc")
1519
+
1520
+ # on site merging
1521
+ def on_site_merging(self):
1522
+ return self.check_split_rule("onSiteMerging")
1523
+
1524
+ # set full chain flag
1525
+ def set_full_chain(self, mode):
1526
+ var = None
1527
+ if mode == "only":
1528
+ var = self.FullChain.Only
1529
+ elif mode == "require":
1530
+ var = self.FullChain.Require
1531
+ elif mode == "capable":
1532
+ var = self.FullChain.Capable
1533
+ if var:
1534
+ self.setSplitRule("fullChain", var)
1535
+
1536
+ # get full chain flag
1537
+ def get_full_chain(self):
1538
+ if self.splitRule:
1539
+ tmpMatch = re.search(self.splitRuleToken["fullChain"] + r"=(\d+)", self.splitRule)
1540
+ if tmpMatch:
1541
+ return tmpMatch.group(1)
1542
+ return None
1543
+
1544
+ # check full chain with mode
1545
+ def check_full_chain_with_mode(self, mode):
1546
+ task_flag = self.get_full_chain()
1547
+ if mode == "only":
1548
+ if task_flag == self.FullChain.Only:
1549
+ return True
1550
+ elif mode == "require":
1551
+ if task_flag == self.FullChain.Require:
1552
+ return True
1553
+ elif mode == "capable":
1554
+ if task_flag == self.FullChain.Capable:
1555
+ return True
1556
+ return False
1557
+
1558
+ # check full chain with nucleus
1559
+ def check_full_chain_with_nucleus(self, nucleus):
1560
+ if self.get_full_chain() and nucleus.get_bare_nucleus_mode():
1561
+ return True
1562
+ return False
1563
+
1564
+ # get RAM for retry
1565
+ def get_ram_for_retry(self, current_ram):
1566
+ if not self.splitRule:
1567
+ return None
1568
+ tmpMatch = re.search(self.splitRuleToken["retryRamOffset"] + r"=(\d+)", self.splitRule)
1569
+ if not tmpMatch:
1570
+ return None
1571
+ offset = int(tmpMatch.group(1))
1572
+ tmpMatch = re.search(self.splitRuleToken["retryRamStep"] + r"=(\d+)", self.splitRule)
1573
+ if tmpMatch:
1574
+ step = int(tmpMatch.group(1))
1575
+ else:
1576
+ step = 0
1577
+ tmpMatch = re.search(self.splitRuleToken["retryRamMax"] + r"=(\d+)", self.splitRule)
1578
+ if tmpMatch:
1579
+ max_ram = int(tmpMatch.group(1))
1580
+ else:
1581
+ max_ram = None
1582
+ if not current_ram:
1583
+ return current_ram
1584
+ if current_ram < offset:
1585
+ if max_ram is None:
1586
+ return offset
1587
+ else:
1588
+ return min(offset, max_ram)
1589
+ if not step:
1590
+ return current_ram
1591
+ if max_ram is None:
1592
+ return offset + math.ceil((current_ram - offset) / step) * step
1593
+ else:
1594
+ return min(offset + math.ceil((current_ram - offset) / step) * step, max_ram)
1595
+
1596
+ # get number of events per input
1597
+ def get_num_events_per_input(self):
1598
+ if self.splitRule is not None:
1599
+ tmpMatch = re.search(self.splitRuleToken["nEventsPerInput"] + "=(\d+)", self.splitRule)
1600
+ if tmpMatch is not None:
1601
+ return int(tmpMatch.group(1))
1602
+ return None
1603
+
1604
+ def get_max_events_per_job(self):
1605
+ if self.splitRule is not None:
1606
+ tmpMatch = re.search(self.splitRuleToken["maxEventsPerJob"] + "=(\d+)", self.splitRule)
1607
+ if tmpMatch is not None:
1608
+ return int(tmpMatch.group(1))
1609
+ return None
1610
+
1611
+ # set order input by
1612
+ def set_order_input_by(self, mode):
1613
+ var = None
1614
+ if mode == "eventsAlignment":
1615
+ var = self.OrderInputBy.eventsAlignment
1616
+ if var:
1617
+ self.setSplitRule("orderInputBy", var)
1618
+
1619
+ # get full chain flag
1620
+ def order_input_by(self):
1621
+ if self.splitRule:
1622
+ tmpMatch = re.search(self.splitRuleToken["orderInputBy"] + r"=(\d+)", self.splitRule)
1623
+ if tmpMatch:
1624
+ if tmpMatch.group(1) == self.OrderInputBy.eventsAlignment:
1625
+ return "eventsAlignment"
1626
+ return None
1627
+
1628
+ # check if intermediate task
1629
+ def is_intermediate_task(self):
1630
+ return self.check_split_rule("intermediateTask")
1631
+
1632
+ # check if message driven
1633
+ def is_msg_driven(self):
1634
+ return is_msg_driven(self.splitRule)
1635
+
1636
+ # check if incomplete input datasets are allowed
1637
+ def allow_incomplete_input(self):
1638
+ return self.check_split_rule("allowIncompleteInDS")
1639
+
1640
+ # check if workflow holdup
1641
+ def is_workflow_holdup(self):
1642
+ return self.check_split_rule("workflowHoldup")
1643
+
1644
+ # set workflow holdup
1645
+ def set_workflow_holdup(self, value: bool):
1646
+ if value:
1647
+ self.setSplitRule("workflowHoldup", "1")
1648
+ else:
1649
+ self.removeSplitRule(self.splitRuleToken["workflowHoldup"])
1650
+
1651
+ # get queued time
1652
+ def get_queued_time(self):
1653
+ """
1654
+ Get queued time in timestamp
1655
+ :return: queued time in timestamp. None if not set
1656
+ """
1657
+ if self.queuedTime is None:
1658
+ return None
1659
+ return self.queuedTime.timestamp()
1660
+
1661
+
1662
+ # utils
1663
+
1664
+
1665
+ # check split rule with positive integer
1666
+ def check_split_rule_positive_int(key, split_rule):
1667
+ if not split_rule:
1668
+ return False
1669
+ tmpMatch = re.search(JediTaskSpec.splitRuleToken[key] + r"=(\d+)", split_rule)
1670
+ if not tmpMatch or int(tmpMatch.group(1)) <= 0:
1671
+ return False
1672
+ return True
1673
+
1674
+
1675
+ # check if push status changes without class instance
1676
+ def push_status_changes(split_rule):
1677
+ return check_split_rule_positive_int("pushStatusChanges", split_rule)
1678
+
1679
+
1680
+ # check if message driven without class instance
1681
+ def is_msg_driven(split_rule):
1682
+ return check_split_rule_positive_int("messageDriven", split_rule)
1683
+
1684
+
1685
+ # check if auto pause is disabled
1686
+ def is_auto_pause_disabled(split_rule):
1687
+ return not check_split_rule_positive_int("noAutoPause", split_rule)