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,648 @@
1
+ import os
2
+ import re
3
+ import socket
4
+ import sys
5
+ import traceback
6
+
7
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
8
+
9
+ from pandajedi.jedicore.MsgWrapper import MsgWrapper
10
+ from pandaserver.dataservice.activator import Activator
11
+
12
+ from .TypicalWatchDogBase import TypicalWatchDogBase
13
+
14
+ logger = PandaLogger().getLogger(__name__.split(".")[-1])
15
+
16
+
17
+ # watchdog for ATLAS analysis
18
+ class AtlasAnalWatchDog(TypicalWatchDogBase):
19
+ # constructor
20
+ def __init__(self, taskBufferIF, ddmIF):
21
+ TypicalWatchDogBase.__init__(self, taskBufferIF, ddmIF)
22
+ self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}-dog"
23
+
24
+ # main
25
+ def doAction(self):
26
+ # get logger
27
+ orig_tmp_log = MsgWrapper(logger)
28
+ try:
29
+ orig_tmp_log.debug("start")
30
+ # handle waiting jobs
31
+ self.doForWaitingJobs()
32
+ # throttle tasks if so many prestaging requests
33
+ self.doForPreStaging()
34
+ # priority massage
35
+ self.doForPriorityMassage()
36
+ # redo stalled analysis jobs
37
+ self.doForRedoStalledJobs()
38
+ # task share and priority boost
39
+ self.doForTaskBoost()
40
+ # action to set scout job data w/o scouts
41
+ self.doActionToSetScoutJobData(orig_tmp_log)
42
+ # periodic action
43
+ self.do_periodic_action()
44
+ except Exception as e:
45
+ orig_tmp_log.error(f"failed with {str(e)} {traceback.format_exc()}")
46
+ # return
47
+ orig_tmp_log.debug("done")
48
+ return self.SC_SUCCEEDED
49
+
50
+ # handle waiting jobs
51
+ def doForWaitingJobs(self):
52
+ try:
53
+ tmpLog = MsgWrapper(logger, "doForWaitingJobs label=user")
54
+ # lock
55
+ got_lock = self.taskBufferIF.lockProcess_JEDI(
56
+ vo=self.vo,
57
+ prodSourceLabel=self.prodSourceLabel,
58
+ cloud=None,
59
+ workqueue_id=None,
60
+ resource_name=None,
61
+ component="AtlasAnalWatchDog.doForWaitingJobs",
62
+ pid=self.pid,
63
+ timeLimit=0.5,
64
+ )
65
+ if not got_lock:
66
+ tmpLog.debug("locked by another process. Skipped")
67
+ return
68
+ # check every 60 min
69
+ checkInterval = 60
70
+ # get lib.tgz for waiting jobs
71
+ libList = self.taskBufferIF.getLibForWaitingRunJob_JEDI(self.vo, self.prodSourceLabel, checkInterval)
72
+ tmpLog.debug(f"got {len(libList)} lib.tgz files")
73
+ # activate or kill orphan jobs which were submitted to use lib.tgz when the lib.tgz was being produced
74
+ for prodUserName, datasetName, tmpFileSpec in libList:
75
+ tmpLog = MsgWrapper(logger, f"< #ATM #KV doForWaitingJobs jediTaskID={tmpFileSpec.jediTaskID} label=user >")
76
+ tmpLog.debug("start")
77
+ # check status of lib.tgz
78
+ if tmpFileSpec.status == "failed":
79
+ # get buildJob
80
+ pandaJobSpecs = self.taskBufferIF.peekJobs([tmpFileSpec.PandaID], fromDefined=False, fromActive=False, fromWaiting=False)
81
+ pandaJobSpec = pandaJobSpecs[0]
82
+ if pandaJobSpec is not None:
83
+ # kill
84
+ self.taskBufferIF.updateJobs([pandaJobSpec], False)
85
+ tmpLog.debug(f' action=killed_downstream_jobs for user="{prodUserName}" with libDS={datasetName}')
86
+ else:
87
+ # PandaJobSpec not found
88
+ tmpLog.error(f' cannot find PandaJobSpec for user="{prodUserName}" with PandaID={tmpFileSpec.PandaID}')
89
+ elif tmpFileSpec.status == "finished":
90
+ # set metadata
91
+ self.taskBufferIF.setGUIDs(
92
+ [
93
+ {
94
+ "guid": tmpFileSpec.GUID,
95
+ "lfn": tmpFileSpec.lfn,
96
+ "checksum": tmpFileSpec.checksum,
97
+ "fsize": tmpFileSpec.fsize,
98
+ "scope": tmpFileSpec.scope,
99
+ }
100
+ ]
101
+ )
102
+ # get lib dataset
103
+ dataset = self.taskBufferIF.queryDatasetWithMap({"name": datasetName})
104
+ if dataset is not None:
105
+ # activate jobs
106
+ aThr = Activator(self.taskBufferIF, dataset)
107
+ aThr.run()
108
+ tmpLog.debug(f' action=activated_downstream_jobs for user="{prodUserName}" with libDS={datasetName}')
109
+ else:
110
+ # datasetSpec not found
111
+ tmpLog.error(f' cannot find datasetSpec for user="{prodUserName}" with libDS={datasetName}')
112
+ else:
113
+ # lib.tgz is not ready
114
+ tmpLog.debug(f' keep waiting for user="{prodUserName}" libDS={datasetName}')
115
+ except Exception:
116
+ errtype, errvalue = sys.exc_info()[:2]
117
+ tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
118
+
119
+ # throttle tasks if so many prestaging requests
120
+ def doForPreStaging(self):
121
+ try:
122
+ tmpLog = MsgWrapper(logger, " #ATM #KV doForPreStaging label=user")
123
+ tmpLog.debug("start")
124
+ # lock
125
+ got_lock = self.taskBufferIF.lockProcess_JEDI(
126
+ vo=self.vo,
127
+ prodSourceLabel=self.prodSourceLabel,
128
+ cloud=None,
129
+ workqueue_id=None,
130
+ resource_name=None,
131
+ component="AtlasAnalWatchDog.doForPreStaging",
132
+ pid=self.pid,
133
+ timeLimit=0.5,
134
+ )
135
+ if not got_lock:
136
+ tmpLog.debug("locked by another process. Skipped")
137
+ return
138
+ # get throttled users
139
+ thrUserTasks = self.taskBufferIF.getThrottledUsersTasks_JEDI(self.vo, self.prodSourceLabel)
140
+ # get dispatch datasets
141
+ dispUserTasks = self.taskBufferIF.getDispatchDatasetsPerUser(self.vo, self.prodSourceLabel, True, True)
142
+ # max size of prestaging requests in GB
143
+ maxPrestaging = self.taskBufferIF.getConfigValue("anal_watchdog", "USER_PRESTAGE_LIMIT", "jedi", "atlas")
144
+ if maxPrestaging is None:
145
+ maxPrestaging = 1024
146
+ # max size of transfer requests in GB
147
+ maxTransfer = self.taskBufferIF.getConfigValue("anal_watchdog", "USER_TRANSFER_LIMIT", "jedi", "atlas")
148
+ if maxTransfer is None:
149
+ maxTransfer = 1024
150
+ # throttle interval
151
+ thrInterval = 120
152
+ # loop over all users
153
+ for userName, userDict in dispUserTasks.items():
154
+ # loop over all transfer types
155
+ for transferType, maxSize in [("prestaging", maxPrestaging), ("transfer", maxTransfer)]:
156
+ if transferType not in userDict:
157
+ continue
158
+ userTotal = int(userDict[transferType]["size"] / 1024)
159
+ tmpLog.debug(f"user={userName} {transferType} total={userTotal} GB")
160
+ # too large
161
+ if userTotal > maxSize:
162
+ tmpLog.debug(f"user={userName} has too large {transferType} total={userTotal} GB > limit={maxSize} GB")
163
+ # throttle tasks
164
+ for taskID in userDict[transferType]["tasks"]:
165
+ if userName not in thrUserTasks or transferType not in thrUserTasks[userName] or taskID not in thrUserTasks[userName][transferType]:
166
+ tmpLog.debug(f"action=throttle_{transferType} jediTaskID={taskID} for user={userName}")
167
+ errDiag = f"throttled since transferring large data volume in total={userTotal}GB > limit={maxSize}GB type={transferType}"
168
+ self.taskBufferIF.throttleTask_JEDI(taskID, thrInterval, errDiag)
169
+ # remove the user from the list
170
+ if userName in thrUserTasks and transferType in thrUserTasks[userName]:
171
+ del thrUserTasks[userName][transferType]
172
+ # release users
173
+ for userName, taskData in thrUserTasks.items():
174
+ for transferType, taskIDs in taskData.items():
175
+ tmpLog.debug(f"user={userName} release throttled tasks with {transferType}")
176
+ # unthrottle tasks
177
+ for taskID in taskIDs:
178
+ tmpLog.debug(f"action=release_{transferType} jediTaskID={taskID} for user={userName}")
179
+ self.taskBufferIF.release_task_on_hold(taskID, "throttled")
180
+ tmpLog.debug("done")
181
+ except Exception:
182
+ errtype, errvalue = sys.exc_info()[:2]
183
+ tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
184
+
185
+ # priority massage
186
+ def doForPriorityMassage(self):
187
+ tmpLog = MsgWrapper(logger, " #ATM #KV doForPriorityMassage label=user")
188
+ tmpLog.debug("start")
189
+ # lock
190
+ got_lock = self.taskBufferIF.lockProcess_JEDI(
191
+ vo=self.vo,
192
+ prodSourceLabel=self.prodSourceLabel,
193
+ cloud=None,
194
+ workqueue_id=None,
195
+ resource_name=None,
196
+ component="AtlasAnalWatchDog.doForPriorityMassage",
197
+ pid=self.pid,
198
+ timeLimit=6,
199
+ )
200
+ if not got_lock:
201
+ tmpLog.debug("locked by another process. Skipped")
202
+ return
203
+ try:
204
+ # get usage breakdown
205
+ usageBreakDownPerUser, usageBreakDownPerSite = self.taskBufferIF.getUsageBreakdown_JEDI(self.prodSourceLabel)
206
+ # get total number of users and running/done jobs
207
+ totalUsers = 0
208
+ totalRunDone = 0
209
+ usersTotalJobs = {}
210
+ usersTotalCores = {}
211
+ for prodUserName in usageBreakDownPerUser:
212
+ wgValMap = usageBreakDownPerUser[prodUserName]
213
+ for workingGroup in wgValMap:
214
+ siteValMap = wgValMap[workingGroup]
215
+ totalUsers += 1
216
+ for computingSite in siteValMap:
217
+ statValMap = siteValMap[computingSite]
218
+ totalRunDone += statValMap["rundone"]
219
+ usersTotalJobs.setdefault(prodUserName, {})
220
+ usersTotalJobs[prodUserName].setdefault(workingGroup, 0)
221
+ usersTotalJobs[prodUserName][workingGroup] += statValMap["running"]
222
+ usersTotalCores.setdefault(prodUserName, {})
223
+ usersTotalCores[prodUserName].setdefault(workingGroup, 0)
224
+ usersTotalCores[prodUserName][workingGroup] += statValMap["runcores"]
225
+ tmpLog.debug(f"total {totalUsers} users, {totalRunDone} RunDone jobs")
226
+ # skip if no user
227
+ if totalUsers == 0:
228
+ tmpLog.debug("no user. Skipped...")
229
+ return
230
+ # cap num of running jobs
231
+ tmpLog.debug("cap running jobs")
232
+ prodUserName = None
233
+ maxNumRunPerUser = self.taskBufferIF.getConfigValue("prio_mgr", "CAP_RUNNING_USER_JOBS")
234
+ maxNumRunPerGroup = self.taskBufferIF.getConfigValue("prio_mgr", "CAP_RUNNING_GROUP_JOBS")
235
+ maxNumCorePerUser = self.taskBufferIF.getConfigValue("prio_mgr", "CAP_RUNNING_USER_CORES")
236
+ maxNumCorePerGroup = self.taskBufferIF.getConfigValue("prio_mgr", "CAP_RUNNING_GROUP_CORES")
237
+ if maxNumRunPerUser is None:
238
+ maxNumRunPerUser = 10000
239
+ if maxNumRunPerGroup is None:
240
+ maxNumRunPerGroup = 10000
241
+ if maxNumCorePerUser is None:
242
+ maxNumCorePerUser = 10000
243
+ if maxNumCorePerGroup is None:
244
+ maxNumCorePerGroup = 10000
245
+ try:
246
+ throttledUsers = self.taskBufferIF.getThrottledUsers()
247
+ for prodUserName in usersTotalJobs:
248
+ for workingGroup in usersTotalJobs[prodUserName]:
249
+ tmpNumTotalJobs = usersTotalJobs[prodUserName][workingGroup]
250
+ tmpNumTotalCores = usersTotalCores[prodUserName][workingGroup]
251
+ if workingGroup is None:
252
+ maxNumRun = maxNumRunPerUser
253
+ maxNumCore = maxNumCorePerUser
254
+ else:
255
+ maxNumRun = maxNumRunPerGroup
256
+ maxNumCore = maxNumCorePerGroup
257
+ if tmpNumTotalJobs >= maxNumRun or tmpNumTotalCores >= maxNumCore:
258
+ # throttle user
259
+ tmpNumJobs = self.taskBufferIF.throttleUserJobs(prodUserName, workingGroup, get_dict=True)
260
+ if tmpNumJobs is not None:
261
+ for tmpJediTaskID, tmpNumJob in tmpNumJobs.items():
262
+ msg = (
263
+ 'throttled {} jobs in jediTaskID={} for user="{}" group={} ' "since too many running jobs ({} > {}) or cores ({} > {}) "
264
+ ).format(tmpNumJob, tmpJediTaskID, prodUserName, workingGroup, tmpNumTotalJobs, maxNumRun, tmpNumTotalCores, maxNumCore)
265
+ tmpLog.debug(msg)
266
+ tmpLog.sendMsg(msg, "userCap", msgLevel="warning")
267
+ elif tmpNumTotalJobs < maxNumRun * 0.9 and tmpNumTotalCores < maxNumCore * 0.9 and (prodUserName, workingGroup) in throttledUsers:
268
+ # unthrottle user
269
+ tmpNumJobs = self.taskBufferIF.unThrottleUserJobs(prodUserName, workingGroup, get_dict=True)
270
+ if tmpNumJobs is not None:
271
+ for tmpJediTaskID, tmpNumJob in tmpNumJobs.items():
272
+ msg = (
273
+ 'released {} jobs in jediTaskID={} for user="{}" group={} '
274
+ "since number of running jobs and cores are less than {} and {}"
275
+ ).format(tmpNumJob, tmpJediTaskID, prodUserName, workingGroup, maxNumRun, maxNumCore)
276
+ tmpLog.debug(msg)
277
+ tmpLog.sendMsg(msg, "userCap")
278
+ except Exception as e:
279
+ errStr = f"cap failed for {prodUserName} : {str(e)}"
280
+ errStr.strip()
281
+ errStr += traceback.format_exc()
282
+ tmpLog.error(errStr)
283
+ # to boost
284
+ tmpLog.debug("boost jobs")
285
+ # global average
286
+ globalAverageRunDone = float(totalRunDone) / float(totalUsers)
287
+ tmpLog.debug(f"global average: {globalAverageRunDone}")
288
+ # count the number of users and run/done jobs for each site
289
+ siteRunDone = {}
290
+ siteUsers = {}
291
+ for computingSite in usageBreakDownPerSite:
292
+ userValMap = usageBreakDownPerSite[computingSite]
293
+ for prodUserName in userValMap:
294
+ wgValMap = userValMap[prodUserName]
295
+ for workingGroup in wgValMap:
296
+ statValMap = wgValMap[workingGroup]
297
+ # count the number of users and running/done jobs
298
+ siteUsers.setdefault(computingSite, 0)
299
+ siteUsers[computingSite] += 1
300
+ siteRunDone.setdefault(computingSite, 0)
301
+ siteRunDone[computingSite] += statValMap["rundone"]
302
+ # get site average
303
+ tmpLog.debug("site average")
304
+ siteAverageRunDone = {}
305
+ for computingSite in siteRunDone:
306
+ nRunDone = siteRunDone[computingSite]
307
+ siteAverageRunDone[computingSite] = float(nRunDone) / float(siteUsers[computingSite])
308
+ tmpLog.debug(" %-25s : %s" % (computingSite, siteAverageRunDone[computingSite]))
309
+ # check if the number of user's jobs is lower than the average
310
+ for prodUserName in usageBreakDownPerUser:
311
+ wgValMap = usageBreakDownPerUser[prodUserName]
312
+ for workingGroup in wgValMap:
313
+ tmpLog.debug(f"---> {prodUserName} group={workingGroup}")
314
+ # count the number of running/done jobs
315
+ userTotalRunDone = 0
316
+ for computingSite in wgValMap[workingGroup]:
317
+ statValMap = wgValMap[workingGroup][computingSite]
318
+ userTotalRunDone += statValMap["rundone"]
319
+ # no priority boost when the number of jobs is higher than the average
320
+ if userTotalRunDone >= globalAverageRunDone:
321
+ tmpLog.debug(f"enough running {userTotalRunDone} > {globalAverageRunDone} (global average)")
322
+ continue
323
+ tmpLog.debug(f"user total:{userTotalRunDone} global average:{globalAverageRunDone}")
324
+ # check with site average
325
+ toBeBoostedSites = []
326
+ for computingSite in wgValMap[workingGroup]:
327
+ statValMap = wgValMap[workingGroup][computingSite]
328
+ # the number of running/done jobs is lower than the average and activated jobs are waiting
329
+ if statValMap["rundone"] >= siteAverageRunDone[computingSite]:
330
+ tmpLog.debug(f"enough running {statValMap['rundone']} > {siteAverageRunDone[computingSite]} (site average) at {computingSite}")
331
+ elif statValMap["activated"] == 0:
332
+ tmpLog.debug(f"no activated jobs at {computingSite}")
333
+ else:
334
+ toBeBoostedSites.append(computingSite)
335
+ # no boost is required
336
+ if toBeBoostedSites == []:
337
+ tmpLog.debug("no sites to be boosted")
338
+ continue
339
+
340
+ # set weight
341
+ totalW = 0
342
+ defaultW = 100
343
+ for _ in toBeBoostedSites:
344
+ totalW += defaultW
345
+
346
+ totalW = float(totalW)
347
+ # the total number of jobs to be boosted
348
+ numBoostedJobs = globalAverageRunDone - float(userTotalRunDone)
349
+ # get quota
350
+ quotaFactor = 1.0 + self.taskBufferIF.checkQuota(prodUserName)
351
+ tmpLog.debug(f"quota factor:{quotaFactor}")
352
+ # make priority boost
353
+ nJobsPerPrioUnit = 5
354
+ highestPrio = 1000
355
+ for computingSite in toBeBoostedSites:
356
+ weight = float(defaultW)
357
+ weight /= totalW
358
+ # the number of boosted jobs at the site
359
+ numBoostedJobsSite = int(numBoostedJobs * weight / quotaFactor)
360
+ tmpLog.debug(f"nSite:{numBoostedJobsSite} nAll:{numBoostedJobs} W:{weight} Q:{quotaFactor} at {computingSite}")
361
+ if numBoostedJobsSite / nJobsPerPrioUnit == 0:
362
+ tmpLog.debug(f"too small number of jobs {numBoostedJobsSite} to be boosted at {computingSite}")
363
+ continue
364
+ # get the highest prio of activated jobs at the site
365
+ varMap = {}
366
+ varMap[":jobStatus"] = "activated"
367
+ varMap[":prodSourceLabel"] = self.prodSourceLabel
368
+ varMap[":pmerge"] = "pmerge"
369
+ varMap[":prodUserName"] = prodUserName
370
+ varMap[":computingSite"] = computingSite
371
+ sql = "SELECT MAX(currentPriority) FROM ATLAS_PANDA.jobsActive4 "
372
+ sql += "WHERE prodSourceLabel=:prodSourceLabel AND jobStatus=:jobStatus AND computingSite=:computingSite "
373
+ sql += "AND processingType<>:pmerge AND prodUserName=:prodUserName "
374
+ if workingGroup is not None:
375
+ varMap[":workingGroup"] = workingGroup
376
+ sql += "AND workingGroup=:workingGroup "
377
+ else:
378
+ sql += "AND workingGroup IS NULL "
379
+ res = self.taskBufferIF.querySQL(sql, varMap, arraySize=10)
380
+ maxPrio = None
381
+ if res is not None:
382
+ try:
383
+ maxPrio = res[0][0]
384
+ except Exception:
385
+ pass
386
+ if maxPrio is None:
387
+ tmpLog.debug(f"cannot get the highest prio at {computingSite}")
388
+ continue
389
+ # delta for priority boost
390
+ prioDelta = highestPrio - maxPrio
391
+ # already boosted
392
+ if prioDelta <= 0:
393
+ tmpLog.debug(f"already boosted (prio={maxPrio}) at {computingSite}")
394
+ continue
395
+ # lower limit
396
+ minPrio = maxPrio - numBoostedJobsSite / nJobsPerPrioUnit
397
+ # SQL for priority boost
398
+ varMap = {}
399
+ varMap[":jobStatus"] = "activated"
400
+ varMap[":prodSourceLabel"] = self.prodSourceLabel
401
+ varMap[":prodUserName"] = prodUserName
402
+ varMap[":computingSite"] = computingSite
403
+ varMap[":prioDelta"] = prioDelta
404
+ varMap[":maxPrio"] = maxPrio
405
+ varMap[":minPrio"] = minPrio
406
+ varMap[":rlimit"] = numBoostedJobsSite
407
+ sql = "UPDATE ATLAS_PANDA.jobsActive4 SET currentPriority=currentPriority+:prioDelta "
408
+ sql += "WHERE prodSourceLabel=:prodSourceLabel AND prodUserName=:prodUserName "
409
+ if workingGroup is not None:
410
+ varMap[":workingGroup"] = workingGroup
411
+ sql += "AND workingGroup=:workingGroup "
412
+ else:
413
+ sql += "AND workingGroup IS NULL "
414
+ sql += "AND jobStatus=:jobStatus AND computingSite=:computingSite AND currentPriority>:minPrio "
415
+ sql += "AND currentPriority<=:maxPrio AND rownum<=:rlimit"
416
+ tmpLog.debug(f"boost {str(varMap)}")
417
+ res = self.taskBufferIF.querySQL(sql, varMap, arraySize=10)
418
+ tmpLog.debug(f" database return : {res}")
419
+ # done
420
+ tmpLog.debug("done")
421
+ except Exception:
422
+ errtype, errvalue = sys.exc_info()[:2]
423
+ tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
424
+
425
+ # redo stalled analysis jobs
426
+ def doForRedoStalledJobs(self):
427
+ tmpLog = MsgWrapper(logger, " #ATM #KV doForRedoStalledJobs label=user")
428
+ tmpLog.debug("start")
429
+ # lock
430
+ got_lock = self.taskBufferIF.lockProcess_JEDI(
431
+ vo=self.vo,
432
+ prodSourceLabel=self.prodSourceLabel,
433
+ cloud=None,
434
+ workqueue_id=None,
435
+ resource_name=None,
436
+ component="AtlasAnalWatchDog.doForRedoStalledJobs",
437
+ pid=self.pid,
438
+ timeLimit=6,
439
+ )
440
+ if not got_lock:
441
+ tmpLog.debug("locked by another process. Skipped")
442
+ return
443
+ # redo stalled analysis jobs
444
+ tmpLog.debug("redo stalled jobs")
445
+ try:
446
+ varMap = {":prodSourceLabel": self.prodSourceLabel}
447
+
448
+ sqlJ = "SELECT jobDefinitionID,prodUserName FROM ATLAS_PANDA.jobsDefined4 "
449
+ sqlJ += "WHERE prodSourceLabel=:prodSourceLabel AND modificationTime<CURRENT_DATE-2/24 "
450
+ sqlJ += "GROUP BY jobDefinitionID,prodUserName"
451
+
452
+ sqlP = "SELECT PandaID FROM ATLAS_PANDA.jobsDefined4 "
453
+ sqlP += "WHERE jobDefinitionID=:jobDefinitionID ANd prodSourceLabel=:prodSourceLabel AND prodUserName=:prodUserName AND rownum <= 1"
454
+
455
+ sqlF = "SELECT lfn,type,destinationDBlock FROM ATLAS_PANDA.filesTable4 WHERE PandaID=:PandaID AND status=:status"
456
+
457
+ sqlL = "SELECT guid,status,PandaID,dataset FROM ATLAS_PANDA.filesTable4 WHERE lfn=:lfn AND type=:type"
458
+
459
+ sqlA = "SELECT PandaID FROM ATLAS_PANDA.jobsDefined4 "
460
+ sqlA += "WHERE jobDefinitionID=:jobDefinitionID ANd prodSourceLabel=:prodSourceLabel AND prodUserName=:prodUserName"
461
+
462
+ sqlU = "UPDATE ATLAS_PANDA.jobsDefined4 SET modificationTime=CURRENT_DATE "
463
+ sqlU += "WHERE jobDefinitionID=:jobDefinitionID ANd prodSourceLabel=:prodSourceLabel AND prodUserName=:prodUserName"
464
+
465
+ # get stalled jobs
466
+ resJ = self.taskBufferIF.querySQL(sqlJ, varMap)
467
+ if resJ is None or len(resJ) == 0:
468
+ pass
469
+ else:
470
+ # loop over all jobID/users
471
+ for jobDefinitionID, prodUserName in resJ:
472
+ tmpLog.debug(f" user:{prodUserName} jobID:{jobDefinitionID}")
473
+ # get stalled jobs
474
+ varMap = {":prodSourceLabel": self.prodSourceLabel, ":jobDefinitionID": jobDefinitionID, ":prodUserName": prodUserName}
475
+ resP = self.taskBufferIF.querySQL(sqlP, varMap)
476
+ if resP is None or len(resP) == 0:
477
+ tmpLog.debug(" no PandaID")
478
+ continue
479
+ useLib = False
480
+ libStatus = None
481
+ libGUID = None
482
+ libLFN = None
483
+ libDSName = None
484
+ destReady = False
485
+ # use the first PandaID
486
+ for (PandaID,) in resP:
487
+ tmpLog.debug(f" check PandaID:{PandaID}")
488
+ # get files
489
+ varMap = {}
490
+ varMap[":PandaID"] = PandaID
491
+ varMap[":status"] = "unknown"
492
+ resF = self.taskBufferIF.querySQL(sqlF, varMap)
493
+ if resF is None or len(resF) == 0:
494
+ tmpLog.debug(" no files")
495
+ else:
496
+ # get lib.tgz and destDBlock
497
+ for lfn, filetype, destinationDBlock in resF:
498
+ if filetype == "input" and lfn.endswith(".lib.tgz"):
499
+ useLib = True
500
+ libLFN = lfn
501
+ varMap = {}
502
+ varMap[":lfn"] = lfn
503
+ varMap[":type"] = "output"
504
+ resL = self.taskBufferIF.querySQL(sqlL, varMap)
505
+ # not found
506
+ if resL is None or len(resL) == 0:
507
+ tmpLog.error(f" cannot find status of {lfn}")
508
+ continue
509
+ # check status
510
+ guid, outFileStatus, pandaIDOutLibTgz, tmpLibDsName = resL[0]
511
+ tmpLog.debug(f" PandaID:{pandaIDOutLibTgz} produces {tmpLibDsName}:{lfn} GUID={guid} status={outFileStatus}")
512
+ libStatus = outFileStatus
513
+ libGUID = guid
514
+ libDSName = tmpLibDsName
515
+ elif filetype in ["log", "output"]:
516
+ if destinationDBlock is not None and re.search("_sub\d+$", destinationDBlock) is not None:
517
+ destReady = True
518
+ break
519
+ tmpLog.debug(f" useLib:{useLib} libStatus:{libStatus} libDsName:{libDSName} libLFN:{libLFN} libGUID:{libGUID} destReady:{destReady}")
520
+ if libStatus == "failed":
521
+ # delete downstream jobs
522
+ tmpLog.debug(" -> delete downstream jobs")
523
+ # FIXME
524
+ # self.taskBufferIF.deleteStalledJobs(libLFN)
525
+ else:
526
+ # activate
527
+ if useLib and libStatus == "ready" and (libGUID not in [None, ""]) and (libDSName not in [None, ""]):
528
+ # update GUID
529
+ tmpLog.debug(f" set GUID:{libGUID} for {libLFN}")
530
+ # retG = self.taskBufferIF.setGUIDs([{'lfn':libLFN,'guid':libGUID}])
531
+ # FIXME
532
+ retG = True
533
+ if not retG:
534
+ tmpLog.error(f" failed to update GUID for {libLFN}")
535
+ else:
536
+ # get PandaID with lib.tgz
537
+ # ids = self.taskBufferIF.updateInFilesReturnPandaIDs(libDSName,'ready')
538
+ ids = []
539
+ # get jobs
540
+ jobs = self.taskBufferIF.peekJobs(ids, fromActive=False, fromArchived=False, fromWaiting=False)
541
+ # remove None and unknown
542
+ acJobs = []
543
+ for job in jobs:
544
+ if job is None or job.jobStatus == "unknown":
545
+ continue
546
+ acJobs.append(job)
547
+ # activate
548
+ tmpLog.debug(" -> activate downstream jobs")
549
+ # self.taskBufferIF.activateJobs(acJobs)
550
+ else:
551
+ # wait
552
+ tmpLog.debug(" -> wait")
553
+ varMap = {":prodSourceLabel": self.prodSourceLabel, ":jobDefinitionID": jobDefinitionID, ":prodUserName": prodUserName}
554
+ # FIXME
555
+ # resU = self.taskBufferIF.querySQL(sqlU, varMap)
556
+ # done
557
+ tmpLog.debug("done")
558
+ except Exception:
559
+ errtype, errvalue = sys.exc_info()[:2]
560
+ tmpLog.error(f"failed to redo stalled jobs with {errtype} {errvalue} {traceback.format_exc()}")
561
+
562
+ # task share and priority boost
563
+ def doForTaskBoost(self):
564
+ tmpLog = MsgWrapper(logger, " #ATM #KV doForTaskBoost label=user")
565
+ tmpLog.debug("start")
566
+ # lock
567
+ got_lock = self.taskBufferIF.lockProcess_JEDI(
568
+ vo=self.vo,
569
+ prodSourceLabel=self.prodSourceLabel,
570
+ cloud=None,
571
+ workqueue_id=None,
572
+ resource_name=None,
573
+ component="AtlasAnalWatchDog.doForTaskBoost",
574
+ pid=self.pid,
575
+ timeLimit=5,
576
+ )
577
+ if not got_lock:
578
+ tmpLog.debug("locked by another process. Skipped")
579
+ return
580
+ try:
581
+ # get active tasks in S-class
582
+ sql_get_tasks = (
583
+ """SELECT /* use_json_type */ tev.value_json.task_id, tev.value_json."user" """
584
+ """FROM ATLAS_PANDA.Task_Evaluation tev, ATLAS_PANDA.JEDI_Tasks t """
585
+ """WHERE tev.value_json.task_id=t.jediTaskID """
586
+ """AND tev.metric='analy_task_eval' """
587
+ """AND tev.value_json.class=:s_class """
588
+ """AND tev.value_json.gshare=:gshare """
589
+ """AND t.gshare=:gshare """
590
+ )
591
+ # varMap
592
+ varMap = {
593
+ ":s_class": 2,
594
+ ":gshare": "User Analysis",
595
+ }
596
+ # result
597
+ res = self.taskBufferIF.querySQL(sql_get_tasks, varMap)
598
+ # Assign to Express Analysis
599
+ new_share = "Express Analysis"
600
+ for task_id, user in res:
601
+ tmpLog.info(
602
+ f" >>> action=gshare_reassignment jediTaskID={task_id} from gshare_old={varMap[':gshare']} to gshare_new={new_share} #ATM #KV label=user"
603
+ )
604
+ self.taskBufferIF.reassignShare([task_id], new_share, True)
605
+ # tweak split rule
606
+ _, task_spec = self.taskBufferIF.getTaskWithID_JEDI(task_id)
607
+ # set max walltime to 1 hour
608
+ if task_spec.getNumFilesPerJob() is None and task_spec.getNumEventsPerJob() is None:
609
+ tmpLog.info(f" >>> set max walltime to 1 hr for jediTaskID={task_id}")
610
+ task_spec.set_max_walltime(1)
611
+ self.taskBufferIF.updateTask_JEDI(task_spec, {"jediTaskID": task_id})
612
+ tmpLog.info(f">>> done jediTaskID={task_id}")
613
+ # done
614
+ tmpLog.debug("done")
615
+ except Exception:
616
+ errtype, errvalue = sys.exc_info()[:2]
617
+ tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
618
+
619
+ # periodic task action
620
+ def do_periodic_action(self):
621
+ tmp_log = MsgWrapper(logger, " do_periodic_action label=user")
622
+ tmp_log.debug("start")
623
+ try:
624
+ # get lifetime for output container
625
+ lifetime = self.taskBufferIF.getConfigValue("user_output", "OUTPUT_CONTAINER_LIFETIME", "jedi")
626
+ if not lifetime:
627
+ lifetime = 14
628
+ lifetime *= 24 * 60 * 60
629
+ # get DDM interface
630
+ ddm_if = self.ddmIF.getInterface(self.vo)
631
+ # get tasks
632
+ task_list = self.taskBufferIF.get_tasks_for_periodic_action(self.vo, self.prodSourceLabel)
633
+ for task_id in task_list:
634
+ # get datasets
635
+ _, tmp_datasets = self.taskBufferIF.getDatasetsWithJediTaskID_JEDI(task_id, ["output"])
636
+ done_containers = set()
637
+ for dataset_spec in tmp_datasets:
638
+ # skip if already done for the container
639
+ if not dataset_spec.containerName or dataset_spec.containerName in done_containers:
640
+ continue
641
+ done_containers.add(dataset_spec.containerName)
642
+ tmp_log.debug(f"extend lifetime taskID={task_id} container={dataset_spec.containerName}")
643
+ ddm_if.updateReplicationRules(
644
+ dataset_spec.containerName,
645
+ {"type=.+": {"lifetime": lifetime}, "(SCRATCH|USER)DISK": {"lifetime": lifetime}},
646
+ )
647
+ except Exception as e:
648
+ tmp_log.error(f"failed with {str(e)}{traceback.format_exc()}")