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,1387 @@
1
+ import datetime
2
+ import math
3
+ import os
4
+ import re
5
+ import sys
6
+ import time
7
+ import traceback
8
+
9
+ import requests
10
+ from pandacommon.pandalogger.LogWrapper import LogWrapper
11
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
12
+ from pandacommon.pandautils.PandaUtils import get_sql_IN_bind_variables, naive_utcnow
13
+ from pandacommon.pandautils.thread_utils import GenericThread
14
+ from urllib3.exceptions import InsecureRequestWarning
15
+
16
+ import pandaserver.userinterface.Client as Client
17
+ from pandaserver.brokerage.SiteMapper import SiteMapper
18
+ from pandaserver.config import panda_config
19
+ from pandaserver.jobdispatcher.Watcher import Watcher
20
+ from pandaserver.taskbuffer import EventServiceUtils
21
+
22
+ # logger
23
+ _logger = PandaLogger().getLogger("copyArchive")
24
+
25
+
26
+ # main
27
+ def main(argv=tuple(), tbuf=None, **kwargs):
28
+ requester_id = GenericThread().get_full_id(__name__, sys.modules[__name__].__file__)
29
+
30
+ # password
31
+ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
32
+
33
+ _logger.debug("===================== start =====================")
34
+
35
+ # memory checker
36
+ def _memoryCheck(str):
37
+ try:
38
+ proc_status = "/proc/%d/status" % os.getpid()
39
+ procfile = open(proc_status)
40
+ name = ""
41
+ vmSize = ""
42
+ vmRSS = ""
43
+ # extract Name,VmSize,VmRSS
44
+ for line in procfile:
45
+ if line.startswith("Name:"):
46
+ name = line.split()[-1]
47
+ continue
48
+ if line.startswith("VmSize:"):
49
+ vmSize = ""
50
+ for item in line.split()[1:]:
51
+ vmSize += item
52
+ continue
53
+ if line.startswith("VmRSS:"):
54
+ vmRSS = ""
55
+ for item in line.split()[1:]:
56
+ vmRSS += item
57
+ continue
58
+ procfile.close()
59
+ _logger.debug(f"MemCheck - {os.getpid()} Name={name} VSZ={vmSize} RSS={vmRSS} : {str}")
60
+ except Exception:
61
+ type, value, traceBack = sys.exc_info()
62
+ _logger.error(f"memoryCheck() : {type} {value}")
63
+ _logger.debug(f"MemCheck - {os.getpid()} unknown : {str}")
64
+ return
65
+
66
+ _memoryCheck("start")
67
+
68
+ # instantiate TB
69
+ from pandaserver.taskbuffer.TaskBuffer import taskBuffer
70
+
71
+ taskBuffer.init(
72
+ panda_config.dbhost,
73
+ panda_config.dbpasswd,
74
+ nDBConnection=1,
75
+ useTimeout=True,
76
+ requester=requester_id,
77
+ )
78
+
79
+ # instantiate sitemapper
80
+ siteMapper = SiteMapper(taskBuffer)
81
+
82
+ # kick merging jobs
83
+ _logger.debug("Kick merging session")
84
+ try:
85
+ # get min PandaID for failed jobs in Active table
86
+ sql = (
87
+ "SELECT j.PandaID, j.jediTaskID, f.destinationDBlock "
88
+ f"FROM {panda_config.schemaPANDA}.jobsActive4 j, {panda_config.schemaPANDA}.filesTable4 f "
89
+ "WHERE f.PandaID=j.PandaID AND j.prodSourceLabel=:prodSourceLabel AND j.jobStatus=:jobStatus "
90
+ "AND f.type=:type "
91
+ )
92
+ var_map = {":jobStatus": "merging", ":prodSourceLabel": "user", ":type": "log"}
93
+ status, res = taskBuffer.querySQLS(sql, var_map)
94
+ destination_blocks = {}
95
+ if res is not None:
96
+ # collect destination blocks
97
+ for panda_id, task_id, destination_block in res:
98
+ if destination_block in destination_blocks:
99
+ continue
100
+ destination_blocks[destination_block] = (panda_id, task_id)
101
+ # loop over all destination blocks
102
+ for destination_block, (panda_id, task_id) in destination_blocks.items():
103
+ # check
104
+ tmp_log = LogWrapper(_logger, f"kick_merge < jediTaskID={task_id} dest={destination_block} >")
105
+ tmp_log.debug("check")
106
+ sqlC = f"WITH w AS (SELECT PandaID FROM {panda_config.schemaPANDA}.filesTable4 WHERE destinationDBlock=:destinationDBlock) "
107
+ sqlC += "SELECT COUNT(*) FROM ("
108
+ sqlC += "SELECT j.PandaID FROM ATLAS_PANDA.jobsActive4 j, w "
109
+ sqlC += "WHERE j.PandaID=w.PandaID "
110
+ sqlC += "AND NOT j.jobStatus IN (:jobStatus1,:jobStatus2) "
111
+ sqlC += "UNION "
112
+ sqlC += "SELECT j.PandaID FROM ATLAS_PANDA.jobsDefined4 j, w "
113
+ sqlC += "WHERE j.PandaID=w.PandaID "
114
+ sqlC += "AND NOT j.jobStatus IN (:jobStatus1,:jobStatus2) "
115
+ sqlC += ") "
116
+ var_map = {}
117
+ var_map[":jobStatus1"] = "failed"
118
+ var_map[":jobStatus2"] = "merging"
119
+ var_map[":destinationDBlock"] = destination_block
120
+ statC, resC = taskBuffer.querySQLS(sqlC, var_map)
121
+ # finalize if all jobs have processed
122
+ if resC is not None:
123
+ num_unprocessed = resC[0][0]
124
+ tmp_log.debug(f"{num_unprocessed} unprocessed jobs")
125
+ if num_unprocessed == 0:
126
+ jobSpecs = taskBuffer.peekJobs(
127
+ [panda_id],
128
+ fromDefined=False,
129
+ fromArchived=False,
130
+ fromWaiting=False,
131
+ )
132
+ jobSpec = jobSpecs[0]
133
+ if jobSpec is None:
134
+ tmp_log.debug(f"skip PandaID={panda_id} not found in jobsActive")
135
+ continue
136
+ if jobSpec.produceUnMerge():
137
+ # collect sub datasets
138
+ sub_ds_names = set()
139
+ sub_ds_list = []
140
+ killed_for_bad_record = False
141
+ for tmpFileSpec in jobSpec.Files:
142
+ if tmpFileSpec.type in ["log", "output"] and re.search("_sub\d+$", tmpFileSpec.destinationDBlock) is not None:
143
+ if tmpFileSpec.destinationDBlock in sub_ds_names:
144
+ continue
145
+ sub_ds_names.add(tmpFileSpec.destinationDBlock)
146
+ datasetSpec = taskBuffer.queryDatasetWithMap({"name": tmpFileSpec.destinationDBlock})
147
+ # kill jobs since sub dataset is missing due to failures in setupper etc
148
+ if datasetSpec is None:
149
+ tmp_log.debug(f"sub dataset {tmpFileSpec.destinationDBlock} is missing")
150
+ sql_missing = "SELECT PandaID FROM ATLAS_PANDA.filesTable4 WHERE destinationDBlock=:destinationDBlock "
151
+ var_map = {":destinationDBlock": tmpFileSpec.destinationDBlock}
152
+ _, res_missing = taskBuffer.querySQLS(sql_missing, var_map)
153
+ missing_ids = [p for p, in res_missing]
154
+ tmp_log.debug(f"missing {tmpFileSpec.destinationDBlock} to kill {missing_ids}")
155
+ Client.kill_jobs(missing_ids, 2)
156
+ killed_for_bad_record = True
157
+ break
158
+ elif datasetSpec.status == "deleted":
159
+ tmp_log.debug(f"sub dataset {tmpFileSpec.destinationDBlock} is deleted")
160
+ sql_deleted = (
161
+ "SELECT j.PandaID "
162
+ "FROM ATLAS_PANDA.jobsActive4 j, ATLAS_PANDA.filesTable4 f "
163
+ "WHERE j.PandaID=f.PandaID AND j.jobStatus=:jobStatus "
164
+ "AND f.destinationDBlock=:destinationDBlock "
165
+ )
166
+ var_map = {":jobStatus": "merging", ":destinationDBlock": tmpFileSpec.destinationDBlock}
167
+ _, res_deleted = taskBuffer.querySQLS(sql_deleted, var_map)
168
+ deleted_ids = [p for p, in res_deleted]
169
+ tmp_log.debug(f"deleted {tmpFileSpec.destinationDBlock} to kill {deleted_ids}")
170
+ Client.kill_jobs(deleted_ids, 2)
171
+ killed_for_bad_record = True
172
+ break
173
+ else:
174
+ sub_ds_list.append(datasetSpec)
175
+ # update unmerged datasets to trigger merge job generation
176
+ if not killed_for_bad_record and sub_ds_list:
177
+ # check dataset status
178
+ all_defined = True
179
+ for datasetSpec in sub_ds_list:
180
+ if datasetSpec.status != "defined":
181
+ all_defined = False
182
+ tmp_log.debug(f"skip to update unmerged datasets since {datasetSpec.name} is {datasetSpec.status}")
183
+ break
184
+ if all_defined:
185
+ tmp_log.debug(f"update unmerged datasets {[d.name for d in sub_ds_list]}")
186
+ taskBuffer.updateUnmergedDatasets(jobSpec, sub_ds_list)
187
+ else:
188
+ tmp_log.debug("number of unprocessed jobs unknown")
189
+ except Exception as e:
190
+ _logger.error(f"Kick merging failed with {str(e)} {traceback.format_exc()}")
191
+
192
+ # finalize failed jobs
193
+ _logger.debug("check stuck merging jobs")
194
+ try:
195
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=2)
196
+ # get PandaIDs
197
+ var_map = {}
198
+ var_map[":prodSourceLabel"] = "managed"
199
+ var_map[":jobStatus"] = "merging"
200
+ var_map[":timeLimit"] = timeLimit
201
+ sql = "SELECT distinct jediTaskID FROM ATLAS_PANDA.jobsActive4 "
202
+ sql += "WHERE prodSourceLabel=:prodSourceLabel AND jobStatus=:jobStatus and modificationTime<:timeLimit "
203
+ tmp, res = taskBuffer.querySQLS(sql, var_map)
204
+
205
+ for (jediTaskID,) in res:
206
+ var_map = {}
207
+ var_map[":jediTaskID"] = jediTaskID
208
+ var_map[":dsType"] = "trn_log"
209
+ sql = "SELECT datasetID FROM ATLAS_PANDA.JEDI_Datasets WHERE jediTaskID=:jediTaskID AND type=:dsType AND nFilesUsed=nFilesTobeUsed "
210
+ tmpP, resD = taskBuffer.querySQLS(sql, var_map)
211
+ for (datasetID,) in resD:
212
+ var_map = {}
213
+ var_map[":jediTaskID"] = jediTaskID
214
+ var_map[":fileStatus"] = "ready"
215
+ var_map[":datasetID"] = datasetID
216
+ sql = "SELECT PandaID FROM ATLAS_PANDA.JEDI_Dataset_Contents "
217
+ sql += "WHERE jediTaskID=:jediTaskID AND datasetid=:datasetID AND status=:fileStatus AND PandaID=OutPandaID AND rownum<=1 "
218
+ tmpP, resP = taskBuffer.querySQLS(sql, var_map)
219
+ if resP == []:
220
+ continue
221
+ PandaID = resP[0][0]
222
+ var_map = {}
223
+ var_map[":PandaID"] = PandaID
224
+ var_map[":fileType"] = "log"
225
+ sql = "SELECT d.status FROM ATLAS_PANDA.filesTable4 f,ATLAS_PANDA.datasets d WHERE PandaID=:PandaID AND f.type=:fileType AND d.name=f.destinationDBlock "
226
+ tmpS, resS = taskBuffer.querySQLS(sql, var_map)
227
+ if resS is not None:
228
+ (subStatus,) = resS[0]
229
+ if subStatus in ["completed"]:
230
+ jobSpecs = taskBuffer.peekJobs(
231
+ [PandaID],
232
+ fromDefined=False,
233
+ fromArchived=False,
234
+ fromWaiting=False,
235
+ )
236
+ jobSpec = jobSpecs[0]
237
+ sub_ds_names = set()
238
+ sub_ds_list = []
239
+ for tmpFileSpec in jobSpec.Files:
240
+ if tmpFileSpec.type in ["log", "output"] and re.search("_sub\d+$", tmpFileSpec.destinationDBlock) is not None:
241
+ if tmpFileSpec.destinationDBlock in sub_ds_names:
242
+ continue
243
+ sub_ds_names.add(tmpFileSpec.destinationDBlock)
244
+ datasetSpec = taskBuffer.queryDatasetWithMap({"name": tmpFileSpec.destinationDBlock})
245
+ sub_ds_list.append(datasetSpec)
246
+ _logger.debug(f"update unmerged datasets for jediTaskID={jediTaskID} PandaID={PandaID}")
247
+ taskBuffer.updateUnmergedDatasets(jobSpec, sub_ds_list, updateCompleted=True)
248
+ except Exception:
249
+ errType, errValue = sys.exc_info()[:2]
250
+ _logger.error(f"check for stuck merging jobs failed with {errType} {errValue}")
251
+
252
+ # get sites to skip various timeout
253
+ var_map = {}
254
+ var_map[":status"] = "paused"
255
+ sql = "SELECT /* use_json_type */ panda_queue FROM ATLAS_PANDA.schedconfig_json scj WHERE scj.data.status=:status "
256
+ sitesToSkipTO = set()
257
+ status, res = taskBuffer.querySQLS(sql, var_map)
258
+ for (siteid,) in res:
259
+ sitesToSkipTO.add(siteid)
260
+ _logger.debug(f"PQs to skip timeout : {','.join(sitesToSkipTO)}")
261
+
262
+ sitesToDisableReassign = set()
263
+ # get sites to disable reassign
264
+ for siteName in siteMapper.siteSpecList:
265
+ siteSpec = siteMapper.siteSpecList[siteName]
266
+ if siteSpec.capability == "ucore" and not siteSpec.is_unified:
267
+ continue
268
+ if siteSpec.disable_reassign():
269
+ sitesToDisableReassign.add(siteName)
270
+ _logger.debug(f"PQs to disable reassign : {','.join(sitesToDisableReassign)}")
271
+
272
+ _memoryCheck("watcher")
273
+
274
+ _logger.debug("Watcher session")
275
+
276
+ # get the list of workflows
277
+ sql = "SELECT /* use_json_type */ DISTINCT scj.data.workflow FROM ATLAS_PANDA.schedconfig_json scj WHERE scj.data.status='online' "
278
+ status, res = taskBuffer.querySQLS(sql, {})
279
+ workflow_timeout_map = {}
280
+ for (workflow,) in res + [("production",), ("analysis",)]:
281
+ timeout = taskBuffer.getConfigValue("watcher", f"HEARTBEAT_TIMEOUT_{workflow}", "pandaserver", "atlas")
282
+ if timeout is not None:
283
+ workflow_timeout_map[workflow] = timeout
284
+ elif workflow in ["production", "analysis"]:
285
+ workflow_timeout_map[workflow] = 2
286
+
287
+ workflows = list(workflow_timeout_map)
288
+
289
+ _logger.debug(f"timeout : {str(workflow_timeout_map)}")
290
+
291
+ # check heartbeat for analysis jobs
292
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=workflow_timeout_map["analysis"])
293
+ var_map = {}
294
+ var_map[":modificationTime"] = timeLimit
295
+ var_map[":prodSourceLabel1"] = "panda"
296
+ var_map[":prodSourceLabel2"] = "user"
297
+ var_map[":jobStatus1"] = "running"
298
+ var_map[":jobStatus2"] = "starting"
299
+ var_map[":jobStatus3"] = "stagein"
300
+ var_map[":jobStatus4"] = "stageout"
301
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE (prodSourceLabel=:prodSourceLabel1 OR prodSourceLabel=:prodSourceLabel2) "
302
+ sql += "AND jobStatus IN (:jobStatus1,:jobStatus2,:jobStatus3,:jobStatus4) AND modificationTime<:modificationTime"
303
+ status, res = taskBuffer.querySQLS(sql, var_map)
304
+ if res is None:
305
+ _logger.debug(f"# of Anal Watcher : {res}")
306
+ else:
307
+ _logger.debug(f"# of Anal Watcher : {len(res)}")
308
+ for (id,) in res:
309
+ _logger.debug(f"Anal Watcher {id}")
310
+ thr = Watcher(taskBuffer, id, single=True, sleepTime=60, sitemapper=siteMapper)
311
+ thr.start()
312
+ thr.join()
313
+
314
+ # check heartbeat for analysis jobs in transferring
315
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=workflow_timeout_map["analysis"])
316
+ var_map = {}
317
+ var_map[":modificationTime"] = timeLimit
318
+ var_map[":prodSourceLabel1"] = "panda"
319
+ var_map[":prodSourceLabel2"] = "user"
320
+ var_map[":jobStatus1"] = "transferring"
321
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE prodSourceLabel IN (:prodSourceLabel1,:prodSourceLabel2) "
322
+ sql += "AND jobStatus=:jobStatus1 AND modificationTime<:modificationTime"
323
+ status, res = taskBuffer.querySQLS(sql, var_map)
324
+ if res is None:
325
+ _logger.debug(f"# of Transferring Anal Watcher : {res}")
326
+ else:
327
+ _logger.debug(f"# of Transferring Anal Watcher : {len(res)}")
328
+ for (id,) in res:
329
+ _logger.debug(f"Trans Anal Watcher {id}")
330
+ thr = Watcher(taskBuffer, id, single=True, sleepTime=60, sitemapper=siteMapper)
331
+ thr.start()
332
+ thr.join()
333
+
334
+ # check heartbeat for sent jobs
335
+ timeLimit = naive_utcnow() - datetime.timedelta(minutes=30)
336
+ var_map = {}
337
+ var_map[":jobStatus"] = "sent"
338
+ var_map[":modificationTime"] = timeLimit
339
+ status, res = taskBuffer.querySQLS(
340
+ "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE jobStatus=:jobStatus AND modificationTime<:modificationTime",
341
+ var_map,
342
+ )
343
+ if res is None:
344
+ _logger.debug(f"# of Sent Watcher : {res}")
345
+ else:
346
+ _logger.debug(f"# of Sent Watcher : {len(res)}")
347
+ for (id,) in res:
348
+ _logger.debug(f"Sent Watcher {id}")
349
+ thr = Watcher(taskBuffer, id, single=True, sleepTime=30, sitemapper=siteMapper)
350
+ thr.start()
351
+ thr.join()
352
+
353
+ # check heartbeat for 'holding' analysis/ddm jobs
354
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=3)
355
+ # get XMLs
356
+ xmlIDs = set()
357
+ # xmlFiles = os.listdir(panda_config.logdir)
358
+ # for file in xmlFiles:
359
+ # match = re.search('^(\d+)_([^_]+)_.{36}$',file)
360
+ # if match is not None:
361
+ # id = match.group(1)
362
+ # xmlIDs.append(int(id))
363
+ job_output_report_list = taskBuffer.listJobOutputReport()
364
+ if job_output_report_list is not None:
365
+ for panda_id, job_status, attempt_nr, time_stamp in job_output_report_list:
366
+ xmlIDs.add(int(panda_id))
367
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE jobStatus=:jobStatus AND (modificationTime<:modificationTime OR (endTime IS NOT NULL AND endTime<:endTime)) AND (prodSourceLabel=:prodSourceLabel1 OR prodSourceLabel=:prodSourceLabel2) AND stateChangeTime != modificationTime"
368
+ var_map = {}
369
+ var_map[":modificationTime"] = timeLimit
370
+ var_map[":endTime"] = timeLimit
371
+ var_map[":jobStatus"] = "holding"
372
+ var_map[":prodSourceLabel1"] = "panda"
373
+ var_map[":prodSourceLabel2"] = "user"
374
+
375
+ status, res = taskBuffer.querySQLS(sql, var_map)
376
+ if res is None:
377
+ _logger.debug(f"# of Holding Anal/DDM Watcher : {res}")
378
+ else:
379
+ _logger.debug(f"# of Holding Anal/DDM Watcher : {len(res)} - XMLs : {len(xmlIDs)}")
380
+ for (id,) in res:
381
+ _logger.debug(f"Holding Anal/DDM Watcher {id}")
382
+ if int(id) in xmlIDs:
383
+ _logger.debug(f" found XML -> skip {id}")
384
+ continue
385
+ thr = Watcher(taskBuffer, id, single=True, sleepTime=180, sitemapper=siteMapper)
386
+ thr.start()
387
+ thr.join()
388
+
389
+ # check heartbeat for high prio production jobs
390
+ timeOutVal = 3
391
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=timeOutVal)
392
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE jobStatus=:jobStatus AND currentPriority>:pLimit "
393
+ sql += "AND (modificationTime<:modificationTime OR (endTime IS NOT NULL AND endTime<:endTime))"
394
+ var_map = {}
395
+ var_map[":modificationTime"] = timeLimit
396
+ var_map[":endTime"] = timeLimit
397
+ var_map[":jobStatus"] = "holding"
398
+ var_map[":pLimit"] = 800
399
+ status, res = taskBuffer.querySQLS(sql, var_map)
400
+ if res is None:
401
+ _logger.debug(f"# of High prio Holding Watcher : {res}")
402
+ else:
403
+ _logger.debug(f"# of High prio Holding Watcher : {len(res)}")
404
+ for (id,) in res:
405
+ _logger.debug(f"High prio Holding Watcher {id}")
406
+ thr = Watcher(
407
+ taskBuffer,
408
+ id,
409
+ single=True,
410
+ sleepTime=60 * timeOutVal,
411
+ sitemapper=siteMapper,
412
+ )
413
+ thr.start()
414
+ thr.join()
415
+
416
+ # check heartbeat for production jobs
417
+ timeOutVal = taskBuffer.getConfigValue("job_timeout", "TIMEOUT_holding", "pandaserver")
418
+ if not timeOutVal:
419
+ timeOutVal = 48
420
+ timeOutVal *= 60
421
+ timeLimit = naive_utcnow() - datetime.timedelta(minutes=timeOutVal)
422
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE jobStatus=:jobStatus AND (modificationTime<:modificationTime OR (endTime IS NOT NULL AND endTime<:endTime))"
423
+ var_map = {}
424
+ var_map[":modificationTime"] = timeLimit
425
+ var_map[":endTime"] = timeLimit
426
+ var_map[":jobStatus"] = "holding"
427
+ status, res = taskBuffer.querySQLS(sql, var_map)
428
+ if res is None:
429
+ _logger.debug(f"# of Holding Watcher with timeout {timeOutVal}min: {str(res)}")
430
+ else:
431
+ _logger.debug(f"# of Holding Watcher with timeout {timeOutVal}min: {len(res)}")
432
+ for (id,) in res:
433
+ _logger.debug(f"Holding Watcher {id}")
434
+ thr = Watcher(taskBuffer, id, single=True, sleepTime=timeOutVal, sitemapper=siteMapper)
435
+ thr.start()
436
+ thr.join()
437
+
438
+ # check heartbeat for production jobs
439
+ sql = (
440
+ "SELECT /* use_json_type */ PandaID, jobStatus, j.computingSite FROM ATLAS_PANDA.jobsActive4 j "
441
+ "LEFT JOIN ATLAS_PANDA.schedconfig_json s ON j.computingSite=s.panda_queue "
442
+ "WHERE jobStatus IN (:jobStatus1,:jobStatus2,:jobStatus3,:jobStatus4) "
443
+ "AND modificationTime<:modificationTime "
444
+ )
445
+ for workflow in workflows:
446
+ if workflow == "analysis":
447
+ continue
448
+ var_map = {}
449
+ var_map[":jobStatus1"] = "running"
450
+ var_map[":jobStatus2"] = "starting"
451
+ var_map[":jobStatus3"] = "stagein"
452
+ var_map[":jobStatus4"] = "stageout"
453
+ sqlX = sql
454
+ if workflow == "production":
455
+ if len(workflows) > 2:
456
+ ng_workflow_var_names_str, ng_workflow_var_map = get_sql_IN_bind_variables(workflows, prefix=":w_", value_as_suffix=True)
457
+ sqlX += f"AND (s.data.workflow IS NULL OR s.data.workflow NOT IN ({ng_workflow_var_names_str})) "
458
+ var_map.update(ng_workflow_var_map)
459
+ else:
460
+ tmp_key = f":w_{workflow}"
461
+ sqlX += f"AND s.data.workflow={tmp_key} "
462
+ var_map[tmp_key] = workflow
463
+ timeOutVal = workflow_timeout_map[workflow]
464
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=timeOutVal)
465
+ var_map[":modificationTime"] = timeLimit
466
+ status, res = taskBuffer.querySQLS(sqlX, var_map)
467
+ if res is None:
468
+ _logger.debug(f"# of General Watcher with workflow={workflow}: {res}")
469
+ else:
470
+ _logger.debug(f"# of General Watcher with workflow={workflow}: {len(res)}")
471
+ for pandaID, jobStatus, computingSite in res:
472
+ if computingSite in sitesToSkipTO:
473
+ _logger.debug(f"skip General Watcher for PandaID={pandaID} at {computingSite} since timeout is disabled for {jobStatus}")
474
+ continue
475
+ _logger.debug(f"General Watcher {pandaID}")
476
+ thr = Watcher(
477
+ taskBuffer,
478
+ pandaID,
479
+ single=True,
480
+ sleepTime=60 * timeOutVal,
481
+ sitemapper=siteMapper,
482
+ )
483
+ thr.start()
484
+ thr.join()
485
+
486
+ _memoryCheck("reassign")
487
+
488
+ # kill long-waiting jobs in defined table
489
+ timeLimit = naive_utcnow() - datetime.timedelta(days=7)
490
+ status, res = taskBuffer.querySQLS(
491
+ "SELECT PandaID,cloud,prodSourceLabel FROM ATLAS_PANDA.jobsDefined4 WHERE creationTime<:creationTime",
492
+ {":creationTime": timeLimit},
493
+ )
494
+ jobs = []
495
+ dashFileMap = {}
496
+ if res is not None:
497
+ for pandaID, cloud, prodSourceLabel in res:
498
+ # collect PandaIDs
499
+ jobs.append(pandaID)
500
+ if len(jobs):
501
+ _logger.debug(f"killJobs for Defined ({str(jobs)})")
502
+ Client.kill_jobs(jobs, 2)
503
+
504
+ # kill long-waiting jobs in active table
505
+ timeLimit = naive_utcnow() - datetime.timedelta(days=7)
506
+ var_map = {}
507
+ var_map[":jobStatus"] = "activated"
508
+ var_map[":creationTime"] = timeLimit
509
+ status, res = taskBuffer.querySQLS(
510
+ "SELECT PandaID from ATLAS_PANDA.jobsActive4 WHERE jobStatus=:jobStatus AND creationTime<:creationTime",
511
+ var_map,
512
+ )
513
+ jobs = []
514
+ if res is not None:
515
+ for (id,) in res:
516
+ jobs.append(id)
517
+ if len(jobs):
518
+ _logger.debug(f"killJobs for Active ({str(jobs)})")
519
+ Client.kill_jobs(jobs, 2)
520
+
521
+ # fast rebrokerage at PQs where Nq/Nr overshoots
522
+ _logger.debug("fast rebrokerage at PQs where Nq/Nr overshoots")
523
+ try:
524
+ ratioLimit = taskBuffer.getConfigValue("rebroker", "FAST_REBRO_THRESHOLD_NQNR_RATIO")
525
+ fractionLimit = taskBuffer.getConfigValue("rebroker", "FAST_REBRO_THRESHOLD_NQUEUE_FRAC")
526
+ if not ratioLimit:
527
+ ratioLimit = 3
528
+ if not fractionLimit:
529
+ fractionLimit = 0.3
530
+
531
+ # get overloaded PQs
532
+ sql = (
533
+ "SELECT COMPUTINGSITE,JOBSTATUS,GSHARE,SUM(NJOBS) FROM ATLAS_PANDA.{} "
534
+ "WHERE workqueue_id NOT IN "
535
+ "(SELECT queue_id FROM ATLAS_PANDA.jedi_work_queue WHERE queue_function = 'Resource') "
536
+ "AND computingsite NOT IN "
537
+ "(SELECT pandaqueuename FROM ATLAS_PANDA.HARVESTER_Slots) GROUP BY COMPUTINGSITE,JOBSTATUS,GSHARE "
538
+ )
539
+
540
+ statsPerShare = {}
541
+ statsPerPQ = {}
542
+ for table in ["JOBS_SHARE_STATS", "JOBSDEFINED_SHARE_STATS"]:
543
+ status, res = taskBuffer.querySQLS(sql.format(table), {})
544
+ for computingSite, jobStatus, gshare, nJobs in res:
545
+ statsPerShare.setdefault(gshare, {"nq": 0, "nr": 0})
546
+ statsPerPQ.setdefault(computingSite, {})
547
+ statsPerPQ[computingSite].setdefault(gshare, {"nq": 0, "nr": 0})
548
+ if jobStatus in ["defined", "assigned", "activated", "starting"]:
549
+ statsPerPQ[computingSite][gshare]["nq"] += nJobs
550
+ statsPerShare[gshare]["nq"] += nJobs
551
+ elif jobStatus == "running":
552
+ statsPerPQ[computingSite][gshare]["nr"] += nJobs
553
+ statsPerShare[gshare]["nr"] += nJobs
554
+
555
+ # check
556
+ sql = (
557
+ "SELECT * FROM ("
558
+ "SELECT * FROM ("
559
+ "SELECT PandaID FROM ATLAS_PANDA.jobsDefined4 "
560
+ "WHERE computingSite=:computingSite "
561
+ "AND gshare=:gshare AND jobStatus IN (:jobStatus1,:jobStatus2,:jobStatus3,:jobStatus4) "
562
+ "UNION "
563
+ "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 "
564
+ "WHERE computingSite=:computingSite "
565
+ "AND gshare=:gshare AND jobStatus IN (:jobStatus1,:jobStatus2,:jobStatus3,:jobStatus4) "
566
+ ") ORDER BY PandaID "
567
+ ") WHERE rownum<:nRows "
568
+ )
569
+ nQueueLimitMap = {}
570
+ for computingSite, shareStat in statsPerPQ.items():
571
+ for gshare, nStat in shareStat.items():
572
+ # get limit
573
+ if gshare not in nQueueLimitMap:
574
+ key = f"FAST_REBRO_THRESHOLD_NQUEUE_{gshare}"
575
+ nQueueLimitMap[gshare] = taskBuffer.getConfigValue("rebroker", key)
576
+ nQueueLimit = nQueueLimitMap[gshare]
577
+ if not nQueueLimit:
578
+ dry_run = True
579
+ nQueueLimit = 10
580
+ else:
581
+ dry_run = False
582
+ ratioCheck = nStat["nr"] * ratioLimit < nStat["nq"]
583
+ statCheck = nStat["nq"] > nQueueLimit
584
+ fracCheck = nStat["nq"] > statsPerShare[gshare]["nq"] * fractionLimit
585
+ _logger.debug(
586
+ "{} in {} : nQueue({})>nRun({})*{}: {},"
587
+ " nQueue>nQueueThreshold({}):{}, nQueue>nQueue_total({})*{}:{}".format(
588
+ computingSite,
589
+ gshare,
590
+ nStat["nq"],
591
+ nStat["nr"],
592
+ ratioLimit,
593
+ ratioCheck,
594
+ nQueueLimit,
595
+ statCheck,
596
+ statsPerShare[gshare]["nq"],
597
+ fractionLimit,
598
+ fracCheck,
599
+ )
600
+ )
601
+ if ratioCheck and statCheck and fracCheck:
602
+ _logger.debug(f"{computingSite} overshoot in {gshare}")
603
+ if not dry_run:
604
+ # calculate excess
605
+ excess = min(
606
+ nStat["nq"] - nStat["nr"] * ratioLimit,
607
+ nStat["nq"] - nQueueLimit,
608
+ )
609
+ excess = min(
610
+ excess,
611
+ nStat["nq"] - statsPerShare[gshare]["nq"] * fractionLimit,
612
+ )
613
+ excess = int(math.ceil(excess))
614
+ var_map = {}
615
+ var_map[":computingSite"] = computingSite
616
+ var_map[":gshare"] = gshare
617
+ var_map[":jobStatus1"] = "defined"
618
+ var_map[":jobStatus2"] = "assigned"
619
+ var_map[":jobStatus3"] = "activated"
620
+ var_map[":jobStatus4"] = "starting"
621
+ var_map[":nRows"] = excess
622
+ status, res = taskBuffer.querySQLS(sql, var_map)
623
+ jediJobs = [p for p, in res]
624
+ _logger.debug(f"got {len(jediJobs)} jobs to kill excess={excess}")
625
+ if jediJobs:
626
+ nJob = 100
627
+ iJob = 0
628
+ while iJob < len(jediJobs):
629
+ _logger.debug(f"reassignJobs for JEDI at Nq/Nr overshoot site {computingSite} ({str(jediJobs[iJob:iJob + nJob])})")
630
+ Client.kill_jobs(jediJobs[iJob : iJob + nJob], 10, keep_unmerged=True)
631
+ iJob += nJob
632
+ except Exception as e:
633
+ _logger.error(f"failed with {str(e)} {traceback.format_exc()}")
634
+
635
+ # reassign activated jobs in inactive sites
636
+ inactiveTimeLimitSite = 2
637
+ inactiveTimeLimitJob = 4
638
+ inactivePrioLimit = 800
639
+ timeLimitSite = naive_utcnow() - datetime.timedelta(hours=inactiveTimeLimitSite)
640
+ timeLimitJob = naive_utcnow() - datetime.timedelta(hours=inactiveTimeLimitJob)
641
+ # get PandaIDs
642
+ sql = "SELECT distinct computingSite FROM ATLAS_PANDA.jobsActive4 "
643
+ sql += "WHERE prodSourceLabel=:prodSourceLabel "
644
+ sql += "AND ((modificationTime<:timeLimit AND jobStatus=:jobStatus1) "
645
+ sql += "OR (stateChangeTime<:timeLimit AND jobStatus=:jobStatus2)) "
646
+ sql += "AND lockedby=:lockedby AND currentPriority>=:prioLimit "
647
+ sql += "AND NOT processingType IN (:pType1) AND relocationFlag<>:rFlag1 "
648
+ var_map = {}
649
+ var_map[":prodSourceLabel"] = "managed"
650
+ var_map[":jobStatus1"] = "activated"
651
+ var_map[":jobStatus2"] = "starting"
652
+ var_map[":lockedby"] = "jedi"
653
+ var_map[":timeLimit"] = timeLimitJob
654
+ var_map[":prioLimit"] = inactivePrioLimit
655
+ var_map[":pType1"] = "pmerge"
656
+ var_map[":rFlag1"] = 2
657
+ stDS, resDS = taskBuffer.querySQLS(sql, var_map)
658
+ sqlSS = "SELECT laststart FROM ATLAS_PANDAMETA.siteData "
659
+ sqlSS += "WHERE site=:site AND flag=:flag AND hours=:hours "
660
+ sqlPI = "SELECT PandaID,eventService,attemptNr FROM ATLAS_PANDA.jobsActive4 "
661
+ sqlPI += "WHERE prodSourceLabel=:prodSourceLabel AND jobStatus IN (:jobStatus1,:jobStatus2) "
662
+ sqlPI += "AND (modificationTime<:timeLimit OR stateChangeTime<:timeLimit) "
663
+ sqlPI += "AND lockedby=:lockedby AND currentPriority>=:prioLimit "
664
+ sqlPI += "AND computingSite=:site AND NOT processingType IN (:pType1) AND relocationFlag<>:rFlag1 "
665
+ for (tmpSite,) in resDS:
666
+ if tmpSite in sitesToDisableReassign:
667
+ _logger.debug(f"skip reassignJobs at inactive site {tmpSite} since reassign is disabled")
668
+ continue
669
+ # check if the site is inactive
670
+ var_map = {}
671
+ var_map[":site"] = tmpSite
672
+ var_map[":flag"] = "production"
673
+ var_map[":hours"] = 3
674
+ stSS, resSS = taskBuffer.querySQLS(sqlSS, var_map)
675
+ if resSS is not None and len(resSS) > 0:
676
+ last_start = resSS[0][0]
677
+ else:
678
+ last_start = None
679
+ site_status = siteMapper.getSite(tmpSite).status
680
+ if stSS is True and ((last_start is not None and last_start < timeLimitSite) or site_status in ["offline", "test"]):
681
+ # get jobs
682
+ var_map = {}
683
+ var_map[":prodSourceLabel"] = "managed"
684
+ var_map[":jobStatus1"] = "activated"
685
+ var_map[":jobStatus2"] = "starting"
686
+ var_map[":lockedby"] = "jedi"
687
+ var_map[":timeLimit"] = timeLimitJob
688
+ var_map[":prioLimit"] = inactivePrioLimit
689
+ var_map[":site"] = tmpSite
690
+ var_map[":pType1"] = "pmerge"
691
+ var_map[":rFlag1"] = 2
692
+ stPI, resPI = taskBuffer.querySQLS(sqlPI, var_map)
693
+ jediJobs = []
694
+ # reassign
695
+ _logger.debug(f"reassignJobs for JEDI at inactive site {tmpSite} laststart={last_start} status={site_status}")
696
+ if resPI is not None:
697
+ for pandaID, eventService, attemptNr in resPI:
698
+ if eventService in [EventServiceUtils.esMergeJobFlagNumber]:
699
+ _logger.debug(f"retrying es merge {pandaID} at inactive site {tmpSite}")
700
+ taskBuffer.retryJob(
701
+ pandaID,
702
+ {},
703
+ getNewPandaID=True,
704
+ attemptNr=attemptNr,
705
+ recoverableEsMerge=True,
706
+ )
707
+ jediJobs.append(pandaID)
708
+ if len(jediJobs) != 0:
709
+ nJob = 100
710
+ iJob = 0
711
+ while iJob < len(jediJobs):
712
+ _logger.debug(f"reassignJobs for JEDI at inactive site {tmpSite} ({jediJobs[iJob:iJob + nJob]})")
713
+ Client.kill_jobs(jediJobs[iJob : iJob + nJob], 51, keep_unmerged=True)
714
+ iJob += nJob
715
+
716
+ # reassign defined jobs in defined table
717
+ timeoutValue = taskBuffer.getConfigValue("job_timeout", "TIMEOUT_defined", "pandaserver")
718
+ if not timeoutValue:
719
+ timeoutValue = 4
720
+ timeoutValue *= 60
721
+ timeLimit = naive_utcnow() - datetime.timedelta(minutes=timeoutValue)
722
+ # get PandaIDs
723
+ status, res = taskBuffer.lockJobsForReassign(
724
+ "ATLAS_PANDA.jobsDefined4",
725
+ timeLimit,
726
+ ["defined"],
727
+ ["managed", "test"],
728
+ [],
729
+ [],
730
+ [],
731
+ True,
732
+ )
733
+ jediJobs = []
734
+ if res is not None:
735
+ for id, lockedby in res:
736
+ if lockedby == "jedi":
737
+ jediJobs.append(id)
738
+
739
+ # reassign
740
+ _logger.debug(f"reassignJobs for JEDI defined jobs -> #{len(jediJobs)}")
741
+ if len(jediJobs) != 0:
742
+ nJob = 100
743
+ iJob = 0
744
+ while iJob < len(jediJobs):
745
+ _logger.debug(f"reassignJobs for JEDI defined jobs ({jediJobs[iJob:iJob + nJob]})")
746
+ Client.kill_jobs(jediJobs[iJob : iJob + nJob], 51, keep_unmerged=True)
747
+ iJob += nJob
748
+
749
+ # reassign stalled defined build and non-JEDI jobs
750
+ timeLimit = naive_utcnow() - datetime.timedelta(minutes=timeoutValue)
751
+ var_map = {}
752
+ var_map[":jobStatus"] = "defined"
753
+ var_map[":prodSourceLabel_p"] = "panda"
754
+ var_map[":timeLimit"] = timeLimit
755
+ status, res = taskBuffer.querySQLS(
756
+ "SELECT PandaID FROM ATLAS_PANDA.jobsDefined4 WHERE ((prodSourceLabel=:prodSourceLabel_p AND transformation LIKE '%build%') OR "
757
+ "lockedBy IS NULL) AND jobStatus=:jobStatus AND creationTime<:timeLimit ORDER BY PandaID",
758
+ var_map,
759
+ )
760
+ jobs = []
761
+ if res is not None:
762
+ for (id,) in res:
763
+ jobs.append(id)
764
+ # kill
765
+ if len(jobs):
766
+ Client.kill_jobs(jobs, 2)
767
+ _logger.debug(f"reassign stalled defined build and non-JEDI jobs with timeout {timeoutValue}min ({str(jobs)})")
768
+
769
+ # reassign long-waiting jobs in defined table
770
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=12)
771
+ status, res = taskBuffer.lockJobsForReassign("ATLAS_PANDA.jobsDefined4", timeLimit, [], ["managed"], [], [], [], True)
772
+ jediJobs = []
773
+ if res is not None:
774
+ for id, lockedby in res:
775
+ if lockedby == "jedi":
776
+ jediJobs.append(id)
777
+ else:
778
+ jobs.append(id)
779
+ # reassign
780
+ _logger.debug(f"reassignJobs for long JEDI in defined table -> #{len(jediJobs)}")
781
+ if len(jediJobs) != 0:
782
+ nJob = 100
783
+ iJob = 0
784
+ while iJob < len(jediJobs):
785
+ _logger.debug(f"reassignJobs for long JEDI in defined table ({jediJobs[iJob:iJob + nJob]})")
786
+ Client.kill_jobs(jediJobs[iJob : iJob + nJob], 51, keep_unmerged=True)
787
+ iJob += nJob
788
+
789
+ # reassign too long activated jobs in active table
790
+ timeLimit = naive_utcnow() - datetime.timedelta(days=2)
791
+ status, res = taskBuffer.lockJobsForReassign(
792
+ "ATLAS_PANDA.jobsActive4",
793
+ timeLimit,
794
+ ["activated"],
795
+ ["managed"],
796
+ [],
797
+ [],
798
+ [],
799
+ True,
800
+ onlyReassignable=True,
801
+ getEventService=True,
802
+ )
803
+ jediJobs = []
804
+ if res is not None:
805
+ for pandaID, lockedby, eventService, attemptNr, computingSite in res:
806
+ if computingSite in sitesToDisableReassign:
807
+ _logger.debug(f"skip reassignJobs for long activated PandaID={pandaID} since disabled at {computingSite}")
808
+ continue
809
+ if lockedby == "jedi":
810
+ if eventService in [EventServiceUtils.esMergeJobFlagNumber]:
811
+ _logger.debug("retrying {0} in long activated" % pandaID)
812
+ taskBuffer.retryJob(
813
+ pandaID,
814
+ {},
815
+ getNewPandaID=True,
816
+ attemptNr=attemptNr,
817
+ recoverableEsMerge=True,
818
+ )
819
+ jediJobs.append(pandaID)
820
+
821
+ _logger.debug(f"reassignJobs for long activated JEDI in active table -> #{len(jediJobs)}")
822
+ if len(jediJobs) != 0:
823
+ nJob = 100
824
+ iJob = 0
825
+ while iJob < len(jediJobs):
826
+ _logger.debug(f"reassignJobs for long activated JEDI in active table ({jediJobs[iJob:iJob + nJob]})")
827
+ Client.kill_jobs(jediJobs[iJob : iJob + nJob], 51, keep_unmerged=True)
828
+ iJob += nJob
829
+
830
+ # reassign too long starting jobs in active table
831
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=48)
832
+ status, res = taskBuffer.lockJobsForReassign(
833
+ "ATLAS_PANDA.jobsActive4",
834
+ timeLimit,
835
+ ["starting"],
836
+ ["managed"],
837
+ [],
838
+ [],
839
+ [],
840
+ True,
841
+ onlyReassignable=True,
842
+ useStateChangeTime=True,
843
+ getEventService=True,
844
+ )
845
+ jediJobs = []
846
+ if res is not None:
847
+ for pandaID, lockedby, eventService, attemptNr, computingSite in res:
848
+ if computingSite in sitesToDisableReassign:
849
+ _logger.debug(f"skip reassignJobs for long starting PandaID={pandaID} since disabled at {computingSite}")
850
+ continue
851
+ if lockedby == "jedi":
852
+ jediJobs.append(pandaID)
853
+
854
+ _logger.debug(f"reassignJobs for long starting JEDI in active table -> #{len(jediJobs)}")
855
+ if len(jediJobs) != 0:
856
+ nJob = 100
857
+ iJob = 0
858
+ while iJob < len(jediJobs):
859
+ _logger.debug(f"reassignJobs for long stating JEDI in active table ({jediJobs[iJob:iJob + nJob]})")
860
+ Client.kill_jobs(jediJobs[iJob : iJob + nJob], 51, keep_unmerged=True)
861
+ iJob += nJob
862
+
863
+ # kill too long-standing analysis jobs in active table
864
+ timeLimit = naive_utcnow() - datetime.timedelta(days=7)
865
+ var_map = {}
866
+ var_map[":prodSourceLabel1"] = "test"
867
+ var_map[":prodSourceLabel2"] = "panda"
868
+ var_map[":prodSourceLabel3"] = "user"
869
+ var_map[":modificationTime"] = timeLimit
870
+ status, res = taskBuffer.querySQLS(
871
+ "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE (prodSourceLabel=:prodSourceLabel1 OR prodSourceLabel=:prodSourceLabel2 OR prodSourceLabel=:prodSourceLabel3) AND modificationTime<:modificationTime ORDER BY PandaID",
872
+ var_map,
873
+ )
874
+ jobs = []
875
+ if res is not None:
876
+ for (id,) in res:
877
+ jobs.append(id)
878
+ # kill
879
+ if len(jobs):
880
+ Client.kill_jobs(jobs, 2)
881
+ _logger.debug(f"killJobs for Anal Active ({str(jobs)})")
882
+
883
+ # kill too long pending jobs
884
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=1)
885
+ var_map = {}
886
+ var_map[":jobStatus"] = "pending"
887
+ var_map[":creationTime"] = timeLimit
888
+ status, res = taskBuffer.querySQLS(
889
+ "SELECT PandaID FROM ATLAS_PANDA.jobsDefined4 WHERE jobStatus=:jobStatus AND creationTime<:creationTime ",
890
+ var_map,
891
+ )
892
+ jobs = []
893
+ if res is not None:
894
+ for (id,) in res:
895
+ jobs.append(id)
896
+ # kill
897
+ if len(jobs):
898
+ if len(jobs):
899
+ nJob = 100
900
+ iJob = 0
901
+ while iJob < len(jobs):
902
+ _logger.debug(f"killJobs for Pending ({str(jobs[iJob:iJob + nJob])})")
903
+ Client.kill_jobs(jobs[iJob : iJob + nJob], 4)
904
+ iJob += nJob
905
+
906
+ # kick waiting ES merge jobs which were generated from fake co-jumbo
907
+ timeLimit = naive_utcnow() - datetime.timedelta(minutes=10)
908
+ var_map = {}
909
+ var_map[":jobStatus"] = "waiting"
910
+ var_map[":creationTime"] = timeLimit
911
+ var_map[":esMerge"] = EventServiceUtils.esMergeJobFlagNumber
912
+ sql = "SELECT PandaID,computingSite FROM ATLAS_PANDA.jobsDefined4 WHERE jobStatus=:jobStatus AND creationTime<:creationTime "
913
+ sql += "AND eventService=:esMerge ORDER BY jediTaskID "
914
+ status, res = taskBuffer.querySQLS(sql, var_map)
915
+ jobsMap = {}
916
+ if res is not None:
917
+ for id, site in res:
918
+ if site not in jobsMap:
919
+ jobsMap[site] = []
920
+ jobsMap[site].append(id)
921
+ # kick
922
+ if len(jobsMap):
923
+ for site in jobsMap:
924
+ jobs = jobsMap[site]
925
+ nJob = 100
926
+ iJob = 0
927
+ while iJob < len(jobs):
928
+ _logger.debug(f"kick waiting ES merge ({str(jobs[iJob:iJob + nJob])})")
929
+ Client.kill_jobs(jobs[iJob : iJob + nJob], 2)
930
+ iJob += nJob
931
+
932
+ # kill too long waiting jobs
933
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=1)
934
+ var_map = {}
935
+ var_map[":jobStatus"] = "waiting"
936
+ var_map[":creationTime"] = timeLimit
937
+ var_map[":coJumbo"] = EventServiceUtils.coJumboJobFlagNumber
938
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsDefined4 WHERE jobStatus=:jobStatus AND creationTime<:creationTime "
939
+ sql += "AND (eventService IS NULL OR eventService<>:coJumbo) "
940
+ status, res = taskBuffer.querySQLS(sql, var_map)
941
+ jobs = []
942
+ if res is not None:
943
+ for (id,) in res:
944
+ jobs.append(id)
945
+ # kill
946
+ if len(jobs):
947
+ if len(jobs):
948
+ nJob = 100
949
+ iJob = 0
950
+ while iJob < len(jobs):
951
+ _logger.debug(f"killJobs for Waiting ({str(jobs[iJob:iJob + nJob])})")
952
+ Client.kill_jobs(jobs[iJob : iJob + nJob], 4)
953
+ iJob += nJob
954
+
955
+ # kill too long running ES jobs
956
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=24)
957
+ var_map = {}
958
+ var_map[":jobStatus1"] = "running"
959
+ var_map[":jobStatus2"] = "starting"
960
+ var_map[":timeLimit"] = timeLimit
961
+ var_map[":esJob"] = EventServiceUtils.esJobFlagNumber
962
+ var_map[":coJumbo"] = EventServiceUtils.coJumboJobFlagNumber
963
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE jobStatus IN (:jobStatus1,:jobStatus2) AND stateChangeTime<:timeLimit "
964
+ sql += "AND eventService IN (:esJob,:coJumbo) AND currentPriority>=900 "
965
+ status, res = taskBuffer.querySQLS(sql, var_map)
966
+ jobs = []
967
+ if res is not None:
968
+ for (id,) in res:
969
+ jobs.append(id)
970
+ # kill
971
+ if len(jobs):
972
+ nJob = 100
973
+ iJob = 0
974
+ while iJob < len(jobs):
975
+ _logger.debug(f"killJobs for long running ES jobs ({str(jobs[iJob:iJob + nJob])})")
976
+ Client.kill_jobs(
977
+ jobs[iJob : iJob + nJob],
978
+ 2,
979
+ keep_unmerged=True,
980
+ job_sub_status="es_toolong",
981
+ )
982
+ iJob += nJob
983
+
984
+ # kill too long running ES merge jobs
985
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=24)
986
+ var_map = {}
987
+ var_map[":jobStatus1"] = "running"
988
+ var_map[":jobStatus2"] = "starting"
989
+ var_map[":timeLimit"] = timeLimit
990
+ var_map[":esMergeJob"] = EventServiceUtils.esMergeJobFlagNumber
991
+ sql = "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE jobStatus IN (:jobStatus1,:jobStatus2) AND stateChangeTime<:timeLimit "
992
+ sql += "AND eventService=:esMergeJob "
993
+ status, res = taskBuffer.querySQLS(sql, var_map)
994
+ jobs = []
995
+ if res is not None:
996
+ for (id,) in res:
997
+ jobs.append(id)
998
+ # kill
999
+ if len(jobs):
1000
+ nJob = 100
1001
+ iJob = 0
1002
+ while iJob < len(jobs):
1003
+ _logger.debug(f"killJobs for long running ES merge jobs ({str(jobs[iJob:iJob + nJob])})")
1004
+ Client.kill_jobs(jobs[iJob : iJob + nJob], 2)
1005
+ iJob += nJob
1006
+
1007
+ # rebrokerage
1008
+ _logger.debug("Rebrokerage start")
1009
+
1010
+ # get timeout value
1011
+ timeoutVal = taskBuffer.getConfigValue("rebroker", "ANALY_TIMEOUT")
1012
+ if timeoutVal is None:
1013
+ timeoutVal = 12
1014
+ _logger.debug(f"timeout value : {timeoutVal}h")
1015
+ try:
1016
+ normalTimeLimit = naive_utcnow() - datetime.timedelta(hours=timeoutVal)
1017
+ sortTimeLimit = naive_utcnow() - datetime.timedelta(hours=3)
1018
+ sql = (
1019
+ "WITH p AS ("
1020
+ "SELECT MIN(PandaID) PandaID,jobDefinitionID,prodUserName,prodUserID,computingSite,jediTaskID,processingType,workingGroup "
1021
+ "FROM ATLAS_PANDA.jobsActive4 "
1022
+ "WHERE prodSourceLabel IN (:prodSourceLabel1,:prodSourceLabel2) "
1023
+ "AND jobStatus IN (:jobStatus1,:jobStatus2,:jobStatus3) "
1024
+ "AND jobsetID IS NOT NULL AND lockedBy=:lockedBy "
1025
+ "GROUP BY jobDefinitionID,prodUserName,prodUserID,computingSite,jediTaskID,processingType,workingGroup "
1026
+ ") "
1027
+ "SELECT /*+ INDEX (s JOBS_STATUSLOG_PANDAID_IDX) */ "
1028
+ "p.jobDefinitionID,p.prodUserName,p.prodUserID,p.computingSite,s.modificationTime,p.jediTaskID,p.processingType,p.workingGroup "
1029
+ "FROM p, ATLAS_PANDA.jobs_statuslog s "
1030
+ "WHERE s.PandaID=p.PandaID AND s.jobStatus=:s_jobStatus AND s.modificationTime<:modificationTime "
1031
+ )
1032
+ var_map = {}
1033
+ var_map[":prodSourceLabel1"] = "user"
1034
+ var_map[":prodSourceLabel2"] = "panda"
1035
+ var_map[":modificationTime"] = sortTimeLimit
1036
+ var_map[":lockedBy"] = "jedi"
1037
+ var_map[":jobStatus1"] = "activated"
1038
+ var_map[":jobStatus2"] = "dummy"
1039
+ var_map[":jobStatus3"] = "starting"
1040
+ var_map[":s_jobStatus"] = "activated"
1041
+ # get jobs older than threshold
1042
+ ret, res = taskBuffer.querySQLS(sql, var_map)
1043
+ resList = []
1044
+ keyList = set()
1045
+ if res is not None:
1046
+ for tmpItem in res:
1047
+ (
1048
+ jobDefinitionID,
1049
+ prodUserName,
1050
+ prodUserID,
1051
+ computingSite,
1052
+ maxTime,
1053
+ jediTaskID,
1054
+ processingType,
1055
+ workingGroup,
1056
+ ) = tmpItem
1057
+ tmpKey = (jediTaskID, jobDefinitionID)
1058
+ keyList.add(tmpKey)
1059
+ resList.append(tmpItem)
1060
+ # get stalled assigned job
1061
+ sqlA = "SELECT jobDefinitionID,prodUserName,prodUserID,computingSite,MAX(creationTime),jediTaskID,processingType,workingGroup "
1062
+ sqlA += "FROM ATLAS_PANDA.jobsDefined4 "
1063
+ sqlA += "WHERE prodSourceLabel IN (:prodSourceLabel1,:prodSourceLabel2) AND jobStatus IN (:jobStatus1,:jobStatus2) "
1064
+ sqlA += "AND creationTime<:modificationTime AND lockedBy=:lockedBy "
1065
+ sqlA += "GROUP BY jobDefinitionID,prodUserName,prodUserID,computingSite,jediTaskID,processingType,workingGroup "
1066
+ var_map = {}
1067
+ var_map[":prodSourceLabel1"] = "user"
1068
+ var_map[":prodSourceLabel2"] = "panda"
1069
+ var_map[":modificationTime"] = sortTimeLimit
1070
+ var_map[":lockedBy"] = "jedi"
1071
+ var_map[":jobStatus1"] = "assigned"
1072
+ var_map[":jobStatus2"] = "defined"
1073
+ retA, resA = taskBuffer.querySQLS(sqlA, var_map)
1074
+ if resA is not None:
1075
+ for tmpItem in resA:
1076
+ (
1077
+ jobDefinitionID,
1078
+ prodUserName,
1079
+ prodUserID,
1080
+ computingSite,
1081
+ maxTime,
1082
+ jediTaskID,
1083
+ processingType,
1084
+ workingGroup,
1085
+ ) = tmpItem
1086
+ tmpKey = (jediTaskID, jobDefinitionID)
1087
+ if tmpKey not in keyList:
1088
+ keyList.add(tmpKey)
1089
+ resList.append(tmpItem)
1090
+ # sql to check recent activity
1091
+ sql = "SELECT PandaID,stateChangeTime,jobStatus FROM %s "
1092
+ sql += "WHERE prodUserName=:prodUserName AND jobDefinitionID=:jobDefinitionID "
1093
+ sql += "AND computingSite=:computingSite AND jediTaskID=:jediTaskID "
1094
+ sql += "AND jobStatus NOT IN (:jobStatus1,:jobStatus2,:jobStatus3) "
1095
+ sql += "AND stateChangeTime>:modificationTime "
1096
+ sql += "AND rownum <= 1"
1097
+ # sql to get associated jobs with jediTaskID
1098
+ sqlJJ = "SELECT PandaID FROM %s "
1099
+ sqlJJ += "WHERE jediTaskID=:jediTaskID AND jobStatus IN (:jobS1,:jobS2,:jobS3,:jobS4,:jobS5) "
1100
+ sqlJJ += "AND jobDefinitionID=:jobDefID AND computingSite=:computingSite "
1101
+ timeoutMap = {}
1102
+ if resList != []:
1103
+ recentRuntimeLimit = naive_utcnow() - datetime.timedelta(hours=3)
1104
+ # loop over all user/jobID combinations
1105
+ iComb = 0
1106
+ nComb = len(resList)
1107
+ _logger.debug(f"total combinations = {nComb}")
1108
+ for (
1109
+ jobDefinitionID,
1110
+ prodUserName,
1111
+ prodUserID,
1112
+ computingSite,
1113
+ maxModificationTime,
1114
+ jediTaskID,
1115
+ processingType,
1116
+ workingGroup,
1117
+ ) in resList:
1118
+ # check if jobs with the jobID have run recently
1119
+ var_map = {}
1120
+ var_map[":jediTaskID"] = jediTaskID
1121
+ var_map[":computingSite"] = computingSite
1122
+ var_map[":prodUserName"] = prodUserName
1123
+ var_map[":jobDefinitionID"] = jobDefinitionID
1124
+ var_map[":modificationTime"] = recentRuntimeLimit
1125
+ var_map[":jobStatus1"] = "closed"
1126
+ var_map[":jobStatus2"] = "failed"
1127
+ var_map[":jobStatus3"] = "starting"
1128
+ _logger.debug(f" rebro:{iComb}/{nComb}:ID={jobDefinitionID}:{prodUserName} jediTaskID={jediTaskID} site={computingSite}")
1129
+ iComb += 1
1130
+ hasRecentJobs = False
1131
+ # check site
1132
+ if not siteMapper.checkSite(computingSite):
1133
+ _logger.debug(f" -> skip unknown site={computingSite}")
1134
+ continue
1135
+ # check site status
1136
+ tmpSiteStatus = siteMapper.getSite(computingSite).status
1137
+ if tmpSiteStatus not in ["offline", "test"]:
1138
+ if workingGroup:
1139
+ if workingGroup not in timeoutMap:
1140
+ tmp_timeoutVal = taskBuffer.getConfigValue("rebroker", f"ANALY_TIMEOUT_{workingGroup}")
1141
+ if tmp_timeoutVal:
1142
+ timeoutMap[workingGroup] = naive_utcnow() - datetime.timedelta(hours=tmp_timeoutVal)
1143
+ else:
1144
+ timeoutMap[workingGroup] = normalTimeLimit
1145
+ tmp_normalTimeLimit = timeoutMap[workingGroup]
1146
+ else:
1147
+ tmp_normalTimeLimit = normalTimeLimit
1148
+ # use normal time limit for normal site status
1149
+ if maxModificationTime > tmp_normalTimeLimit:
1150
+ _logger.debug(f" -> skip wait for normal timelimit={tmp_normalTimeLimit}<maxModTime={maxModificationTime}")
1151
+ continue
1152
+ for tableName in [
1153
+ "ATLAS_PANDA.jobsActive4",
1154
+ "ATLAS_PANDA.jobsArchived4",
1155
+ ]:
1156
+ retU, resU = taskBuffer.querySQLS(sql % tableName, var_map)
1157
+ if resU is None:
1158
+ # database error
1159
+ raise RuntimeError("failed to check modTime")
1160
+ if resU != []:
1161
+ # found recent jobs
1162
+ hasRecentJobs = True
1163
+ _logger.debug(f" -> skip due to recent activity {resU[0][0]} to {resU[0][2]} at {resU[0][1]}")
1164
+ break
1165
+ else:
1166
+ _logger.debug(f" -> immediate rebro due to site status={tmpSiteStatus}")
1167
+ if hasRecentJobs:
1168
+ # skip since some jobs have run recently
1169
+ continue
1170
+ else:
1171
+ if jediTaskID is None:
1172
+ _logger.debug(" -> rebro for normal task : no action")
1173
+ else:
1174
+ _logger.debug(" -> rebro for JEDI task")
1175
+ killJobs = []
1176
+ var_map = {}
1177
+ var_map[":jediTaskID"] = jediTaskID
1178
+ var_map[":jobDefID"] = jobDefinitionID
1179
+ var_map[":computingSite"] = computingSite
1180
+ var_map[":jobS1"] = "defined"
1181
+ var_map[":jobS2"] = "assigned"
1182
+ var_map[":jobS3"] = "activated"
1183
+ var_map[":jobS4"] = "dummy"
1184
+ var_map[":jobS5"] = "starting"
1185
+ for tableName in [
1186
+ "ATLAS_PANDA.jobsDefined4",
1187
+ "ATLAS_PANDA.jobsActive4",
1188
+ ]:
1189
+ retJJ, resJJ = taskBuffer.querySQLS(sqlJJ % tableName, var_map)
1190
+ for (tmpPandaID,) in resJJ:
1191
+ killJobs.append(tmpPandaID)
1192
+ # reverse sort to kill buildJob in the end
1193
+ killJobs.sort()
1194
+ killJobs.reverse()
1195
+ # kill to reassign
1196
+ taskBuffer.killJobs(killJobs, "JEDI", "51", True)
1197
+ except Exception as e:
1198
+ _logger.error(f"rebrokerage failed with {str(e)} : {traceback.format_exc()}")
1199
+
1200
+ # kill too long running jobs
1201
+ timeLimit = naive_utcnow() - datetime.timedelta(days=21)
1202
+ status, res = taskBuffer.querySQLS(
1203
+ "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE creationTime<:creationTime",
1204
+ {":creationTime": timeLimit},
1205
+ )
1206
+ jobs = []
1207
+ if res is not None:
1208
+ for (id,) in res:
1209
+ jobs.append(id)
1210
+ # kill
1211
+ if len(jobs):
1212
+ nJob = 100
1213
+ iJob = 0
1214
+ while iJob < len(jobs):
1215
+ # set tobekill
1216
+ _logger.debug(f"killJobs for Running ({jobs[iJob:iJob + nJob]})")
1217
+ Client.kill_jobs(jobs[iJob : iJob + nJob], 2)
1218
+ # run watcher
1219
+ for id in jobs[iJob : iJob + nJob]:
1220
+ thr = Watcher(
1221
+ taskBuffer,
1222
+ id,
1223
+ single=True,
1224
+ sitemapper=siteMapper,
1225
+ sleepTime=60 * 24 * 21,
1226
+ )
1227
+ thr.start()
1228
+ thr.join()
1229
+ time.sleep(1)
1230
+ iJob += nJob
1231
+ time.sleep(10)
1232
+
1233
+ # kill too long throttled jobs
1234
+ timeLimit = naive_utcnow() - datetime.timedelta(days=7)
1235
+ var_map = {}
1236
+ var_map[":jobStatus"] = "throttled"
1237
+ var_map[":creationTime"] = timeLimit
1238
+ status, res = taskBuffer.querySQLS(
1239
+ "SELECT PandaID FROM ATLAS_PANDA.jobsActive4 WHERE jobStatus=:jobStatus AND creationTime<:creationTime ",
1240
+ var_map,
1241
+ )
1242
+ jobs = []
1243
+ if res is not None:
1244
+ for (id,) in res:
1245
+ jobs.append(id)
1246
+ # kill
1247
+ if len(jobs):
1248
+ Client.kill_jobs(jobs, 2)
1249
+ _logger.debug(f"killJobs for throttled ({str(jobs)})")
1250
+
1251
+ # check if merge job is valid
1252
+ _logger.debug("kill invalid pmerge")
1253
+ var_map = {}
1254
+ var_map[":processingType"] = "pmerge"
1255
+ var_map[":timeLimit"] = naive_utcnow() - datetime.timedelta(minutes=30)
1256
+ sql = "SELECT PandaID,jediTaskID FROM ATLAS_PANDA.jobsDefined4 WHERE processingType=:processingType AND modificationTime<:timeLimit "
1257
+ sql += "UNION "
1258
+ sql += "SELECT PandaID,jediTaskID FROM ATLAS_PANDA.jobsActive4 WHERE processingType=:processingType AND modificationTime<:timeLimit "
1259
+ status, res = taskBuffer.querySQLS(sql, var_map)
1260
+ nPmerge = 0
1261
+ badPmerge = 0
1262
+ _logger.debug(f"check {len(res)} pmerge")
1263
+ for pandaID, jediTaskID in res:
1264
+ nPmerge += 1
1265
+ isValid, tmpMsg = taskBuffer.isValidMergeJob(pandaID, jediTaskID)
1266
+ if isValid is False:
1267
+ _logger.debug(f"kill pmerge {pandaID} since {tmpMsg} gone")
1268
+ taskBuffer.killJobs(
1269
+ [pandaID],
1270
+ f"killed since pre-merge job {tmpMsg} gone",
1271
+ "52",
1272
+ True,
1273
+ )
1274
+ badPmerge += 1
1275
+ _logger.debug(f"killed invalid pmerge {badPmerge}/{nPmerge}")
1276
+
1277
+ # cleanup of jumbo jobs
1278
+ _logger.debug("jumbo job cleanup")
1279
+ res = taskBuffer.cleanupJumboJobs()
1280
+ _logger.debug(res)
1281
+
1282
+ _memoryCheck("delete XML")
1283
+
1284
+ # delete old files in DA cache
1285
+ timeLimit = naive_utcnow() - datetime.timedelta(days=7)
1286
+ files = os.listdir(panda_config.cache_dir)
1287
+ for file in files:
1288
+ # skip special test file
1289
+ if file == "sources.72c48dc5-f055-43e5-a86e-4ae9f8ea3497.tar.gz":
1290
+ continue
1291
+ if file == "sources.090f3f51-fc81-4e80-9749-a5e4b2bd58de.tar.gz":
1292
+ continue
1293
+ try:
1294
+ # get timestamp
1295
+ timestamp = datetime.datetime.fromtimestamp(os.stat(f"{panda_config.cache_dir}/{file}").st_mtime)
1296
+ # delete
1297
+ if timestamp < timeLimit:
1298
+ _logger.debug(f"delete {file} ")
1299
+ os.remove(f"{panda_config.cache_dir}/{file}")
1300
+ except Exception:
1301
+ pass
1302
+
1303
+ _memoryCheck("delete core")
1304
+
1305
+ # delete core
1306
+ dirName = f"{panda_config.logdir}/.."
1307
+ for file in os.listdir(dirName):
1308
+ if file.startswith("core."):
1309
+ _logger.debug(f"delete {file} ")
1310
+ try:
1311
+ os.remove(f"{dirName}/{file}")
1312
+ except Exception:
1313
+ pass
1314
+
1315
+ # sandbox
1316
+ _logger.debug("Touch sandbox")
1317
+ timeLimit = naive_utcnow() - datetime.timedelta(days=1)
1318
+ sqlC = (
1319
+ "SELECT hostName,fileName,creationTime,userName FROM ATLAS_PANDAMETA.userCacheUsage "
1320
+ "WHERE creationTime>:timeLimit AND creationTime>modificationTime "
1321
+ "AND (fileName like 'sources%' OR fileName like 'jobO%') "
1322
+ )
1323
+ sqlU = "UPDATE ATLAS_PANDAMETA.userCacheUsage SET modificationTime=CURRENT_DATE " "WHERE userName=:userName AND fileName=:fileName "
1324
+ status, res = taskBuffer.querySQLS(sqlC, {":timeLimit": timeLimit})
1325
+ if res is None:
1326
+ _logger.error("failed to get files")
1327
+ elif len(res) > 0:
1328
+ _logger.debug(f"{len(res)} files to touch")
1329
+ for hostName, fileName, creationTime, userName in res:
1330
+ base_url = f"https://{hostName}:{panda_config.pserverport}"
1331
+ _logger.debug(f"touch {fileName} on {hostName} created at {creationTime}")
1332
+ s, o = Client.touch_file(base_url, fileName)
1333
+ _logger.debug(o)
1334
+ if o == "True":
1335
+ var_map = dict()
1336
+ var_map[":userName"] = userName
1337
+ var_map[":fileName"] = fileName
1338
+ taskBuffer.querySQLS(sqlU, var_map)
1339
+
1340
+ _logger.debug("Check sandbox")
1341
+ timeLimit = naive_utcnow() - datetime.timedelta(days=1)
1342
+ expireLimit = naive_utcnow() - datetime.timedelta(days=30)
1343
+ sqlD = "DELETE FROM ATLAS_PANDAMETA.userCacheUsage WHERE userName=:userName AND fileName=:fileName "
1344
+ nRange = 100
1345
+ for i in range(nRange):
1346
+ _logger.debug(f"{nRange}/{i} {len(res)} files to check")
1347
+ res = taskBuffer.getLockSandboxFiles(timeLimit, 1000)
1348
+ if res is None:
1349
+ _logger.error("failed to get files")
1350
+ break
1351
+ elif len(res) == 0:
1352
+ break
1353
+ for userName, hostName, fileName, creationTime, modificationTime in res:
1354
+ url = f"https://{hostName}:{panda_config.pserverport}/cache/{fileName}"
1355
+ _logger.debug(f"checking {url} created at {creationTime}")
1356
+ toDelete = False
1357
+ try:
1358
+ x = requests.head(url, verify=False)
1359
+ _logger.debug(f"code {x.status_code}")
1360
+ if x.status_code == 404:
1361
+ _logger.debug("delete")
1362
+ toDelete = True
1363
+ except Exception as e:
1364
+ _logger.debug(f"failed with {str(e)}")
1365
+ if creationTime < expireLimit:
1366
+ toDelete = True
1367
+ _logger.debug(f"delete due to creationTime={creationTime}")
1368
+ # update or delete
1369
+ var_map = dict()
1370
+ var_map[":userName"] = userName
1371
+ var_map[":fileName"] = fileName
1372
+ if toDelete:
1373
+ taskBuffer.querySQLS(sqlD, var_map)
1374
+ else:
1375
+ _logger.debug("keep")
1376
+
1377
+ _memoryCheck("end")
1378
+
1379
+ # stop taskBuffer if created inside this script
1380
+ taskBuffer.cleanup(requester=requester_id)
1381
+
1382
+ _logger.debug("===================== end =====================")
1383
+
1384
+
1385
+ # run
1386
+ if __name__ == "__main__":
1387
+ main(argv=sys.argv)