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,629 @@
1
+ import copy
2
+ import datetime
3
+ import math
4
+ import random
5
+ import sys
6
+ import traceback
7
+
8
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
9
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
10
+
11
+ from pandajedi.jedicore import Interaction
12
+ from pandajedi.jedicore.MsgWrapper import MsgWrapper
13
+ from pandajedi.jedicore.ThreadUtils import (
14
+ ListWithLock,
15
+ MapWithLock,
16
+ ThreadPool,
17
+ WorkerThread,
18
+ )
19
+ from pandajedi.jedirefine import RefinerUtils
20
+ from pandaserver.dataservice import DataServiceUtils
21
+
22
+ from . import AtlasBrokerUtils
23
+ from .AtlasProdJobBroker import AtlasProdJobBroker
24
+ from .TaskBrokerBase import TaskBrokerBase
25
+
26
+ logger = PandaLogger().getLogger(__name__.split(".")[-1])
27
+
28
+
29
+ # brokerage for ATLAS production
30
+ class AtlasProdTaskBroker(TaskBrokerBase):
31
+ # constructor
32
+ def __init__(self, taskBufferIF, ddmIF):
33
+ TaskBrokerBase.__init__(self, taskBufferIF, ddmIF)
34
+
35
+ # main to check
36
+ def doCheck(self, taskSpecList):
37
+ return self.SC_SUCCEEDED, {}
38
+
39
+ # main to assign
40
+ def doBrokerage(self, inputList, vo, prodSourceLabel, workQueue, resource_name):
41
+ # list with a lock
42
+ inputListWorld = ListWithLock([])
43
+
44
+ # make logger
45
+ tmpLog = MsgWrapper(logger)
46
+ tmpLog.debug("start doBrokerage")
47
+
48
+ # return for failure
49
+ retTmpError = self.SC_FAILED
50
+ tmpLog.debug(f"vo={vo} label={prodSourceLabel} queue={workQueue.queue_name} resource_name={resource_name} nTasks={len(inputList)}")
51
+
52
+ # build the map with Remaining Work by priority
53
+ allRwMap = {}
54
+
55
+ # loop over all tasks and build the list of WORLD tasks. Nowadays all tasks are WORLD
56
+ for tmpJediTaskID, tmpInputList in inputList:
57
+ for taskSpec, cloudName, inputChunk in tmpInputList:
58
+ if taskSpec.useWorldCloud():
59
+ inputListWorld.append((taskSpec, inputChunk))
60
+
61
+ # broker WORLD tasks
62
+ if inputListWorld:
63
+ # thread pool
64
+ threadPool = ThreadPool()
65
+ # get full RW for WORLD
66
+ fullRWs = self.taskBufferIF.calculateWorldRWwithPrio_JEDI(vo, prodSourceLabel, None, None)
67
+ if fullRWs is None:
68
+ tmpLog.error("failed to calculate full WORLD RW")
69
+ return retTmpError
70
+ # get RW per priority
71
+ for taskSpec, inputChunk in inputListWorld:
72
+ if taskSpec.currentPriority not in allRwMap:
73
+ tmpRW = self.taskBufferIF.calculateWorldRWwithPrio_JEDI(vo, prodSourceLabel, workQueue, taskSpec.currentPriority)
74
+ if tmpRW is None:
75
+ tmpLog.error(f"failed to calculate RW with prio={taskSpec.currentPriority}")
76
+ return retTmpError
77
+ allRwMap[taskSpec.currentPriority] = tmpRW
78
+
79
+ # live counter for RWs
80
+ liveCounter = MapWithLock(allRwMap)
81
+ # make workers
82
+ ddmIF = self.ddmIF.getInterface(vo)
83
+ for iWorker in range(4):
84
+ thr = AtlasProdTaskBrokerThread(inputListWorld, threadPool, self.taskBufferIF, ddmIF, fullRWs, liveCounter, workQueue)
85
+ thr.start()
86
+ threadPool.join(60 * 10)
87
+ # return
88
+ tmpLog.debug("doBrokerage done")
89
+ return self.SC_SUCCEEDED
90
+
91
+
92
+ # thread for real worker
93
+ class AtlasProdTaskBrokerThread(WorkerThread):
94
+ # constructor
95
+ def __init__(self, inputList, threadPool, taskbufferIF, ddmIF, fullRW, prioRW, workQueue):
96
+ # initialize woker with no semaphore
97
+ WorkerThread.__init__(self, None, threadPool, logger)
98
+ # attributres
99
+ self.inputList = inputList
100
+ self.taskBufferIF = taskbufferIF
101
+ self.ddmIF = ddmIF
102
+ self.msgType = "taskbrokerage"
103
+ self.fullRW = fullRW
104
+ self.prioRW = prioRW
105
+ self.numTasks = 0
106
+ self.workQueue = workQueue
107
+ self.summaryList = None
108
+
109
+ # init summary list
110
+ def init_summary_list(self, header, comment, initial_list):
111
+ self.summaryList = []
112
+ self.summaryList.append(f"===== {header} =====")
113
+ if comment:
114
+ self.summaryList.append(comment)
115
+ self.summaryList.append(f"the number of initial candidates: {len(initial_list)}")
116
+
117
+ # dump summary
118
+ def dump_summary(self, tmp_log, final_candidates=None):
119
+ if not self.summaryList:
120
+ return
121
+ tmp_log.info("")
122
+ for m in self.summaryList:
123
+ tmp_log.info(m)
124
+ if not final_candidates:
125
+ final_candidates = []
126
+ tmp_log.info(f"the number of final candidates: {len(final_candidates)}")
127
+ tmp_log.info("")
128
+
129
+ # make summary
130
+ def add_summary_message(self, old_list, new_list, message):
131
+ if old_list and len(old_list) != len(new_list):
132
+ red = int(math.ceil(((len(old_list) - len(new_list)) * 100) / len(old_list)))
133
+ self.summaryList.append(f"{len(old_list):>5} -> {len(new_list):>3} candidates, {red:>3}% cut : {message}")
134
+
135
+ # post-process for errors
136
+ def post_process_for_error(self, task_spec, tmp_log, msg, dump_summary=True):
137
+ if dump_summary:
138
+ self.dump_summary(tmp_log)
139
+ tmp_log.error(msg)
140
+ task_spec.resetChangedList()
141
+ task_spec.setErrDiag(tmp_log.uploadLog(task_spec.jediTaskID))
142
+ self.taskBufferIF.updateTask_JEDI(task_spec, {"jediTaskID": task_spec.jediTaskID}, oldStatus=["assigning"], updateDEFT=False, setFrozenTime=False)
143
+
144
+ # main function
145
+ def runImpl(self):
146
+ # cutoff for disk in TB
147
+ diskThreshold = self.taskBufferIF.getConfigValue(self.msgType, f"DISK_THRESHOLD_{self.workQueue.queue_name}", "jedi", "atlas")
148
+ if diskThreshold is None:
149
+ diskThreshold = self.taskBufferIF.getConfigValue(self.msgType, "DISK_THRESHOLD", "jedi", "atlas")
150
+ if diskThreshold is None:
151
+ diskThreshold = 100
152
+ diskThreshold *= 1024
153
+ # cutoff for free disk in TB
154
+ free_disk_cutoff = self.taskBufferIF.getConfigValue(self.msgType, f"FREE_DISK_CUTOFF", "jedi", "atlas")
155
+ if free_disk_cutoff is None:
156
+ free_disk_cutoff = 1000
157
+ # dataset type to ignore file availability check
158
+ datasetTypeToSkipCheck = ["log"]
159
+ # thresholds for data availability check
160
+ thrInputSize = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_SIZE_THRESHOLD", "jedi", "atlas")
161
+ if thrInputSize is None:
162
+ thrInputSize = 1
163
+ thrInputSize *= 1024 * 1024 * 1024
164
+ thrInputNum = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_NUM_THRESHOLD", "jedi", "atlas")
165
+ if thrInputNum is None:
166
+ thrInputNum = 100
167
+ thrInputSizeFrac = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_SIZE_FRACTION", "jedi", "atlas")
168
+ if thrInputSizeFrac is None:
169
+ thrInputSizeFrac = 10
170
+ thrInputSizeFrac = float(thrInputSizeFrac) / 100
171
+ thrInputNumFrac = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_NUM_FRACTION", "jedi", "atlas")
172
+ if thrInputNumFrac is None:
173
+ thrInputNumFrac = 10
174
+ thrInputNumFrac = float(thrInputNumFrac) / 100
175
+ cutOffRW = 50
176
+ negWeightTape = 0.001
177
+ minIoIntensityWithLD = self.taskBufferIF.getConfigValue(self.msgType, "MIN_IO_INTENSITY_WITH_LOCAL_DATA", "jedi", "atlas")
178
+ if minIoIntensityWithLD is None:
179
+ minIoIntensityWithLD = 200
180
+ minInputSizeWithLD = self.taskBufferIF.getConfigValue(self.msgType, "MIN_INPUT_SIZE_WITH_LOCAL_DATA", "jedi", "atlas")
181
+ if minInputSizeWithLD is None:
182
+ minInputSizeWithLD = 10000
183
+ maxTaskPrioWithLD = self.taskBufferIF.getConfigValue(self.msgType, "MAX_TASK_PRIO_WITH_LOCAL_DATA", "jedi", "atlas")
184
+ if maxTaskPrioWithLD is None:
185
+ maxTaskPrioWithLD = 800
186
+ # ignore data locality once the period passes (in days)
187
+ data_location_check_period = self.taskBufferIF.getConfigValue(self.msgType, "DATA_LOCATION_CHECK_PERIOD", "jedi", "atlas")
188
+ if not data_location_check_period:
189
+ data_location_check_period = 7
190
+ # main
191
+ lastJediTaskID = None
192
+ siteMapper = self.taskBufferIF.get_site_mapper()
193
+ while True:
194
+ try:
195
+ taskInputList = self.inputList.get(1)
196
+ # no more datasets
197
+ if len(taskInputList) == 0:
198
+ self.logger.debug(f"{self.__class__.__name__} terminating after processing {self.numTasks} tasks since no more inputs ")
199
+ return
200
+ # loop over all tasks
201
+ for taskSpec, inputChunk in taskInputList:
202
+ lastJediTaskID = taskSpec.jediTaskID
203
+ # make logger
204
+ tmpLog = MsgWrapper(self.logger, f"<jediTaskID={taskSpec.jediTaskID}>", monToken=f"jediTaskID={taskSpec.jediTaskID}")
205
+ tmpLog.debug("start")
206
+ tmpLog.info(f"thrInputSize:{thrInputSize} thrInputNum:{thrInputNum} thrInputSizeFrac:{thrInputSizeFrac} thrInputNumFrac:{thrInputNumFrac}")
207
+ tmpLog.info(f"full-chain:{taskSpec.get_full_chain()} ioIntensity={taskSpec.ioIntensity}")
208
+ # read task parameters
209
+ try:
210
+ taskParam = self.taskBufferIF.getTaskParamsWithID_JEDI(taskSpec.jediTaskID)
211
+ taskParamMap = RefinerUtils.decodeJSON(taskParam)
212
+ except Exception:
213
+ tmpLog.error("failed to read task params")
214
+ taskSpec.resetChangedList()
215
+ taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
216
+ self.taskBufferIF.updateTask_JEDI(
217
+ taskSpec, {"jediTaskID": taskSpec.jediTaskID}, oldStatus=["assigning"], updateDEFT=False, setFrozenTime=False
218
+ )
219
+ continue
220
+ # RW
221
+ taskRW = self.taskBufferIF.calculateTaskWorldRW_JEDI(taskSpec.jediTaskID)
222
+ # get nuclei
223
+ nucleusList = copy.copy(siteMapper.nuclei)
224
+ if taskSpec.get_full_chain():
225
+ # use satellites with bareNucleus as nuclei for full chain
226
+ for tmpNucleus, tmpNucleusSpec in siteMapper.satellites.items():
227
+ if tmpNucleusSpec.get_bare_nucleus_mode():
228
+ nucleusList[tmpNucleus] = tmpNucleusSpec
229
+ # init summary list
230
+ self.init_summary_list("Task brokerage summary", None, nucleusList)
231
+ if taskSpec.nucleus in siteMapper.nuclei:
232
+ candidateNucleus = taskSpec.nucleus
233
+ elif taskSpec.nucleus in siteMapper.satellites:
234
+ nucleusList = siteMapper.satellites
235
+ candidateNucleus = taskSpec.nucleus
236
+ else:
237
+ tmpLog.info(f"got {len(nucleusList)} candidates")
238
+ ######################################
239
+ # check data
240
+ dataset_availability_info = {}
241
+ to_skip = False
242
+ for datasetSpec in inputChunk.getDatasets():
243
+ # only for real datasets
244
+ if datasetSpec.isPseudo():
245
+ continue
246
+ # ignore DBR
247
+ if DataServiceUtils.isDBR(datasetSpec.datasetName):
248
+ continue
249
+ # skip locality check
250
+ if DataServiceUtils.getDatasetType(datasetSpec.datasetName) in datasetTypeToSkipCheck:
251
+ continue
252
+ # primary only
253
+ if taskParamMap.get("taskBrokerOnMaster") is True and not datasetSpec.isMaster():
254
+ continue
255
+ # use deep scan for primary dataset unless data carousel
256
+ if datasetSpec.isMaster() and not taskSpec.inputPreStaging():
257
+ deepScan = True
258
+ else:
259
+ deepScan = False
260
+ # get nuclei where data is available
261
+ tmp_status, tmp_data_map, remote_source_available = AtlasBrokerUtils.getNucleiWithData(
262
+ siteMapper, self.ddmIF, datasetSpec.datasetName, list(nucleusList.keys()), deepScan
263
+ )
264
+ if tmp_status != Interaction.SC_SUCCEEDED:
265
+ self.post_process_for_error(taskSpec, tmpLog, f"failed to get nuclei where data is available, since {tmp_data_map}", False)
266
+ to_skip = True
267
+ break
268
+ # sum
269
+ for tmpNucleus, tmpVals in tmp_data_map.items():
270
+ if tmpNucleus not in dataset_availability_info:
271
+ dataset_availability_info[tmpNucleus] = tmpVals
272
+ else:
273
+ dataset_availability_info[tmpNucleus] = dict(
274
+ (k, v + tmpVals[k]) for (k, v) in dataset_availability_info[tmpNucleus].items() if not isinstance(v, bool)
275
+ )
276
+ # set remote_source_available to True if any is readable over WAN
277
+ if tmpVals.get("can_be_remote_source"):
278
+ dataset_availability_info[tmpNucleus]["can_be_remote_source"] = True
279
+ if not remote_source_available:
280
+ self.post_process_for_error(
281
+ taskSpec, tmpLog, f"dataset={datasetSpec.datasetName} is incomplete/missing at online endpoints with read_wan=ON", False
282
+ )
283
+ to_skip = True
284
+ break
285
+ if to_skip:
286
+ continue
287
+ ######################################
288
+ # check status
289
+ newNucleusList = {}
290
+ oldNucleusList = copy.copy(nucleusList)
291
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
292
+ if tmpNucleusSpec.state not in ["ACTIVE"]:
293
+ tmpLog.info(f" skip nucleus={tmpNucleus} due to status={tmpNucleusSpec.state} criteria=-status")
294
+ else:
295
+ newNucleusList[tmpNucleus] = tmpNucleusSpec
296
+ nucleusList = newNucleusList
297
+ tmpLog.info(f"{len(nucleusList)} candidates passed status check")
298
+ self.add_summary_message(oldNucleusList, nucleusList, "status check")
299
+ if not nucleusList:
300
+ self.post_process_for_error(taskSpec, tmpLog, "no candidates")
301
+ continue
302
+ ######################################
303
+ # check status of transfer backlog
304
+ t1Weight = taskSpec.getT1Weight()
305
+ if t1Weight < 0:
306
+ tmpLog.info("skip transfer backlog check due to negative T1Weight")
307
+ else:
308
+ newNucleusList = {}
309
+ oldNucleusList = copy.copy(nucleusList)
310
+ backlogged_nuclei = self.taskBufferIF.getBackloggedNuclei()
311
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
312
+ if tmpNucleus in backlogged_nuclei:
313
+ tmpLog.info(f" skip nucleus={tmpNucleus} due to long transfer backlog criteria=-transfer_backlog")
314
+ else:
315
+ newNucleusList[tmpNucleus] = tmpNucleusSpec
316
+ nucleusList = newNucleusList
317
+ tmpLog.info(f"{len(nucleusList)} candidates passed transfer backlog check")
318
+ self.add_summary_message(oldNucleusList, nucleusList, "transfer backlog check")
319
+ if not nucleusList:
320
+ self.post_process_for_error(taskSpec, tmpLog, "no candidates")
321
+ continue
322
+ ######################################
323
+ # check endpoint
324
+ fractionFreeSpace = {}
325
+ newNucleusList = {}
326
+ oldNucleusList = copy.copy(nucleusList)
327
+ tmpStat, tmpDatasetSpecList = self.taskBufferIF.getDatasetsWithJediTaskID_JEDI(taskSpec.jediTaskID, ["output", "log"])
328
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
329
+ to_skip = False
330
+ origNucleusSpec = tmpNucleusSpec
331
+ for tmpDatasetSpec in tmpDatasetSpecList:
332
+ tmpNucleusSpec = origNucleusSpec
333
+ # use secondary nucleus for full-chain if defined
334
+ if taskSpec.get_full_chain() and tmpNucleusSpec.get_secondary_nucleus():
335
+ tmpNucleusSpec = siteMapper.getNucleus(tmpNucleusSpec.get_secondary_nucleus())
336
+ # ignore distributed datasets
337
+ if DataServiceUtils.getDistributedDestination(tmpDatasetSpec.storageToken) is not None:
338
+ continue
339
+ # get endpoint with the pattern
340
+ tmpEP = tmpNucleusSpec.getAssociatedEndpoint(tmpDatasetSpec.storageToken)
341
+ tmp_ddm_endpoint_name = tmpEP["ddm_endpoint_name"]
342
+ if tmpEP is None:
343
+ tmpLog.info(f" skip nucleus={tmpNucleus} since no endpoint with {tmpDatasetSpec.storageToken} criteria=-match")
344
+ to_skip = True
345
+ break
346
+ # check blacklist
347
+ read_wan_status = tmpEP["detailed_status"].get("read_wan")
348
+ if read_wan_status in ["OFF", "TEST"]:
349
+ tmpLog.info(
350
+ f" skip nucleus={tmpNucleus} since {tmp_ddm_endpoint_name} has read_wan={read_wan_status} criteria=-source_blacklist"
351
+ )
352
+ to_skip = True
353
+ break
354
+ write_wan_status = tmpEP["detailed_status"].get("write_wan")
355
+ if write_wan_status in ["OFF", "TEST"]:
356
+ tmpLog.info(
357
+ f" skip nucleus={tmpNucleus} since {tmp_ddm_endpoint_name} has write_wan={read_wan_status} criteria=-destination_blacklist"
358
+ )
359
+ to_skip = True
360
+ break
361
+ # check space
362
+ tmpSpaceSize = 0
363
+ if tmpEP["space_free"]:
364
+ tmpSpaceSize += tmpEP["space_free"]
365
+ if tmpEP["space_expired"]:
366
+ tmpSpaceSize += tmpEP["space_expired"]
367
+ tmpSpaceToUse = 0
368
+ if tmpNucleus in self.fullRW:
369
+ # 0.25GB per cpuTime/corePower/day
370
+ tmpSpaceToUse = int(self.fullRW[tmpNucleus] / 10 / 24 / 3600 * 0.25)
371
+ if tmpSpaceSize - tmpSpaceToUse < diskThreshold:
372
+ tmpLog.info(
373
+ " skip nucleus={0} since disk shortage (free {1} TB - reserved {2} TB < thr {3} TB) at endpoint {4} criteria=-space".format(
374
+ tmpNucleus, tmpSpaceSize // 1024, tmpSpaceToUse // 1024, diskThreshold // 1024, tmpEP["ddm_endpoint_name"]
375
+ )
376
+ )
377
+ to_skip = True
378
+ break
379
+ # keep fraction of free space
380
+ if tmpNucleus not in fractionFreeSpace:
381
+ fractionFreeSpace[tmpNucleus] = {"total": 0, "free": 0}
382
+ try:
383
+ tmpOld = float(fractionFreeSpace[tmpNucleus]["free"]) / float(fractionFreeSpace[tmpNucleus]["total"])
384
+ except Exception:
385
+ tmpOld = None
386
+ try:
387
+ tmpNew = float(tmpSpaceSize - tmpSpaceToUse) / float(tmpEP["space_total"])
388
+ except Exception:
389
+ tmpNew = None
390
+ if tmpNew is not None and (tmpOld is None or tmpNew < tmpOld):
391
+ fractionFreeSpace[tmpNucleus] = {"total": tmpEP["space_total"], "free": tmpSpaceSize - tmpSpaceToUse}
392
+ if not to_skip:
393
+ newNucleusList[tmpNucleus] = origNucleusSpec
394
+ nucleusList = newNucleusList
395
+ tmpLog.info(f"{len(nucleusList)} candidates passed endpoint check with DISK_THRESHOLD={diskThreshold // 1024} TB")
396
+ self.add_summary_message(oldNucleusList, nucleusList, "storage endpoint check")
397
+ if not nucleusList:
398
+ self.post_process_for_error(taskSpec, tmpLog, "no candidates")
399
+ continue
400
+ ######################################
401
+ # ability to execute jobs
402
+ newNucleusList = {}
403
+ oldNucleusList = copy.copy(nucleusList)
404
+ # get all panda sites
405
+ tmpSiteList = []
406
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
407
+ tmpSiteList += tmpNucleusSpec.allPandaSites
408
+ tmpSiteList = list(set(tmpSiteList))
409
+ tmpLog.debug("===== start for job check")
410
+ jobBroker = AtlasProdJobBroker(self.ddmIF, self.taskBufferIF)
411
+ tmp_status, tmp_data_map = jobBroker.doBrokerage(taskSpec, taskSpec.cloud, inputChunk, None, True, tmpSiteList, tmpLog)
412
+ tmpLog.debug("===== done for job check")
413
+ if tmp_status != Interaction.SC_SUCCEEDED:
414
+ tmpLog.error("no sites can run jobs")
415
+ taskSpec.resetChangedList()
416
+ taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
417
+ self.taskBufferIF.updateTask_JEDI(
418
+ taskSpec, {"jediTaskID": taskSpec.jediTaskID}, oldStatus=["assigning"], updateDEFT=False, setFrozenTime=False
419
+ )
420
+ continue
421
+ okNuclei = set()
422
+ for tmpSite in tmp_data_map:
423
+ siteSpec = siteMapper.getSite(tmpSite)
424
+ okNuclei.add(siteSpec.pandasite)
425
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
426
+ if tmpNucleus in okNuclei:
427
+ newNucleusList[tmpNucleus] = tmpNucleusSpec
428
+ else:
429
+ tmpLog.info(f" skip nucleus={tmpNucleus} due to missing ability to run jobs criteria=-job")
430
+ nucleusList = newNucleusList
431
+ tmpLog.info(f"{len(nucleusList)} candidates passed job check")
432
+ self.add_summary_message(oldNucleusList, nucleusList, "job check")
433
+ if not nucleusList:
434
+ self.post_process_for_error(taskSpec, tmpLog, "no candidates")
435
+ continue
436
+ ######################################
437
+ # data locality
438
+ time_now = naive_utcnow()
439
+ if taskSpec.frozenTime and time_now - taskSpec.frozenTime > datetime.timedelta(days=data_location_check_period):
440
+ tmpLog.info(f"disabled data check since the task was in assigning for " f"{data_location_check_period} days")
441
+ else:
442
+ dataset_availability_info = {k: v for k, v in dataset_availability_info.items() if k in nucleusList}
443
+ if dataset_availability_info != {}:
444
+ newNucleusList = {}
445
+ oldNucleusList = copy.copy(nucleusList)
446
+ # skip if no data
447
+ skipMsgList = []
448
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
449
+ if taskSpec.inputPreStaging() and dataset_availability_info[tmpNucleus]["ava_num_any"] > 0:
450
+ # use incomplete replicas for data carousel since the completeness is guaranteed
451
+ newNucleusList[tmpNucleus] = tmpNucleusSpec
452
+ elif (
453
+ dataset_availability_info[tmpNucleus]["tot_size"] > thrInputSize
454
+ and dataset_availability_info[tmpNucleus]["ava_size_any"]
455
+ < dataset_availability_info[tmpNucleus]["tot_size"] * thrInputSizeFrac
456
+ ):
457
+ tmpMsg = " skip nucleus={0} due to insufficient input size {1}B < {2}*{3} criteria=-insize".format(
458
+ tmpNucleus,
459
+ dataset_availability_info[tmpNucleus]["ava_size_any"],
460
+ dataset_availability_info[tmpNucleus]["tot_size"],
461
+ thrInputSizeFrac,
462
+ )
463
+ skipMsgList.append(tmpMsg)
464
+ elif (
465
+ dataset_availability_info[tmpNucleus]["tot_num"] > thrInputNum
466
+ and dataset_availability_info[tmpNucleus]["ava_num_any"]
467
+ < dataset_availability_info[tmpNucleus]["tot_num"] * thrInputNumFrac
468
+ ):
469
+ tmpMsg = " skip nucleus={0} due to short number of input files {1} < {2}*{3} criteria=-innum".format(
470
+ tmpNucleus,
471
+ dataset_availability_info[tmpNucleus]["ava_num_any"],
472
+ dataset_availability_info[tmpNucleus]["tot_num"],
473
+ thrInputNumFrac,
474
+ )
475
+ skipMsgList.append(tmpMsg)
476
+ else:
477
+ newNucleusList[tmpNucleus] = tmpNucleusSpec
478
+ totInputSize = list(dataset_availability_info.values())[0]["tot_size"] / 1024 / 1024 / 1024
479
+ data_locality_check_str = (
480
+ f"(ioIntensity ({taskSpec.ioIntensity}) is None or less than {minIoIntensityWithLD} kBPerS "
481
+ f"and input size ({int(totInputSize)} GB) is less than "
482
+ f"{minInputSizeWithLD}) "
483
+ f"or (task.currentPriority ("
484
+ f"{taskSpec.currentPriority}) is higher than or equal to {maxTaskPrioWithLD}) "
485
+ f"or the task is in assigning for {data_location_check_period} days"
486
+ )
487
+ if len(newNucleusList) > 0:
488
+ nucleusList = newNucleusList
489
+ for tmpMsg in skipMsgList:
490
+ tmpLog.info(tmpMsg)
491
+ elif (
492
+ (taskSpec.ioIntensity is None or taskSpec.ioIntensity <= minIoIntensityWithLD) and totInputSize <= minInputSizeWithLD
493
+ ) or taskSpec.currentPriority >= maxTaskPrioWithLD:
494
+ dataset_availability_info = {}
495
+ tmpLog.info(f" disable data locality check since no nucleus has input data, {data_locality_check_str}")
496
+ else:
497
+ # no candidate + unavoidable data locality check
498
+ nucleusList = newNucleusList
499
+ for tmpMsg in skipMsgList:
500
+ tmpLog.info(tmpMsg)
501
+ tmpLog.info(f" the following conditions required to disable data locality check: {data_locality_check_str}")
502
+ tmpLog.info(f"{len(nucleusList)} candidates passed data check")
503
+ self.add_summary_message(oldNucleusList, nucleusList, "data check")
504
+ if not nucleusList:
505
+ self.post_process_for_error(taskSpec, tmpLog, "no candidates")
506
+ continue
507
+ ######################################
508
+ # check for full chain
509
+ newNucleusList = {}
510
+ oldNucleusList = copy.copy(nucleusList)
511
+ parent_full_chain = False
512
+ if taskSpec.get_full_chain() and taskSpec.jediTaskID != taskSpec.parent_tid:
513
+ tmpStat, parentTaskSpec = self.taskBufferIF.getTaskWithID_JEDI(taskSpec.parent_tid, False)
514
+
515
+ if not tmpStat or parentTaskSpec is None:
516
+ self.post_process_for_error(taskSpec, tmpLog, f"failed to get parent taskSpec with jediTaskID={taskSpec.parent_tid}", False)
517
+ continue
518
+ parent_full_chain = parentTaskSpec.check_full_chain_with_nucleus(siteMapper.getNucleus(parentTaskSpec.nucleus))
519
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
520
+ # nucleus to run only full-chain tasks
521
+ if tmpNucleusSpec.get_bare_nucleus_mode() == "only" and taskSpec.get_full_chain() is None:
522
+ tmpLog.info(f" skip nucleus={tmpNucleus} since only full-chain tasks are allowed criteria=-full_chain")
523
+ continue
524
+ # task requirement to run on nucleus with full-chain support
525
+ if tmpNucleusSpec.get_bare_nucleus_mode() is None and (
526
+ taskSpec.check_full_chain_with_mode("only") or taskSpec.check_full_chain_with_mode("require")
527
+ ):
528
+ tmpLog.info(f" skip nucleus={tmpNucleus} due to lack of full-chain capability criteria=-full_chain")
529
+ continue
530
+ # check parent task
531
+ if taskSpec.get_full_chain() and parent_full_chain:
532
+ if tmpNucleus != parentTaskSpec.nucleus:
533
+ tmpLog.info(f" skip nucleus={tmpNucleus} since the parent of the full-chain ran elsewhere criteria=-full_chain")
534
+ continue
535
+ newNucleusList[tmpNucleus] = tmpNucleusSpec
536
+ nucleusList = newNucleusList
537
+ tmpLog.info(f"{len(nucleusList)} candidates passed full-chain check")
538
+ self.add_summary_message(oldNucleusList, nucleusList, "full-chain check")
539
+ if not nucleusList:
540
+ self.post_process_for_error(taskSpec, tmpLog, "no candidates")
541
+ continue
542
+ self.dump_summary(tmpLog, nucleusList)
543
+ ######################################
544
+ # weight
545
+ self.prioRW.acquire()
546
+ nucleusRW = self.prioRW[taskSpec.currentPriority]
547
+ self.prioRW.release()
548
+ totalWeight = 0
549
+ nucleusweights = []
550
+ for tmpNucleus, tmpNucleusSpec in nucleusList.items():
551
+ if tmpNucleus not in nucleusRW:
552
+ nucleusRW[tmpNucleus] = 0
553
+ wStr = "1"
554
+ # with RW
555
+ if tmpNucleus in nucleusRW and nucleusRW[tmpNucleus] and nucleusRW[tmpNucleus] >= cutOffRW:
556
+ weight = 1 / float(nucleusRW[tmpNucleus])
557
+ wStr += f"/( RW={nucleusRW[tmpNucleus]} )"
558
+ else:
559
+ weight = 1
560
+ wStr += f"/(1 : RW={nucleusRW[tmpNucleus]}<{cutOffRW})"
561
+ # with data
562
+ if dataset_availability_info != {}:
563
+ if dataset_availability_info[tmpNucleus]["tot_size"] > 0:
564
+ # use input size only when high IO intensity
565
+ if taskSpec.ioIntensity is None or taskSpec.ioIntensity > minIoIntensityWithLD:
566
+ weight *= float(dataset_availability_info[tmpNucleus]["ava_size_any"])
567
+ weight /= float(dataset_availability_info[tmpNucleus]["tot_size"])
568
+ wStr += f"* ( available_input_size_DISKTAPE={dataset_availability_info[tmpNucleus]['ava_size_any']} )"
569
+ wStr += f"/ ( total_input_size={dataset_availability_info[tmpNucleus]['tot_size']} )"
570
+ # negative weight for tape
571
+ if dataset_availability_info[tmpNucleus]["ava_size_any"] > dataset_availability_info[tmpNucleus]["ava_size_disk"]:
572
+ weight *= negWeightTape
573
+ wStr += f"*( weight_TAPE={negWeightTape} )"
574
+ # fraction of free space
575
+ if tmpNucleus in fractionFreeSpace:
576
+ try:
577
+ tmpFrac = float(fractionFreeSpace[tmpNucleus]["free"]) / float(fractionFreeSpace[tmpNucleus]["total"])
578
+ weight *= tmpFrac
579
+ wStr += f"*( free_space={fractionFreeSpace[tmpNucleus]['free'] // 1024} TB )/( total_space={fractionFreeSpace[tmpNucleus]['total'] // 1024} TB )"
580
+ free_disk_in_tb = fractionFreeSpace[tmpNucleus]["free"] // 1024
581
+ free_disk_term = min(free_disk_cutoff, free_disk_in_tb)
582
+ weight *= free_disk_term
583
+ wStr += f"*min( free_space={free_disk_in_tb} TB, FREE_DISK_CUTOFF={free_disk_cutoff} TB )"
584
+ except Exception:
585
+ pass
586
+ tmpLog.info(f" use nucleus={tmpNucleus} weight={weight} {wStr} criteria=+use")
587
+ totalWeight += weight
588
+ nucleusweights.append((tmpNucleus, weight))
589
+ ######################################
590
+ # final selection
591
+ tgtWeight = random.uniform(0, totalWeight)
592
+ candidateNucleus = None
593
+ for tmpNucleus, weight in nucleusweights:
594
+ tgtWeight -= weight
595
+ if tgtWeight <= 0:
596
+ candidateNucleus = tmpNucleus
597
+ break
598
+ if candidateNucleus is None:
599
+ candidateNucleus = nucleusweights[-1][0]
600
+ ######################################
601
+ # update
602
+ nucleusSpec = nucleusList[candidateNucleus]
603
+ # get output/log datasets
604
+ tmpStat, tmpDatasetSpecs = self.taskBufferIF.getDatasetsWithJediTaskID_JEDI(taskSpec.jediTaskID, ["output", "log"])
605
+ # get destinations
606
+ retMap = {taskSpec.jediTaskID: AtlasBrokerUtils.getDictToSetNucleus(nucleusSpec, tmpDatasetSpecs)}
607
+ tmp_ret = self.taskBufferIF.setCloudToTasks_JEDI(retMap)
608
+ tmpLog.info(f" set nucleus={candidateNucleus} with {tmp_ret} criteria=+set")
609
+ if tmp_ret:
610
+ tmpMsg = "set task_status=ready"
611
+ tmpLog.sendMsg(tmpMsg, self.msgType)
612
+ # update RW table
613
+ self.prioRW.acquire()
614
+ for prio, rwMap in self.prioRW.items():
615
+ if prio > taskSpec.currentPriority:
616
+ continue
617
+ if candidateNucleus in rwMap:
618
+ if not rwMap[candidateNucleus]:
619
+ rwMap[candidateNucleus] = 0
620
+ rwMap[candidateNucleus] += taskRW
621
+ else:
622
+ rwMap[candidateNucleus] = taskRW
623
+ self.prioRW.release()
624
+ except Exception:
625
+ errtype, errvalue = sys.exc_info()[:2]
626
+ errMsg = f"{self.__class__.__name__}.runImpl() failed with {errtype.__name__} {errvalue} "
627
+ errMsg += f"lastJediTaskID={lastJediTaskID} "
628
+ errMsg += traceback.format_exc()
629
+ logger.error(errMsg)