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,785 @@
1
+ import datetime
2
+ import math
3
+ import os
4
+ import re
5
+ import socket
6
+ import sys
7
+ import time
8
+ import traceback
9
+ import uuid
10
+
11
+ from pandajedi.jediconfig import jedi_config
12
+ from pandajedi.jedicore import Interaction
13
+ from pandajedi.jedicore.MsgWrapper import MsgWrapper
14
+ from pandajedi.jedicore.ThreadUtils import ListWithLock, ThreadPool, WorkerThread
15
+ from pandajedi.jedirefine import RefinerUtils
16
+
17
+ from .JediKnight import JediKnight
18
+
19
+ try:
20
+ import idds.common.constants
21
+ import idds.common.utils
22
+ from idds.client.client import Client as iDDS_Client
23
+ except ImportError:
24
+ pass
25
+
26
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
27
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
28
+
29
+ logger = PandaLogger().getLogger(__name__.split(".")[-1])
30
+
31
+
32
+ # worker class to take care of DatasetContents table
33
+ class ContentsFeeder(JediKnight):
34
+ # constructor
35
+ def __init__(self, commuChannel, taskBufferIF, ddmIF, vos, prodSourceLabels):
36
+ self.vos = self.parseInit(vos)
37
+ self.prodSourceLabels = self.parseInit(prodSourceLabels)
38
+ self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}_{os.getpgrp()}-con"
39
+ JediKnight.__init__(self, commuChannel, taskBufferIF, ddmIF, logger)
40
+
41
+ # main
42
+ def start(self):
43
+ # start base class
44
+ JediKnight.start(self)
45
+ # go into main loop
46
+ while True:
47
+ startTime = naive_utcnow()
48
+ try:
49
+ # loop over all vos
50
+ for vo in self.vos:
51
+ # loop over all sourceLabels
52
+ for prodSourceLabel in self.prodSourceLabels:
53
+ # get the list of datasets to feed contents to DB
54
+ tmpList = self.taskBufferIF.getDatasetsToFeedContents_JEDI(vo, prodSourceLabel)
55
+ if tmpList is None:
56
+ # failed
57
+ logger.error("failed to get the list of datasets to feed contents")
58
+ else:
59
+ logger.debug(f"got {len(tmpList)} datasets")
60
+ # put to a locked list
61
+ dsList = ListWithLock(tmpList)
62
+ # make thread pool
63
+ threadPool = ThreadPool()
64
+ # make workers
65
+ nWorker = jedi_config.confeeder.nWorkers
66
+ for iWorker in range(nWorker):
67
+ thr = ContentsFeederThread(dsList, threadPool, self.taskBufferIF, self.ddmIF, self.pid)
68
+ thr.start()
69
+ # join
70
+ threadPool.join()
71
+ except Exception:
72
+ errtype, errvalue = sys.exc_info()[:2]
73
+ logger.error(f"failed in {self.__class__.__name__}.start() with {errtype.__name__} {errvalue}")
74
+ # sleep if needed
75
+ loopCycle = jedi_config.confeeder.loopCycle
76
+ timeDelta = naive_utcnow() - startTime
77
+ sleepPeriod = loopCycle - timeDelta.seconds
78
+ if sleepPeriod > 0:
79
+ time.sleep(sleepPeriod)
80
+ # randomize cycle
81
+ self.randomSleep(max_val=loopCycle)
82
+
83
+
84
+ # thread for real worker
85
+ class ContentsFeederThread(WorkerThread):
86
+ # constructor
87
+ def __init__(self, taskDsList, threadPool, taskbufferIF, ddmIF, pid):
88
+ # initialize woker with no semaphore
89
+ WorkerThread.__init__(self, None, threadPool, logger)
90
+ # attributres
91
+ self.taskDsList = taskDsList
92
+ self.taskBufferIF = taskbufferIF
93
+ self.ddmIF = ddmIF
94
+ self.msgType = "contentsfeeder"
95
+ self.pid = pid
96
+
97
+ # main
98
+ def runImpl(self):
99
+ while True:
100
+ try:
101
+ # get a part of list
102
+ nTasks = 10
103
+ taskDsList = self.taskDsList.get(nTasks)
104
+ # no more datasets
105
+ if len(taskDsList) == 0:
106
+ self.logger.debug(f"{self.__class__.__name__} terminating since no more items")
107
+ return
108
+ # feed to tasks
109
+ self.feed_contents_to_tasks(taskDsList)
110
+ except Exception as e:
111
+ logger.error(f"{self.__class__.__name__} failed in runImpl() with {str(e)}: {traceback.format_exc()}")
112
+
113
+ # feed contents to tasks
114
+ def feed_contents_to_tasks(self, task_ds_list, real_run=True):
115
+ # max number of file records per dataset
116
+ maxFileRecords = 200000
117
+ # loop over all tasks
118
+ for jediTaskID, dsList in task_ds_list:
119
+ allUpdated = True
120
+ taskBroken = False
121
+ taskOnHold = False
122
+ runningTask = False
123
+ taskToFinish = False
124
+ missingMap = {}
125
+ datasetsIdxConsistency = []
126
+
127
+ # get task
128
+ tmpStat, taskSpec = self.taskBufferIF.getTaskWithID_JEDI(jediTaskID, False, real_run, self.pid, 10, clearError=True)
129
+ if not tmpStat or taskSpec is None:
130
+ self.logger.debug(f"failed to get taskSpec for jediTaskID={jediTaskID}")
131
+ continue
132
+
133
+ # make logger
134
+ try:
135
+ gshare = "_".join(taskSpec.gshare.split(" "))
136
+ except Exception:
137
+ gshare = "Undefined"
138
+ tmpLog = MsgWrapper(self.logger, f"<jediTaskID={jediTaskID} gshare={gshare}>")
139
+
140
+ try:
141
+ # get task parameters
142
+ taskParam = self.taskBufferIF.getTaskParamsWithID_JEDI(jediTaskID)
143
+ taskParamMap = RefinerUtils.decodeJSON(taskParam)
144
+ except Exception as e:
145
+ tmpLog.error(f"task param conversion from json failed with {str(e)}")
146
+ # unlock
147
+ tmpStat = self.taskBufferIF.unlockSingleTask_JEDI(jediTaskID, self.pid)
148
+ tmpLog.debug(f"unlocked with {tmpStat}")
149
+ continue
150
+ # renaming of parameters
151
+ if "nEventsPerInputFile" in taskParamMap:
152
+ taskParamMap["nEventsPerFile"] = taskParamMap["nEventsPerInputFile"]
153
+ # the number of files per job
154
+ nFilesPerJob = taskSpec.getNumFilesPerJob()
155
+ # the number of chunks used by scout
156
+ nChunksForScout = 10
157
+ # load XML
158
+ if taskSpec.useLoadXML():
159
+ xmlConfig = taskParamMap["loadXML"]
160
+ else:
161
+ xmlConfig = None
162
+ # skip files used by another task
163
+ if "skipFilesUsedBy" in taskParamMap:
164
+ skipFilesUsedBy = taskParamMap["skipFilesUsedBy"]
165
+ else:
166
+ skipFilesUsedBy = None
167
+ # check no wait
168
+ noWaitParent = False
169
+ parentOutDatasets = set()
170
+ if taskSpec.noWaitParent() and taskSpec.parent_tid not in [None, taskSpec.jediTaskID]:
171
+ tmpStat = self.taskBufferIF.checkParentTask_JEDI(taskSpec.parent_tid, taskSpec.jediTaskID)
172
+ if tmpStat is None or tmpStat == "running":
173
+ noWaitParent = True
174
+ # get output datasets from parent task
175
+ tmpParentStat, tmpParentOutDatasets = self.taskBufferIF.getDatasetsWithJediTaskID_JEDI(taskSpec.parent_tid, ["output", "log"])
176
+ # collect dataset names
177
+ for tmpParentOutDataset in tmpParentOutDatasets:
178
+ parentOutDatasets.add(tmpParentOutDataset.datasetName)
179
+ if tmpParentOutDataset.containerName:
180
+ if tmpParentOutDataset.containerName.endswith("/"):
181
+ parentOutDatasets.add(tmpParentOutDataset.containerName)
182
+ parentOutDatasets.add(tmpParentOutDataset.containerName[:-1])
183
+ else:
184
+ parentOutDatasets.add(tmpParentOutDataset.containerName)
185
+ parentOutDatasets.add(tmpParentOutDataset.containerName + "/")
186
+ # loop over all datasets
187
+ nFilesMaster = 0
188
+ nFilesMasterReady = 0
189
+ checkedMaster = False
190
+ setFrozenTime = True
191
+ master_offset = None
192
+ master_is_open = False
193
+ if not taskBroken:
194
+ ddmIF = self.ddmIF.getInterface(taskSpec.vo, taskSpec.cloud)
195
+ origNumFiles = None
196
+ if "nFiles" in taskParamMap:
197
+ origNumFiles = taskParamMap["nFiles"]
198
+ id_to_container = {}
199
+ [id_to_container.update({datasetSpec.datasetID: datasetSpec.containerName}) for datasetSpec in dsList]
200
+ skip_secondaries = False
201
+ for datasetSpec in dsList:
202
+ tmpLog.debug(f"start loop for {datasetSpec.datasetName}(id={datasetSpec.datasetID})")
203
+ if skip_secondaries and not datasetSpec.isMaster():
204
+ tmpLog.debug(f"skip {datasetSpec.datasetName} since it is secondary")
205
+ continue
206
+ # index consistency
207
+ if datasetSpec.indexConsistent():
208
+ datasetsIdxConsistency.append(datasetSpec.datasetID)
209
+ # prestaging
210
+ if taskSpec.inputPreStaging() and (datasetSpec.isMaster() or datasetSpec.isSeqNumber()):
211
+ if datasetSpec.is_no_staging():
212
+ inputPreStaging = False
213
+ elif (
214
+ (nStaging := self.taskBufferIF.getNumStagingFiles_JEDI(taskSpec.jediTaskID)) is not None
215
+ and nStaging == 0
216
+ and datasetSpec.nFiles > 0
217
+ ):
218
+ inputPreStaging = False
219
+ else:
220
+ inputPreStaging = True
221
+ else:
222
+ inputPreStaging = False
223
+ # get dataset metadata
224
+ tmpLog.debug("get metadata")
225
+ stateUpdateTime = naive_utcnow()
226
+ try:
227
+ if not datasetSpec.isPseudo():
228
+ tmpMetadata = ddmIF.getDatasetMetaData(datasetSpec.datasetName, ignore_missing=True)
229
+ else:
230
+ # dummy metadata for pseudo dataset
231
+ tmpMetadata = {"state": "closed"}
232
+ # check if master is open
233
+ if datasetSpec.isMaster() and tmpMetadata["state"] == "open":
234
+ master_is_open = True
235
+ # set mutable when the dataset is open and parent is running or task is configured to run until the dataset is closed
236
+ if (noWaitParent or taskSpec.runUntilClosed() or inputPreStaging) and (
237
+ tmpMetadata["state"] == "open"
238
+ or datasetSpec.datasetName in parentOutDatasets
239
+ or datasetSpec.datasetName.split(":")[-1] in parentOutDatasets
240
+ or inputPreStaging
241
+ ):
242
+ # dummy metadata when parent is running
243
+ tmpMetadata = {"state": "mutable"}
244
+ # set mutable when workflow holdup is set
245
+ if taskSpec.is_workflow_holdup():
246
+ tmpMetadata = {"state": "mutable"}
247
+ except Exception:
248
+ errtype, errvalue = sys.exc_info()[:2]
249
+ tmpLog.error(f"{self.__class__.__name__} failed to get metadata to {errtype.__name__}:{errvalue}")
250
+ if errtype == Interaction.JEDIFatalError:
251
+ # fatal error
252
+ datasetStatus = "broken"
253
+ taskBroken = True
254
+ # update dataset status
255
+ self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog)
256
+ else:
257
+ if not taskSpec.ignoreMissingInDS():
258
+ # temporary error
259
+ taskOnHold = True
260
+ else:
261
+ # ignore missing
262
+ datasetStatus = "failed"
263
+ # update dataset status
264
+ self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog)
265
+ taskSpec.setErrDiag(f"failed to get metadata for {datasetSpec.datasetName}")
266
+ if not taskSpec.ignoreMissingInDS():
267
+ allUpdated = False
268
+ else:
269
+ # to skip missing dataset
270
+ if tmpMetadata["state"] == "missing":
271
+ # ignore missing
272
+ datasetStatus = "finished"
273
+ # update dataset status
274
+ self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog, "closed")
275
+ tmpLog.debug(f"disabled missing {datasetSpec.datasetName}")
276
+ continue
277
+ # get file list specified in task parameters
278
+ if taskSpec.is_work_segmented() and not datasetSpec.isPseudo() and not datasetSpec.isMaster():
279
+ fileList = []
280
+ includePatt = []
281
+ excludePatt = []
282
+ try:
283
+ segment_id = int(id_to_container[datasetSpec.masterID].split("/")[-1])
284
+ for item in taskParamMap["segmentSpecs"]:
285
+ if item["id"] == segment_id:
286
+ if "files" in item:
287
+ fileList = item["files"]
288
+ elif "datasets" in item:
289
+ for tmpDatasetName in item["datasets"]:
290
+ tmpRet = ddmIF.getFilesInDataset(tmpDatasetName)
291
+ fileList += [tmpAttr["lfn"] for tmpAttr in tmpRet.values()]
292
+ except Exception:
293
+ pass
294
+ else:
295
+ fileList, includePatt, excludePatt = RefinerUtils.extractFileList(taskParamMap, datasetSpec.datasetName)
296
+ # get the number of events in metadata
297
+ if "getNumEventsInMetadata" in taskParamMap:
298
+ getNumEvents = True
299
+ else:
300
+ getNumEvents = False
301
+ # get file list from DDM
302
+ tmpLog.debug("get files")
303
+ try:
304
+ useInFilesWithNewAttemptNr = False
305
+ skipDuplicate = not datasetSpec.useDuplicatedFiles()
306
+ if not datasetSpec.isPseudo():
307
+ if fileList != [] and "useInFilesInContainer" in taskParamMap and datasetSpec.containerName not in ["", None]:
308
+ # read files from container if file list is specified in task parameters
309
+ tmpDatasetName = datasetSpec.containerName
310
+ else:
311
+ tmpDatasetName = datasetSpec.datasetName
312
+ # use long format for LB
313
+ longFormat = False
314
+ if taskSpec.respectLumiblock() or taskSpec.orderByLB():
315
+ longFormat = True
316
+ tmpRet = ddmIF.getFilesInDataset(tmpDatasetName, getNumEvents=getNumEvents, skipDuplicate=skipDuplicate, longFormat=longFormat)
317
+ tmpLog.debug(f"got {len(tmpRet)} files in {tmpDatasetName}")
318
+ else:
319
+ if datasetSpec.isSeqNumber():
320
+ # make dummy files for seq_number
321
+ if datasetSpec.getNumRecords() is not None:
322
+ nPFN = datasetSpec.getNumRecords()
323
+ elif origNumFiles is not None:
324
+ nPFN = origNumFiles
325
+ if "nEventsPerFile" in taskParamMap and taskSpec.get_min_granularity():
326
+ nPFN = nPFN * taskParamMap["nEventsPerFile"] // taskSpec.get_min_granularity()
327
+ elif (
328
+ "nEventsPerJob" in taskParamMap
329
+ and "nEventsPerFile" in taskParamMap
330
+ and taskParamMap["nEventsPerFile"] > taskParamMap["nEventsPerJob"]
331
+ ):
332
+ nPFN = nPFN * math.ceil(taskParamMap["nEventsPerFile"] / taskParamMap["nEventsPerJob"])
333
+ elif "nEventsPerJob" in taskParamMap and "nEventsPerFile" not in taskParamMap:
334
+ max_events_in_file = self.taskBufferIF.get_max_events_in_dataset(jediTaskID, datasetSpec.masterID)
335
+ if max_events_in_file and max_events_in_file > taskParamMap["nEventsPerJob"]:
336
+ nPFN = nPFN * math.ceil(max_events_in_file / taskParamMap["nEventsPerJob"])
337
+ elif "nEvents" in taskParamMap and "nEventsPerJob" in taskParamMap:
338
+ nPFN = math.ceil(taskParamMap["nEvents"] / taskParamMap["nEventsPerJob"])
339
+ elif "nEvents" in taskParamMap and "nEventsPerFile" in taskParamMap and taskSpec.getNumFilesPerJob() is not None:
340
+ nPFN = math.ceil(taskParamMap["nEvents"] / taskParamMap["nEventsPerFile"] / taskSpec.getNumFilesPerJob())
341
+ else:
342
+ # the default number of records for seq_number
343
+ seqDefNumRecords = 10000
344
+ # get nFiles of the master
345
+ tmpMasterAtt = self.taskBufferIF.getDatasetAttributes_JEDI(datasetSpec.jediTaskID, datasetSpec.masterID, ["nFiles"])
346
+ # use nFiles of the master as the number of records if it is larger than the default
347
+ if "nFiles" in tmpMasterAtt and tmpMasterAtt["nFiles"] > seqDefNumRecords:
348
+ nPFN = tmpMasterAtt["nFiles"]
349
+ else:
350
+ nPFN = seqDefNumRecords
351
+ # check usedBy
352
+ if skipFilesUsedBy is not None:
353
+ for tmpJediTaskID in str(skipFilesUsedBy).split(","):
354
+ tmpParentAtt = self.taskBufferIF.getDatasetAttributesWithMap_JEDI(
355
+ tmpJediTaskID, {"datasetName": datasetSpec.datasetName}, ["nFiles"]
356
+ )
357
+ if "nFiles" in tmpParentAtt and tmpParentAtt["nFiles"]:
358
+ nPFN += tmpParentAtt["nFiles"]
359
+ if nPFN > maxFileRecords:
360
+ raise Interaction.JEDIFatalError(f"Too many file records for seq_number >{maxFileRecords}")
361
+ tmpRet = {}
362
+ # get offset
363
+ tmpOffset = datasetSpec.getOffset()
364
+ tmpOffset += 1
365
+ for iPFN in range(nPFN):
366
+ tmpRet[str(uuid.uuid4())] = {
367
+ "lfn": iPFN + tmpOffset,
368
+ "scope": None,
369
+ "filesize": 0,
370
+ "checksum": None,
371
+ }
372
+ elif not taskSpec.useListPFN():
373
+ # dummy file list for pseudo dataset
374
+ tmpRet = {
375
+ str(uuid.uuid4()): {
376
+ "lfn": "pseudo_lfn",
377
+ "scope": None,
378
+ "filesize": 0,
379
+ "checksum": None,
380
+ }
381
+ }
382
+ else:
383
+ # make dummy file list for PFN list
384
+ if "nFiles" in taskParamMap:
385
+ nPFN = taskParamMap["nFiles"]
386
+ else:
387
+ nPFN = 1
388
+ tmpRet = {}
389
+ for iPFN in range(nPFN):
390
+ base_name = taskParamMap["pfnList"][iPFN].split("/")[-1]
391
+ n_events = None
392
+ if "^" in base_name:
393
+ base_name, n_events = base_name.split("^")
394
+
395
+ tmpRet[str(uuid.uuid4())] = {
396
+ "lfn": f"{iPFN:06d}:{base_name}",
397
+ "scope": None,
398
+ "filesize": 0,
399
+ "checksum": None,
400
+ "events": n_events,
401
+ }
402
+ except Exception:
403
+ errtype, errvalue = sys.exc_info()[:2]
404
+ reason = str(errvalue)
405
+ tmpLog.error(f"failed to get files in {self.__class__.__name__}:{errtype.__name__} due to {reason}")
406
+ if errtype == Interaction.JEDIFatalError:
407
+ # fatal error
408
+ datasetStatus = "broken"
409
+ taskBroken = True
410
+ # update dataset status
411
+ self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog)
412
+ else:
413
+ # temporary error
414
+ taskOnHold = True
415
+ taskSpec.setErrDiag(f"failed to get files for {datasetSpec.datasetName} due to {reason}")
416
+ allUpdated = False
417
+ else:
418
+ # parameters for master input
419
+ respectLB = False
420
+ useRealNumEvents = False
421
+ if datasetSpec.isMaster():
422
+ # respect LB boundaries
423
+ respectLB = taskSpec.respectLumiblock()
424
+ # use real number of events
425
+ useRealNumEvents = taskSpec.useRealNumEvents()
426
+ # the number of events per file
427
+ nEventsPerFile = None
428
+ nEventsPerJob = None
429
+ nEventsPerRange = None
430
+ tgtNumEventsPerJob = None
431
+ if (datasetSpec.isMaster() and ("nEventsPerFile" in taskParamMap or useRealNumEvents)) or (
432
+ datasetSpec.isPseudo() and "nEvents" in taskParamMap and not datasetSpec.isSeqNumber()
433
+ ):
434
+ if "nEventsPerFile" in taskParamMap:
435
+ nEventsPerFile = taskParamMap["nEventsPerFile"]
436
+ elif datasetSpec.isMaster() and datasetSpec.isPseudo() and "nEvents" in taskParamMap:
437
+ # use nEvents as nEventsPerFile for pseudo input
438
+ nEventsPerFile = taskParamMap["nEvents"]
439
+ if taskSpec.get_min_granularity():
440
+ nEventsPerRange = taskSpec.get_min_granularity()
441
+ elif "nEventsPerJob" in taskParamMap:
442
+ nEventsPerJob = taskParamMap["nEventsPerJob"]
443
+ if "tgtNumEventsPerJob" in taskParamMap:
444
+ tgtNumEventsPerJob = taskParamMap["tgtNumEventsPerJob"]
445
+ # reset nEventsPerJob
446
+ nEventsPerJob = None
447
+ # max attempts
448
+ maxAttempt = None
449
+ maxFailure = None
450
+ if datasetSpec.isMaster() or datasetSpec.toKeepTrack():
451
+ # max attempts
452
+ if taskSpec.disableAutoRetry():
453
+ # disable auto retry
454
+ maxAttempt = 1
455
+ elif "maxAttempt" in taskParamMap:
456
+ maxAttempt = taskParamMap["maxAttempt"]
457
+ else:
458
+ # use default value
459
+ maxAttempt = 3
460
+ # max failure
461
+ if "maxFailure" in taskParamMap:
462
+ maxFailure = taskParamMap["maxFailure"]
463
+ # first event number
464
+ firstEventNumber = None
465
+ if datasetSpec.isMaster():
466
+ # first event number
467
+ firstEventNumber = 1 + taskSpec.getFirstEventOffset()
468
+ # nMaxEvents
469
+ nMaxEvents = None
470
+ if datasetSpec.isMaster() and "nEvents" in taskParamMap:
471
+ nMaxEvents = taskParamMap["nEvents"]
472
+ # nMaxFiles
473
+ nMaxFiles = None
474
+ if "nFiles" in taskParamMap:
475
+ if datasetSpec.isMaster():
476
+ nMaxFiles = taskParamMap["nFiles"]
477
+ else:
478
+ # calculate for secondary
479
+ if not datasetSpec.isPseudo():
480
+ # check nFilesPerJob
481
+ nFilesPerJobSec = datasetSpec.getNumFilesPerJob()
482
+ if nFilesPerJobSec is not None:
483
+ nMaxFiles = origNumFiles * nFilesPerJobSec
484
+ # check ratio
485
+ if nMaxFiles is None:
486
+ nMaxFiles = datasetSpec.getNumMultByRatio(origNumFiles)
487
+ # multiplied by the number of jobs per file for event-level splitting
488
+ if nMaxFiles is not None:
489
+ if "nEventsPerFile" in taskParamMap:
490
+ if taskSpec.get_min_granularity():
491
+ if taskParamMap["nEventsPerFile"] > taskSpec.get_min_granularity():
492
+ nMaxFiles *= float(taskParamMap["nEventsPerFile"]) / float(taskSpec.get_min_granularity())
493
+ nMaxFiles = int(math.ceil(nMaxFiles))
494
+ elif "nEventsPerJob" in taskParamMap:
495
+ if taskParamMap["nEventsPerFile"] > taskParamMap["nEventsPerJob"]:
496
+ nMaxFiles *= float(taskParamMap["nEventsPerFile"]) / float(taskParamMap["nEventsPerJob"])
497
+ nMaxFiles = int(math.ceil(nMaxFiles))
498
+ elif "useRealNumEvents" in taskParamMap:
499
+ # reset nMaxFiles since it is unknown
500
+ nMaxFiles = None
501
+ # use scout
502
+ useScout = False
503
+ if datasetSpec.isMaster() and taskSpec.useScout() and (datasetSpec.status != "toupdate" or not taskSpec.isPostScout()):
504
+ useScout = True
505
+ # use files with new attempt numbers
506
+ useFilesWithNewAttemptNr = False
507
+ if not datasetSpec.isPseudo() and fileList != [] and "useInFilesWithNewAttemptNr" in taskParamMap:
508
+ useFilesWithNewAttemptNr = True
509
+ # ramCount
510
+ ramCount = 0
511
+ # skip short input
512
+ if (
513
+ datasetSpec.isMaster()
514
+ and not datasetSpec.isPseudo()
515
+ and nEventsPerFile is not None
516
+ and nEventsPerJob is not None
517
+ and nEventsPerFile >= nEventsPerJob
518
+ and "skipShortInput" in taskParamMap
519
+ and taskParamMap["skipShortInput"] is True
520
+ ):
521
+ skipShortInput = True
522
+ else:
523
+ skipShortInput = False
524
+ # skip short output
525
+ if datasetSpec.isMaster() and not datasetSpec.isPseudo() and taskParamMap.get("skipShortOutput"):
526
+ skip_short_output = True
527
+ else:
528
+ skip_short_output = False
529
+ # order by
530
+ if taskSpec.order_input_by() and datasetSpec.isMaster() and not datasetSpec.isPseudo():
531
+ orderBy = taskSpec.order_input_by()
532
+ else:
533
+ orderBy = None
534
+ # feed files to the contents table
535
+ tmpLog.debug("update contents")
536
+ res_dict = self.taskBufferIF.insertFilesForDataset_JEDI(
537
+ datasetSpec,
538
+ tmpRet,
539
+ tmpMetadata["state"],
540
+ stateUpdateTime,
541
+ nEventsPerFile,
542
+ nEventsPerJob,
543
+ maxAttempt,
544
+ firstEventNumber,
545
+ nMaxFiles,
546
+ nMaxEvents,
547
+ useScout,
548
+ fileList,
549
+ useFilesWithNewAttemptNr,
550
+ nFilesPerJob,
551
+ nEventsPerRange,
552
+ nChunksForScout,
553
+ includePatt,
554
+ excludePatt,
555
+ xmlConfig,
556
+ noWaitParent,
557
+ taskSpec.parent_tid,
558
+ self.pid,
559
+ maxFailure,
560
+ useRealNumEvents,
561
+ respectLB,
562
+ tgtNumEventsPerJob,
563
+ skipFilesUsedBy,
564
+ ramCount,
565
+ taskSpec,
566
+ skipShortInput,
567
+ inputPreStaging,
568
+ orderBy,
569
+ maxFileRecords,
570
+ skip_short_output,
571
+ )
572
+ retDB = res_dict["ret_val"]
573
+ missingFileList = res_dict["missingFileList"]
574
+ nFilesUnique = res_dict["numUniqueLfn"]
575
+ diagMap = res_dict["diagMap"]
576
+ if retDB is False:
577
+ taskSpec.setErrDiag(f"failed to insert files for {datasetSpec.datasetName}. {diagMap['errMsg']}")
578
+ allUpdated = False
579
+ taskBroken = True
580
+ break
581
+ elif retDB is None:
582
+ # the dataset is locked by another or status is not applicable
583
+ allUpdated = False
584
+ tmpLog.debug("escape since task or dataset is locked")
585
+ break
586
+ elif missingFileList != []:
587
+ # files are missing
588
+ tmpErrStr = f"{len(missingFileList)} files missing in {datasetSpec.datasetName}"
589
+ tmpLog.debug(tmpErrStr)
590
+ taskSpec.setErrDiag(tmpErrStr)
591
+ allUpdated = False
592
+ taskOnHold = True
593
+ missingMap[datasetSpec.datasetName] = {"datasetSpec": datasetSpec, "missingFiles": missingFileList}
594
+ else:
595
+ # reduce the number of files to be read
596
+ if "nFiles" in taskParamMap:
597
+ if datasetSpec.isMaster():
598
+ taskParamMap["nFiles"] -= nFilesUnique
599
+ # reduce the number of files for scout
600
+ if useScout:
601
+ nChunksForScout = diagMap["nChunksForScout"]
602
+ # number of master input files
603
+ if datasetSpec.isMaster():
604
+ checkedMaster = True
605
+ nFilesMaster += nFilesUnique
606
+ nFilesMasterReady += res_dict.get("nReady", 0)
607
+ master_offset = datasetSpec.getOffset()
608
+ # running task
609
+ if diagMap["isRunningTask"]:
610
+ runningTask = True
611
+ # no activated pending input for noWait
612
+ if (
613
+ (noWaitParent or inputPreStaging)
614
+ and diagMap["nActivatedPending"] == 0
615
+ and not (useScout and nChunksForScout <= 0)
616
+ and tmpMetadata["state"] != "closed"
617
+ and datasetSpec.isMaster()
618
+ ):
619
+ tmpErrStr = "insufficient inputs are ready. "
620
+ if inputPreStaging:
621
+ # message for pending for staging
622
+ tmpErrStr += "inputs are staging from a tape. "
623
+ tmpErrStr += diagMap["errMsg"]
624
+ tmpLog.debug(tmpErrStr + ". skipping secondary datasets from now")
625
+ taskSpec.setErrDiag(tmpErrStr)
626
+ taskOnHold = True
627
+ setFrozenTime = False
628
+ skip_secondaries = True
629
+ tmpLog.debug("end loop")
630
+ # task holdup by workflow if no master inputs are ready
631
+ if not taskOnHold and not taskBroken and allUpdated and nFilesMasterReady == 0 and checkedMaster and taskSpec.is_workflow_holdup():
632
+ # hold up by the workflow
633
+ taskOnHold = True
634
+ tmpLog.debug("task to hold up by workflow")
635
+ # no master input
636
+ if not taskOnHold and not taskBroken and allUpdated and nFilesMaster == 0 and checkedMaster:
637
+ tmpErrStr = "no master input files. input dataset is empty"
638
+ if master_offset:
639
+ tmpErrStr += f" with offset={master_offset}"
640
+ tmpLog.error(tmpErrStr)
641
+ taskSpec.setErrDiag(tmpErrStr, None)
642
+ if noWaitParent:
643
+ # parent is running
644
+ taskOnHold = True
645
+ else:
646
+ # the task has no parent or parent is finished
647
+ if master_is_open and taskSpec.runUntilClosed():
648
+ # wait until the input dataset is closed
649
+ taskOnHold = True
650
+ elif not taskSpec.allowEmptyInput():
651
+ # empty input is not allowed
652
+ taskBroken = True
653
+ else:
654
+ # finish the task with empty input
655
+ taskToFinish = True
656
+ # index consistency
657
+ if not taskOnHold and not taskBroken and len(datasetsIdxConsistency) > 0:
658
+ self.taskBufferIF.removeFilesIndexInconsistent_JEDI(jediTaskID, datasetsIdxConsistency)
659
+ # update task status
660
+ if taskBroken or taskToFinish:
661
+ if taskBroken:
662
+ # task is broken
663
+ taskSpec.status = "tobroken"
664
+ else:
665
+ taskSpec.status = "finishing"
666
+ tmpMsg = f"set task_status={taskSpec.status}"
667
+ tmpLog.info(tmpMsg)
668
+ tmpLog.sendMsg(tmpMsg, self.msgType)
669
+ allRet = self.taskBufferIF.updateTaskStatusByContFeeder_JEDI(jediTaskID, taskSpec, pid=self.pid)
670
+ # change task status unless the task is running
671
+ if not runningTask:
672
+ # send prestaging request
673
+ if taskSpec.inputPreStaging() and taskSpec.is_first_contents_feed():
674
+ tmpStat, tmpErrStr = self.send_prestaging_request(taskSpec, taskParamMap, dsList, tmpLog)
675
+ if tmpStat:
676
+ taskSpec.set_first_contents_feed(False)
677
+ else:
678
+ tmpLog.debug(tmpErrStr)
679
+ taskSpec.setErrDiag(tmpErrStr)
680
+ taskOnHold = True
681
+ if taskOnHold:
682
+ # go to pending state
683
+ if taskSpec.status not in ["broken", "tobroken", "finishing"]:
684
+ taskSpec.setOnHold()
685
+ tmpMsg = f"set task_status={taskSpec.status}"
686
+ tmpLog.info(tmpMsg)
687
+ tmpLog.sendMsg(tmpMsg, self.msgType)
688
+ allRet = self.taskBufferIF.updateTaskStatusByContFeeder_JEDI(jediTaskID, taskSpec, pid=self.pid, setFrozenTime=setFrozenTime)
689
+ elif allUpdated:
690
+ # all OK
691
+ allRet, newTaskStatus = self.taskBufferIF.updateTaskStatusByContFeeder_JEDI(
692
+ jediTaskID, getTaskStatus=True, pid=self.pid, useWorldCloud=taskSpec.useWorldCloud()
693
+ )
694
+ tmpMsg = f"set task_status={newTaskStatus}"
695
+ tmpLog.info(tmpMsg)
696
+ tmpLog.sendMsg(tmpMsg, self.msgType)
697
+ # just unlock
698
+ retUnlock = self.taskBufferIF.unlockSingleTask_JEDI(jediTaskID, self.pid)
699
+ tmpLog.debug(f"unlock not-running task with {retUnlock}")
700
+ else:
701
+ # just unlock
702
+ retUnlock = self.taskBufferIF.unlockSingleTask_JEDI(jediTaskID, self.pid)
703
+ tmpLog.debug(f"unlock task with {retUnlock}")
704
+ # send message to job generator if new inputs are ready
705
+ if not taskOnHold and not taskBroken and allUpdated and taskSpec.is_msg_driven():
706
+ push_ret = self.taskBufferIF.push_task_trigger_message("jedi_job_generator", jediTaskID, task_spec=taskSpec)
707
+ if push_ret:
708
+ tmpLog.debug("pushed trigger message to jedi_job_generator")
709
+ else:
710
+ tmpLog.warning("failed to push trigger message to jedi_job_generator")
711
+ tmpLog.debug("done")
712
+
713
+ # update dataset
714
+ def updateDatasetStatus(self, datasetSpec, datasetStatus, tmpLog, datasetState=None):
715
+ # update dataset status
716
+ datasetSpec.status = datasetStatus
717
+ datasetSpec.lockedBy = None
718
+ if datasetState:
719
+ datasetSpec.state = datasetState
720
+ tmpLog.info(f"update dataset status to {datasetSpec.status} state to {datasetSpec.state}")
721
+ self.taskBufferIF.updateDataset_JEDI(datasetSpec, {"datasetID": datasetSpec.datasetID, "jediTaskID": datasetSpec.jediTaskID}, lockTask=True)
722
+
723
+ # send prestaging request
724
+ def send_prestaging_request(self, task_spec, task_params_map, ds_list, tmp_log):
725
+ try:
726
+ c = iDDS_Client(idds.common.utils.get_rest_host())
727
+ for datasetSpec in ds_list:
728
+ if datasetSpec.is_no_staging():
729
+ # skip no_staging
730
+ continue
731
+ # get rule
732
+ try:
733
+ tmp_scope, tmp_name = datasetSpec.datasetName.split(":")
734
+ tmp_name = re.sub("/$", "", tmp_name)
735
+ except Exception:
736
+ continue
737
+ try:
738
+ if "prestagingRuleID" in task_params_map:
739
+ if tmp_name not in task_params_map["prestagingRuleID"]:
740
+ continue
741
+ rule_id = task_params_map["prestagingRuleID"][tmp_name]
742
+ elif "selfPrestagingRule" in task_params_map:
743
+ if not datasetSpec.isMaster() or datasetSpec.isPseudo():
744
+ continue
745
+ rule_id = self.ddmIF.getInterface(task_spec.vo, task_spec.cloud).make_staging_rule(
746
+ tmp_scope + ":" + tmp_name, task_params_map["selfPrestagingRule"]
747
+ )
748
+ if not rule_id:
749
+ continue
750
+ else:
751
+ rule_id = self.ddmIF.getInterface(task_spec.vo, task_spec.cloud).getActiveStagingRule(tmp_scope + ":" + tmp_name)
752
+ if rule_id is None:
753
+ continue
754
+ except Exception as e:
755
+ return False, f"DDM error : {str(e)}"
756
+ # request
757
+ tmp_log.debug(f"sending request to iDDS for {datasetSpec.datasetName}")
758
+ req = {
759
+ "scope": tmp_scope,
760
+ "name": tmp_name,
761
+ "requester": "panda",
762
+ "request_type": idds.common.constants.RequestType.StageIn,
763
+ "transform_tag": idds.common.constants.RequestType.StageIn.value,
764
+ "status": idds.common.constants.RequestStatus.New,
765
+ "priority": 0,
766
+ "lifetime": 30,
767
+ "request_metadata": {
768
+ "workload_id": task_spec.jediTaskID,
769
+ "rule_id": rule_id,
770
+ },
771
+ }
772
+ tmp_log.debug(f"req {str(req)}")
773
+ ret = c.add_request(**req)
774
+ tmp_log.debug(f"got requestID={str(ret)}")
775
+ except Exception as e:
776
+ return False, f"iDDS error : {str(e)}"
777
+ return True, None
778
+
779
+
780
+ # launch
781
+
782
+
783
+ def launcher(commuChannel, taskBufferIF, ddmIF, vos=None, prodSourceLabels=None):
784
+ p = ContentsFeeder(commuChannel, taskBufferIF, ddmIF, vos, prodSourceLabels)
785
+ p.start()