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,1154 @@
1
+ import datetime
2
+ import os
3
+ import re
4
+ import sys
5
+ import threading
6
+ import time
7
+ import traceback
8
+
9
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
10
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
11
+ from pandacommon.pandautils.thread_utils import GenericThread
12
+
13
+ import pandaserver.taskbuffer.ErrorCode
14
+ from pandaserver.brokerage.SiteMapper import SiteMapper
15
+ from pandaserver.config import panda_config
16
+ from pandaserver.dataservice import DataServiceUtils
17
+ from pandaserver.dataservice.closer import Closer
18
+ from pandaserver.dataservice.DataServiceUtils import select_scope
19
+ from pandaserver.dataservice.ddm import rucioAPI
20
+ from pandaserver.dataservice.finisher import Finisher
21
+ from pandaserver.taskbuffer import EventServiceUtils
22
+
23
+ _logger = PandaLogger().getLogger("datasetManager")
24
+
25
+ TRANSFER_TIMEOUT_HI_PRIORITY = 2
26
+ TRANSFER_TIMEOUT_LO_PRIORITY = 6
27
+
28
+
29
+ def main(tbuf=None, **kwargs):
30
+ _logger.debug("===================== start =====================")
31
+
32
+ # memory checker
33
+ def _memoryCheck(str):
34
+ try:
35
+ proc_status = f"/proc/{os.getpid()}/status"
36
+ procfile = open(proc_status)
37
+ name = ""
38
+ vmSize = ""
39
+ vmRSS = ""
40
+ # extract Name,VmSize,VmRSS
41
+ for line in procfile:
42
+ if line.startswith("Name:"):
43
+ name = line.split()[-1]
44
+ continue
45
+ if line.startswith("VmSize:"):
46
+ vmSize = ""
47
+ for item in line.split()[1:]:
48
+ vmSize += item
49
+ continue
50
+ if line.startswith("VmRSS:"):
51
+ vmRSS = ""
52
+ for item in line.split()[1:]:
53
+ vmRSS += item
54
+ continue
55
+ procfile.close()
56
+ _logger.debug(f"MemCheck - {os.getpid()} Name={name} VSZ={vmSize} RSS={vmRSS} : {str}")
57
+ except Exception:
58
+ type, value, traceBack = sys.exc_info()
59
+ _logger.error(f"memoryCheck() : {type} {value}")
60
+ _logger.debug(f"MemCheck - {os.getpid()} unknown : {str}")
61
+ return
62
+
63
+ _memoryCheck("start")
64
+
65
+ from pandaserver.taskbuffer.TaskBuffer import taskBuffer
66
+
67
+ requester_id = GenericThread().get_full_id(__name__, sys.modules[__name__].__file__)
68
+ taskBuffer.init(
69
+ panda_config.dbhost,
70
+ panda_config.dbpasswd,
71
+ nDBConnection=1,
72
+ useTimeout=True,
73
+ requester=requester_id,
74
+ )
75
+ # else:
76
+ # taskBuffer = tbuf
77
+
78
+ # instantiate sitemapper
79
+ siteMapper = SiteMapper(taskBuffer)
80
+
81
+ # list with lock
82
+ class ListWithLock:
83
+ def __init__(self):
84
+ self.lock = threading.Lock()
85
+ self.list = []
86
+
87
+ def __contains__(self, item):
88
+ self.lock.acquire()
89
+ ret = self.list.__contains__(item)
90
+ self.lock.release()
91
+ return ret
92
+
93
+ def append(self, item):
94
+ appended = False
95
+ self.lock.acquire()
96
+ if item not in self.list:
97
+ self.list.append(item)
98
+ appended = True
99
+ self.lock.release()
100
+ return appended
101
+
102
+ # list of dis datasets to be deleted
103
+ deletedDisList = ListWithLock()
104
+
105
+ # set tobedeleted to dis dataset
106
+ def setTobeDeletedToDis(subDsName):
107
+ try:
108
+ # only production sub datasets
109
+ if subDsName.startswith("user") or subDsName.startswith("group") or re.search("_sub\d+$", subDsName) is None:
110
+ return
111
+ # get _dis names with _sub
112
+ disNameList = taskBuffer.getAssociatedDisDatasets(subDsName)
113
+ _logger.debug(f"setTobeDeletedToDis : sub:{subDsName} has dis:{str(disNameList)}")
114
+ # loop over all _dis datasets
115
+ for tmpDisName in disNameList:
116
+ # try to append to locked list
117
+ if not deletedDisList.append(tmpDisName):
118
+ # another thread already took care of the _dis
119
+ continue
120
+ # skip non _dis
121
+ if re.search("_dis\d+$", tmpDisName) is None:
122
+ continue
123
+ # get dataset
124
+ _logger.debug(f"setTobeDeletedToDis : try to get {tmpDisName} in DB")
125
+ tmpDS = taskBuffer.queryDatasetWithMap({"name": tmpDisName})
126
+ if tmpDS is None:
127
+ _logger.error(f"setTobeDeletedToDis : cannot get {tmpDisName} in DB")
128
+ continue
129
+ # check status
130
+ if tmpDS.status in ["tobedeleted", "deleted"]:
131
+ _logger.debug(f"setTobeDeletedToDis : skip {tmpDisName} since status={tmpDS.status}")
132
+ continue
133
+ # check the number of failed jobs associated to the _dis
134
+ if tmpDS.currentfiles == 0:
135
+ # all succeeded
136
+ tmpDS.status = "deleting"
137
+ excStatus = "deleted"
138
+ else:
139
+ # some failed, to reduce the lifetime
140
+ tmpDS.status = "shortening"
141
+ excStatus = "shortened"
142
+ # update dataset
143
+ retU = taskBuffer.updateDatasets(
144
+ [tmpDS],
145
+ withLock=True,
146
+ withCriteria="status<>:crStatus",
147
+ criteriaMap={":crStatus": excStatus},
148
+ )
149
+ _logger.debug(f"setTobeDeletedToDis : set {tmpDS.status} to {tmpDisName} with {str(retU)}")
150
+ except Exception:
151
+ errType, errValue = sys.exc_info()[:2]
152
+ _logger.error(f"setTobeDeletedToDis : {subDsName} {errType} {errValue}")
153
+
154
+ # thread pool
155
+ class ThreadPool:
156
+ def __init__(self):
157
+ self.lock = threading.Lock()
158
+ self.list = []
159
+
160
+ def add(self, obj):
161
+ self.lock.acquire()
162
+ self.list.append(obj)
163
+ self.lock.release()
164
+
165
+ def remove(self, obj):
166
+ self.lock.acquire()
167
+ self.list.remove(obj)
168
+ self.lock.release()
169
+
170
+ def join(self):
171
+ self.lock.acquire()
172
+ thrlist = tuple(self.list)
173
+ self.lock.release()
174
+ for thr in thrlist:
175
+ thr.join()
176
+
177
+ # thread to close dataset
178
+ class CloserThr(threading.Thread):
179
+ def __init__(self, lock, proxyLock, datasets, pool):
180
+ threading.Thread.__init__(self)
181
+ self.datasets = datasets
182
+ self.lock = lock
183
+ self.proxyLock = proxyLock
184
+ self.pool = pool
185
+ self.pool.add(self)
186
+
187
+ def run(self):
188
+ self.lock.acquire()
189
+ try:
190
+ # loop over all datasets
191
+ for vuid, name, modDate in self.datasets:
192
+ _logger.debug(f"Close {modDate} {name}")
193
+ dsExists = True
194
+ if name.startswith("user.") or name.startswith("group.") or name.startswith("hc_test.") or name.startswith("panda.um."):
195
+ dsExists = False
196
+ if dsExists:
197
+ # check if dataset exists
198
+ status, out = rucioAPI.get_metadata(name)
199
+ if status is True:
200
+ if out is not None:
201
+ try:
202
+ rucioAPI.close_dataset(name)
203
+ status = True
204
+ except Exception:
205
+ errtype, errvalue = sys.exc_info()[:2]
206
+ out = f"failed to freeze : {errtype} {errvalue}"
207
+ status = False
208
+ else:
209
+ # dataset not exist
210
+ status, out = True, ""
211
+ dsExists = False
212
+ else:
213
+ status, out = True, ""
214
+ if not status:
215
+ _logger.error(f"{name} failed to close with {out}")
216
+ else:
217
+ self.proxyLock.acquire()
218
+ varMap = {}
219
+ varMap[":vuid"] = vuid
220
+ varMap[":newstatus"] = "completed"
221
+ varMap[":oldstatus"] = "tobeclosed"
222
+ taskBuffer.querySQLS(
223
+ "UPDATE ATLAS_PANDA.Datasets SET status=:newstatus,modificationdate=CURRENT_DATE WHERE vuid=:vuid AND status=:oldstatus",
224
+ varMap,
225
+ )
226
+ self.proxyLock.release()
227
+ # set tobedeleted to dis
228
+ setTobeDeletedToDis(name)
229
+ # skip if dataset is not real
230
+ if not dsExists:
231
+ continue
232
+ # count # of files
233
+ status, out = rucioAPI.get_number_of_files(name)
234
+ if status is not True:
235
+ if status is False:
236
+ _logger.error(out)
237
+ else:
238
+ _logger.debug(out)
239
+ try:
240
+ nFile = int(out)
241
+ if nFile == 0:
242
+ # erase dataset
243
+ _logger.debug(f"erase {name}")
244
+ status, out = rucioAPI.erase_dataset(name)
245
+ _logger.debug(f"OK with {name}")
246
+ except Exception:
247
+ pass
248
+ except Exception:
249
+ pass
250
+ self.pool.remove(self)
251
+ self.lock.release()
252
+
253
+ # close datasets
254
+ _logger.debug("==== close datasets ====")
255
+ timeLimitU = naive_utcnow() - datetime.timedelta(minutes=1)
256
+ timeLimitL = naive_utcnow() - datetime.timedelta(days=3)
257
+ closeLock = threading.Semaphore(5)
258
+ closeProxyLock = threading.Lock()
259
+ closeThreadPool = ThreadPool()
260
+ maxRows = 100000
261
+ while True:
262
+ # lock
263
+ closeLock.acquire()
264
+ # get datasets
265
+ closeProxyLock.acquire()
266
+ varMap = {}
267
+ varMap[":modificationdateU"] = timeLimitU
268
+ varMap[":modificationdateL"] = timeLimitL
269
+ varMap[":type"] = "output"
270
+ varMap[":status"] = "tobeclosed"
271
+ sqlQuery = f"type=:type AND status=:status AND (modificationdate BETWEEN :modificationdateL AND :modificationdateU) AND rownum <= {maxRows}"
272
+ res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
273
+ if res is None:
274
+ _logger.debug(f"# of datasets to be closed: {res}")
275
+ else:
276
+ _logger.debug(f"# of datasets to be closed: {len(res)}")
277
+ if res is None or len(res) == 0:
278
+ closeProxyLock.release()
279
+ closeLock.release()
280
+ break
281
+ # release
282
+ closeProxyLock.release()
283
+ closeLock.release()
284
+ # run thread
285
+ iRows = 0
286
+ nRows = 500
287
+ while iRows < len(res):
288
+ closerThr = CloserThr(closeLock, closeProxyLock, res[iRows : iRows + nRows], closeThreadPool)
289
+ closerThr.start()
290
+ iRows += nRows
291
+ closeThreadPool.join()
292
+ if len(res) < maxRows:
293
+ break
294
+
295
+ # thread to freeze dataset
296
+ class Freezer(threading.Thread):
297
+ def __init__(self, lock, proxyLock, datasets, pool):
298
+ threading.Thread.__init__(self)
299
+ self.datasets = datasets
300
+ self.lock = lock
301
+ self.proxyLock = proxyLock
302
+ self.pool = pool
303
+ self.pool.add(self)
304
+
305
+ def run(self):
306
+ self.lock.acquire()
307
+ try:
308
+ for vuid, name, modDate in self.datasets:
309
+ _logger.debug(f"Freezer start {modDate} {name}")
310
+ self.proxyLock.acquire()
311
+ retF, resF = taskBuffer.querySQLS(
312
+ "SELECT /*+ index(tab FILESTABLE4_DESTDBLOCK_IDX) */ PandaID,status FROM ATLAS_PANDA.filesTable4 tab WHERE destinationDBlock=:destinationDBlock ",
313
+ {":destinationDBlock": name},
314
+ )
315
+ self.proxyLock.release()
316
+ if isinstance(retF, int) and retF < 0:
317
+ _logger.error("SQL error")
318
+ else:
319
+ allFinished = True
320
+ onePandaID = None
321
+ for tmpPandaID, tmpFileStatus in resF:
322
+ onePandaID = tmpPandaID
323
+ if tmpFileStatus not in [
324
+ "ready",
325
+ "failed",
326
+ "skipped",
327
+ "merging",
328
+ "finished",
329
+ ]:
330
+ allFinished = False
331
+ break
332
+ # check sub datasets in the jobset for event service job
333
+ if allFinished:
334
+ self.proxyLock.acquire()
335
+ tmpJobs = taskBuffer.getFullJobStatus([onePandaID])
336
+ self.proxyLock.release()
337
+ if len(tmpJobs) > 0 and tmpJobs[0] is not None:
338
+ if EventServiceUtils.isEventServiceMerge(tmpJobs[0]):
339
+ self.proxyLock.acquire()
340
+ cThr = Closer(taskBuffer, [], tmpJobs[0])
341
+ allFinished = cThr.checkSubDatasetsInJobset()
342
+ self.proxyLock.release()
343
+ _logger.debug(f"closer checked sub datasets in the jobset for {name} : {allFinished}")
344
+ # no files in filesTable
345
+ if allFinished:
346
+ _logger.debug(f"freeze {name} ")
347
+ dsExists = True
348
+ if name.startswith("user.") or name.startswith("group.") or name.startswith("hc_test.") or name.startswith("panda.um."):
349
+ dsExists = False
350
+ if name.startswith("panda.um."):
351
+ self.proxyLock.acquire()
352
+ retMer, resMer = taskBuffer.querySQLS(
353
+ "SELECT /*+ index(tab FILESTABLE4_DESTDBLOCK_IDX) */ PandaID FROM ATLAS_PANDA.filesTable4 tab WHERE destinationDBlock=:destinationDBlock AND status IN (:statusM,:statusF) ",
354
+ {
355
+ ":destinationDBlock": name,
356
+ ":statusM": "merging",
357
+ ":statusF": "failed",
358
+ },
359
+ )
360
+ self.proxyLock.release()
361
+ if resMer is not None and len(resMer) > 0:
362
+ mergeID = resMer[0][0]
363
+ # get merging jobs
364
+ self.proxyLock.acquire()
365
+ mergingJobs = taskBuffer.peekJobs(
366
+ [mergeID],
367
+ fromDefined=False,
368
+ fromArchived=False,
369
+ fromWaiting=False,
370
+ )
371
+ self.proxyLock.release()
372
+ mergeJob = mergingJobs[0]
373
+ if mergeJob is not None:
374
+ tmpDestDBlocks = []
375
+ # get destDBlock
376
+ for tmpFile in mergeJob.Files:
377
+ if tmpFile.type in ["output", "log"]:
378
+ if tmpFile.destinationDBlock not in tmpDestDBlocks:
379
+ tmpDestDBlocks.append(tmpFile.destinationDBlock)
380
+ # run
381
+ _logger.debug(f"start JEDI closer for {name} ")
382
+ self.proxyLock.acquire()
383
+ cThr = Closer(taskBuffer, tmpDestDBlocks, mergeJob)
384
+ cThr.run()
385
+ self.proxyLock.release()
386
+ _logger.debug(f"end JEDI closer for {name} ")
387
+ continue
388
+ else:
389
+ _logger.debug(f"failed to get merging job for {name} ")
390
+ else:
391
+ _logger.debug(f"failed to get merging file for {name} ")
392
+ status, out = True, ""
393
+ elif dsExists:
394
+ # check if dataset exists
395
+ status, out = rucioAPI.get_metadata(name)
396
+ if status is True:
397
+ if out is not None:
398
+ try:
399
+ rucioAPI.close_dataset(name)
400
+ status = True
401
+ except Exception:
402
+ errtype, errvalue = sys.exc_info()[:2]
403
+ out = f"failed to freeze : {errtype} {errvalue}"
404
+ status = False
405
+ else:
406
+ # dataset not exist
407
+ status, out = True, ""
408
+ dsExists = False
409
+ else:
410
+ status, out = True, ""
411
+ if not status:
412
+ _logger.error(f"{name} failed to freeze with {out}")
413
+ else:
414
+ self.proxyLock.acquire()
415
+ varMap = {}
416
+ varMap[":vuid"] = vuid
417
+ varMap[":status"] = "completed"
418
+ taskBuffer.querySQLS(
419
+ "UPDATE ATLAS_PANDA.Datasets SET status=:status,modificationdate=CURRENT_DATE WHERE vuid=:vuid",
420
+ varMap,
421
+ )
422
+ self.proxyLock.release()
423
+ if name.startswith("panda.um.") or not dsExists:
424
+ continue
425
+ # set tobedeleted to dis
426
+ setTobeDeletedToDis(name)
427
+ # count # of files
428
+ status, out = rucioAPI.get_number_of_files(name)
429
+ if status is not True:
430
+ if status is False:
431
+ _logger.error(out)
432
+ else:
433
+ _logger.debug(out)
434
+ try:
435
+ nFile = int(out)
436
+ _logger.debug(nFile)
437
+ if nFile == 0:
438
+ # erase dataset
439
+ _logger.debug(f"erase {name}")
440
+ status, out = rucioAPI.erase_dataset(name)
441
+ _logger.debug(f"OK with {name}")
442
+ except Exception:
443
+ pass
444
+ else:
445
+ _logger.debug(f"wait {name} ")
446
+ self.proxyLock.acquire()
447
+ taskBuffer.querySQLS(
448
+ "UPDATE ATLAS_PANDA.Datasets SET modificationdate=CURRENT_DATE WHERE vuid=:vuid",
449
+ {":vuid": vuid},
450
+ )
451
+ self.proxyLock.release()
452
+ _logger.debug(f"end {name} ")
453
+ except Exception:
454
+ errStr = traceback.format_exc()
455
+ _logger.error(errStr)
456
+ self.pool.remove(self)
457
+ self.lock.release()
458
+
459
+ # freeze dataset
460
+ _logger.debug("==== freeze datasets ====")
461
+ timeLimitRU = naive_utcnow() - datetime.timedelta(hours=3)
462
+ timeLimitRL = naive_utcnow() - datetime.timedelta(hours=12)
463
+ timeLimitU = naive_utcnow() - datetime.timedelta(hours=6)
464
+ timeLimitL = naive_utcnow() - datetime.timedelta(days=14)
465
+ # reset doing so that Closer can update unmerged datasets
466
+ sql = "SELECT name FROM ATLAS_PANDA.Datasets "
467
+ sql += "WHERE type=:type AND (modificationdate BETWEEN :modificationdateRL AND :modificationdateRU) AND subType=:subType AND status=:oldStatus "
468
+ varMap = {}
469
+ varMap[":modificationdateRU"] = timeLimitRU
470
+ varMap[":modificationdateRL"] = timeLimitRL
471
+ varMap[":type"] = "output"
472
+ varMap[":subType"] = "sub"
473
+ varMap[":oldStatus"] = "doing"
474
+ retReset, resReset = taskBuffer.querySQLS(sql, varMap)
475
+ sql = "UPDATE ATLAS_PANDA.Datasets SET status=:newStatus,modificationdate=:modificationdateU WHERE name=:name AND status=:oldStatus "
476
+ if resReset is not None:
477
+ for (name,) in resReset:
478
+ varMap = {}
479
+ varMap[":name"] = name
480
+ varMap[":oldStatus"] = "doing"
481
+ varMap[":newStatus"] = "running"
482
+ varMap[":modificationdateU"] = timeLimitU
483
+ _logger.debug(f"reset {name} to freeze")
484
+ taskBuffer.querySQLS(sql, varMap)
485
+ # loop for freezer
486
+ freezeLock = threading.Semaphore(5)
487
+ freezeProxyLock = threading.Lock()
488
+ freezeThreadPool = ThreadPool()
489
+ maxRows = 100000
490
+ while True:
491
+ # lock
492
+ freezeLock.acquire()
493
+ # get datasets
494
+ sqlQuery = (
495
+ "type=:type AND status IN (:status1,:status2,:status3,:status4,:status5) "
496
+ + f"AND (modificationdate BETWEEN :modificationdateL AND :modificationdateU) AND subType=:subType AND rownum <= {maxRows}"
497
+ )
498
+ varMap = {}
499
+ varMap[":modificationdateU"] = timeLimitU
500
+ varMap[":modificationdateL"] = timeLimitL
501
+ varMap[":type"] = "output"
502
+ varMap[":status1"] = "running"
503
+ varMap[":status2"] = "created"
504
+ varMap[":status3"] = "defined"
505
+ varMap[":status4"] = "locked"
506
+ varMap[":status5"] = "doing"
507
+ varMap[":subType"] = "sub"
508
+ freezeProxyLock.acquire()
509
+ res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
510
+ if res is None:
511
+ _logger.debug(f"# of datasets to be frozen: {res}")
512
+ else:
513
+ _logger.debug(f"# of datasets to be frozen: {len(res)}")
514
+ if res is None or len(res) == 0:
515
+ freezeProxyLock.release()
516
+ freezeLock.release()
517
+ break
518
+ freezeProxyLock.release()
519
+ # release
520
+ freezeLock.release()
521
+ # run freezer
522
+ iRows = 0
523
+ nRows = 500
524
+ while iRows < len(res):
525
+ freezer = Freezer(
526
+ freezeLock,
527
+ freezeProxyLock,
528
+ res[iRows : iRows + nRows],
529
+ freezeThreadPool,
530
+ )
531
+ freezer.start()
532
+ iRows += nRows
533
+ freezeThreadPool.join()
534
+ if len(res) < maxRows:
535
+ break
536
+
537
+ # delete dis datasets
538
+ class EraserThr(threading.Thread):
539
+ def __init__(self, lock, proxyLock, datasets, pool, operationType):
540
+ threading.Thread.__init__(self)
541
+ self.datasets = datasets
542
+ self.lock = lock
543
+ self.proxyLock = proxyLock
544
+ self.pool = pool
545
+ self.pool.add(self)
546
+ self.operationType = operationType
547
+
548
+ def run(self):
549
+ self.lock.acquire()
550
+ try:
551
+ # loop over all datasets
552
+ for vuid, name, modDate in self.datasets:
553
+ # only dis datasets
554
+ if re.search("_dis\d+$", name) is None:
555
+ _logger.error(f"Eraser : non disDS {name}")
556
+ continue
557
+ # delete
558
+ _logger.debug(f"Eraser {self.operationType} dis {modDate} {name}")
559
+ # delete or shorten
560
+ endStatus = "deleted"
561
+ status, out = rucioAPI.erase_dataset(name)
562
+ if not status:
563
+ _logger.error(out)
564
+ continue
565
+ _logger.debug(f"OK with {name}")
566
+ # update
567
+ self.proxyLock.acquire()
568
+ varMap = {}
569
+ varMap[":vuid"] = vuid
570
+ varMap[":status"] = endStatus
571
+ taskBuffer.querySQLS(
572
+ "UPDATE ATLAS_PANDA.Datasets SET status=:status,modificationdate=CURRENT_DATE WHERE vuid=:vuid",
573
+ varMap,
574
+ )
575
+ self.proxyLock.release()
576
+ except Exception:
577
+ errStr = traceback.format_exc()
578
+ _logger.error(errStr)
579
+ self.pool.remove(self)
580
+ self.lock.release()
581
+
582
+ # delete dis datasets
583
+ _logger.debug("==== delete dis datasets ====")
584
+ timeLimitU = naive_utcnow() - datetime.timedelta(minutes=30)
585
+ timeLimitL = naive_utcnow() - datetime.timedelta(days=3)
586
+ disEraseLock = threading.Semaphore(5)
587
+ disEraseProxyLock = threading.Lock()
588
+ disEraseThreadPool = ThreadPool()
589
+ # maxRows = 100000
590
+ maxRows = 5000
591
+ for targetStatus in ["deleting", "shortening"]:
592
+ for i in range(10):
593
+ # lock
594
+ disEraseLock.acquire()
595
+ # get datasets
596
+ varMap = {}
597
+ varMap[":modificationdateU"] = timeLimitU
598
+ varMap[":modificationdateL"] = timeLimitL
599
+ varMap[":type"] = "dispatch"
600
+ varMap[":status"] = targetStatus
601
+ sqlQuery = f"type=:type AND status=:status AND (modificationdate BETWEEN :modificationdateL AND :modificationdateU) AND rownum <= {maxRows}"
602
+ disEraseProxyLock.acquire()
603
+ res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
604
+ if res is None:
605
+ _logger.debug(f"# of dis datasets for {targetStatus}: None")
606
+ else:
607
+ _logger.debug(f"# of dis datasets for {targetStatus}: {len(res)}")
608
+ if res is None or len(res) == 0:
609
+ disEraseProxyLock.release()
610
+ disEraseLock.release()
611
+ break
612
+ disEraseProxyLock.release()
613
+ # release
614
+ disEraseLock.release()
615
+ # run disEraser
616
+ iRows = 0
617
+ nRows = 500
618
+ while iRows < len(res):
619
+ disEraser = EraserThr(
620
+ disEraseLock,
621
+ disEraseProxyLock,
622
+ res[iRows : iRows + nRows],
623
+ disEraseThreadPool,
624
+ targetStatus,
625
+ )
626
+ disEraser.start()
627
+ iRows += nRows
628
+ disEraseThreadPool.join()
629
+ if len(res) < 100:
630
+ break
631
+
632
+ _memoryCheck("finisher")
633
+
634
+ # finisher thread
635
+ class FinisherThr(threading.Thread):
636
+ def __init__(self, lock, proxyLock, ids, pool, timeNow):
637
+ threading.Thread.__init__(self)
638
+ self.ids = ids
639
+ self.lock = lock
640
+ self.proxyLock = proxyLock
641
+ self.pool = pool
642
+ self.timeNow = timeNow
643
+ self.pool.add(self)
644
+
645
+ def run(self):
646
+ self.lock.acquire()
647
+ try:
648
+ # get jobs from DB
649
+ ids = self.ids
650
+ self.proxyLock.acquire()
651
+ jobs = taskBuffer.peekJobs(ids, fromDefined=False, fromArchived=False, fromWaiting=False)
652
+ self.proxyLock.release()
653
+ upJobs = []
654
+ finJobs = []
655
+ for job in jobs:
656
+ if job is None or job.jobStatus == "unknown":
657
+ continue
658
+ seList = ["dummy"]
659
+ tmpNucleus = siteMapper.getNucleus(job.nucleus)
660
+ # get SEs
661
+ if job.prodSourceLabel == "user" and job.destinationSE not in siteMapper.siteSpecList:
662
+ # using --destSE for analysis job to transfer output
663
+ seList = [job.destinationSE]
664
+ elif tmpNucleus is not None:
665
+ seList = [tmpNucleus.default_ddm_endpoint_out]
666
+
667
+ # get LFN list
668
+ lfns = []
669
+ guids = []
670
+ scopes = []
671
+ nTokens = 0
672
+ for file in job.Files:
673
+ # only output files are checked
674
+ if file.type == "output" or file.type == "log":
675
+ if file.status == "nooutput":
676
+ continue
677
+ if DataServiceUtils.getDistributedDestination(file.destinationDBlockToken) is not None:
678
+ continue
679
+ lfns.append(file.lfn)
680
+ guids.append(file.GUID)
681
+ scopes.append(file.scope)
682
+ nTokens += len(file.destinationDBlockToken.split(","))
683
+ # get files
684
+ _logger.debug(f"{job.PandaID} Cloud:{job.cloud}")
685
+ tmpStat, okFiles = rucioAPI.list_file_replicas(scopes, lfns, seList)
686
+ if not tmpStat:
687
+ _logger.error(f"{job.PandaID} failed to get file replicas")
688
+ okFiles = {}
689
+ # count files
690
+ nOkTokens = 0
691
+ for okLFN in okFiles:
692
+ okSEs = okFiles[okLFN]
693
+ nOkTokens += len(okSEs)
694
+ # check all files are ready
695
+ _logger.debug(f"{job.PandaID} nToken:{nTokens} nOkToken:{nOkTokens}")
696
+ if nTokens <= nOkTokens:
697
+ _logger.debug(f"{job.PandaID} Finisher : Finish")
698
+ for file in job.Files:
699
+ if file.type == "output" or file.type == "log":
700
+ if file.status != "nooutput":
701
+ file.status = "ready"
702
+ # append to run Finisher
703
+ finJobs.append(job)
704
+ else:
705
+ endTime = job.endTime
706
+ if endTime == "NULL":
707
+ endTime = job.startTime
708
+ # priority-dependent timeout
709
+ if job.currentPriority >= 800 and (job.prodSourceLabel not in ["user"]):
710
+ timeOutValue = TRANSFER_TIMEOUT_HI_PRIORITY
711
+ else:
712
+ timeOutValue = TRANSFER_TIMEOUT_LO_PRIORITY
713
+
714
+ timeOut = self.timeNow - datetime.timedelta(days=timeOutValue)
715
+ _logger.debug(f"{job.PandaID} Priority:{job.currentPriority} Limit:{str(timeOut)} End:{str(endTime)}")
716
+ if endTime < timeOut:
717
+ # timeout
718
+ _logger.debug(f"{job.PandaID} Finisher : Kill")
719
+ strMiss = ""
720
+ for lfn in lfns:
721
+ if lfn not in okFiles:
722
+ strMiss += f" {lfn}"
723
+ job.jobStatus = "failed"
724
+ job.taskBufferErrorCode = pandaserver.taskbuffer.ErrorCode.EC_Transfer
725
+ job.taskBufferErrorDiag = f"transfer timeout for {strMiss}"
726
+ guidMap = {}
727
+ for file in job.Files:
728
+ # set file status
729
+ if file.status == "transferring" or file.type in [
730
+ "log",
731
+ "output",
732
+ ]:
733
+ file.status = "failed"
734
+ # collect GUIDs to delete files from _tid datasets
735
+ if file.type == "output" or file.type == "log":
736
+ if file.destinationDBlock not in guidMap:
737
+ guidMap[file.destinationDBlock] = []
738
+ guidMap[file.destinationDBlock].append(file.GUID)
739
+ else:
740
+ # wait
741
+ _logger.debug(f"{job.PandaID} Finisher : Wait")
742
+ for lfn in lfns:
743
+ if lfn not in okFiles:
744
+ _logger.debug(f"{job.PandaID} -> {lfn}")
745
+ upJobs.append(job)
746
+ # update
747
+ _logger.debug("updating ...")
748
+ self.proxyLock.acquire()
749
+ taskBuffer.updateJobs(upJobs, False)
750
+ self.proxyLock.release()
751
+ # run Finisher
752
+ for job in finJobs:
753
+ fThr = Finisher(taskBuffer, None, job)
754
+ fThr.run()
755
+ _logger.debug("done")
756
+ time.sleep(1)
757
+ except Exception:
758
+ errtype, errvalue = sys.exc_info()[:2]
759
+ errStr = f"FinisherThr failed with {errtype} {errvalue}"
760
+ errStr += traceback.format_exc()
761
+ _logger.error(errStr)
762
+ self.pool.remove(self)
763
+ self.lock.release()
764
+
765
+ # finish transferring jobs
766
+ _logger.debug("==== finish transferring jobs ====")
767
+ finisherLock = threading.Semaphore(3)
768
+ finisherProxyLock = threading.Lock()
769
+ finisherThreadPool = ThreadPool()
770
+ for loopIdx in ["low", "high"]:
771
+ timeNow = naive_utcnow()
772
+ if loopIdx == "high":
773
+ highPrioFlag = True
774
+ else:
775
+ highPrioFlag = False
776
+ # get jobs
777
+ for ii in range(1000):
778
+ # lock
779
+ finisherLock.acquire()
780
+ finisherProxyLock.acquire()
781
+ ret, res = taskBuffer.lockJobsForFinisher(timeNow, 200, highPrioFlag)
782
+ finisherProxyLock.release()
783
+ finisherLock.release()
784
+ if res is None:
785
+ _logger.debug(f"# of jobs to be finished for {loopIdx} : {res}")
786
+ else:
787
+ _logger.debug(f"# of jobs to be finished for {loopIdx} : {len(res)}")
788
+ if res is None or len(res) == 0:
789
+ break
790
+ # run thread
791
+ finThr = FinisherThr(finisherLock, finisherProxyLock, res, finisherThreadPool, timeNow)
792
+ finThr.start()
793
+ # wait
794
+ finisherThreadPool.join()
795
+
796
+ # activator thread
797
+ class ActivatorThr(threading.Thread):
798
+ def __init__(self, lock, proxyLock, ids, pool):
799
+ threading.Thread.__init__(self)
800
+ self.ids = ids
801
+ self.lock = lock
802
+ self.proxyLock = proxyLock
803
+ self.pool = pool
804
+ self.pool.add(self)
805
+
806
+ def run(self):
807
+ self.lock.acquire()
808
+ try:
809
+ # get jobs from DB
810
+ ids = self.ids
811
+ self.proxyLock.acquire()
812
+ jobs = taskBuffer.peekJobs(ids, fromActive=False, fromArchived=False, fromWaiting=False)
813
+ self.proxyLock.release()
814
+ actJobs = []
815
+ for tmpJob in jobs:
816
+ if tmpJob is None or tmpJob.jobStatus == "unknown":
817
+ continue
818
+ # get LFN list
819
+ lfns = []
820
+ guids = []
821
+ scopes = []
822
+ for tmpFile in tmpJob.Files:
823
+ # only input files are checked
824
+ if tmpFile.type == "input" and tmpFile.status != "ready":
825
+ lfns.append(tmpFile.lfn)
826
+ scopes.append(tmpFile.scope)
827
+ # get file replicas
828
+ _logger.debug(f"{tmpJob.PandaID} check input files at {tmpJob.computingSite}")
829
+ tmpStat, okFiles = rucioAPI.list_file_replicas(scopes, lfns)
830
+ if not tmpStat:
831
+ pass
832
+ else:
833
+ # check if locally available
834
+ siteSpec = siteMapper.getSite(tmpJob.computingSite)
835
+ scope_input, scope_output = select_scope(siteSpec, tmpJob.prodSourceLabel, tmpJob.job_label)
836
+ allOK = True
837
+ for tmpFile in tmpJob.Files:
838
+ # only input
839
+ if tmpFile.type == "input" and tmpFile.status != "ready":
840
+ # check RSEs
841
+ if tmpFile.lfn in okFiles:
842
+ for rse in okFiles[tmpFile.lfn]:
843
+ if (
844
+ siteSpec.ddm_endpoints_input[scope_input].isAssociated(rse)
845
+ and siteSpec.ddm_endpoints_input[scope_input].getEndPoint(rse)["is_tape"] == "N"
846
+ ):
847
+ tmpFile.status = "ready"
848
+ break
849
+ # missing
850
+ if tmpFile.status != "ready":
851
+ allOK = False
852
+ _logger.debug(f"{tmpJob.PandaID} skip since {tmpFile.scope}:{tmpFile.lfn} is missing")
853
+ break
854
+ if not allOK:
855
+ continue
856
+ # append to run activator
857
+ _logger.debug(f"{tmpJob.PandaID} to activate")
858
+ actJobs.append(tmpJob)
859
+ # update
860
+ _logger.debug("activating ...")
861
+ self.proxyLock.acquire()
862
+ taskBuffer.activateJobs(actJobs)
863
+ self.proxyLock.release()
864
+ _logger.debug("done")
865
+ time.sleep(1)
866
+ except Exception:
867
+ errtype, errvalue = sys.exc_info()[:2]
868
+ _logger.error(f"ActivatorThr failed with {errtype} {errvalue}")
869
+ self.pool.remove(self)
870
+ self.lock.release()
871
+
872
+ _memoryCheck("activator")
873
+
874
+ # activate assigned jobs
875
+ _logger.debug("==== activate assigned jobs ====")
876
+ activatorLock = threading.Semaphore(3)
877
+ activatorProxyLock = threading.Lock()
878
+ activatorThreadPool = ThreadPool()
879
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=1)
880
+ # get jobs
881
+ for ii in range(1000):
882
+ # lock
883
+ activatorLock.acquire()
884
+ activatorProxyLock.acquire()
885
+ ret, res = taskBuffer.lockJobsForActivator(timeLimit, 100, 800)
886
+ activatorProxyLock.release()
887
+ activatorLock.release()
888
+ if res is None:
889
+ _logger.debug(f"# of jobs to be activated for {res} ")
890
+ else:
891
+ _logger.debug(f"# of jobs to be activated for {len(res)} ")
892
+ if res is None or len(res) == 0:
893
+ break
894
+ # run thread
895
+ actThr = ActivatorThr(activatorLock, activatorProxyLock, res, activatorThreadPool)
896
+ actThr.start()
897
+ # wait
898
+ activatorThreadPool.join()
899
+
900
+ # activator thread with rule
901
+ class ActivatorWithRuleThr(threading.Thread):
902
+ def __init__(self, lock, proxyLock, ids, pool):
903
+ threading.Thread.__init__(self)
904
+ self.ids = ids
905
+ self.lock = lock
906
+ self.proxyLock = proxyLock
907
+ self.pool = pool
908
+ self.pool.add(self)
909
+
910
+ def run(self):
911
+ self.lock.acquire()
912
+ try:
913
+ # get jobs from DB
914
+ ids = self.ids
915
+ self.proxyLock.acquire()
916
+ jobs = taskBuffer.peekJobs(ids, fromActive=False, fromArchived=False, fromWaiting=False)
917
+ self.proxyLock.release()
918
+ actJobs = []
919
+ replicaMap = dict()
920
+ for tmpJob in jobs:
921
+ if tmpJob is None or tmpJob.jobStatus == "unknown":
922
+ continue
923
+ # check if locally available
924
+ siteSpec = siteMapper.getSite(tmpJob.computingSite)
925
+ scope_input, scope_output = select_scope(siteSpec, tmpJob.prodSourceLabel, tmpJob.job_label)
926
+ allOK = True
927
+ for tmpFile in tmpJob.Files:
928
+ # only input files are checked
929
+ if tmpFile.type == "input" and tmpFile.status != "ready":
930
+ # get replicas
931
+ if tmpFile.dispatchDBlock not in replicaMap:
932
+ tmpStat, repMap = rucioAPI.list_dataset_replicas(tmpFile.dispatchDBlock)
933
+ if tmpStat != 0:
934
+ repMap = {}
935
+ replicaMap[tmpFile.dispatchDBlock] = repMap
936
+ # check RSEs
937
+ for rse in replicaMap[tmpFile.dispatchDBlock]:
938
+ repInfo = replicaMap[tmpFile.dispatchDBlock][rse]
939
+ if (
940
+ siteSpec.ddm_endpoints_input[scope_input].isAssociated(rse)
941
+ and siteSpec.ddm_endpoints_input[scope_input].getEndPoint(rse)["is_tape"] == "N"
942
+ and repInfo[0]["total"] == repInfo[0]["found"]
943
+ and repInfo[0]["total"] is not None
944
+ and repInfo[0]["total"] > 0
945
+ ):
946
+ tmpFile.status = "ready"
947
+ break
948
+ # missing
949
+ if tmpFile.status != "ready":
950
+ allOK = False
951
+ _logger.debug(f"{tmpJob.PandaID} skip since {tmpFile.scope}:{tmpFile.lfn} is missing with rule")
952
+ break
953
+ if not allOK:
954
+ continue
955
+ # append to run activator
956
+ _logger.debug(f"{tmpJob.PandaID} to activate with rule {str(replicaMap)}")
957
+ actJobs.append(tmpJob)
958
+ # update
959
+ _logger.debug("activating ...")
960
+ self.proxyLock.acquire()
961
+ taskBuffer.activateJobs(actJobs)
962
+ self.proxyLock.release()
963
+ _logger.debug("done")
964
+ time.sleep(1)
965
+ except Exception:
966
+ errtype, errvalue = sys.exc_info()[:2]
967
+ _logger.error(f"ActivatorThr failed with {errtype} {errvalue}")
968
+ self.pool.remove(self)
969
+ self.lock.release()
970
+
971
+ _memoryCheck("activator")
972
+
973
+ # activate assigned jobs
974
+ _logger.debug("==== activate assigned jobs with rule ====")
975
+ activatorLock = threading.Semaphore(3)
976
+ activatorProxyLock = threading.Lock()
977
+ activatorThreadPool = ThreadPool()
978
+ timeLimit = naive_utcnow() - datetime.timedelta(hours=1)
979
+ # get jobs
980
+ for ii in range(1000):
981
+ # lock
982
+ activatorLock.acquire()
983
+ activatorProxyLock.acquire()
984
+ ret, res = taskBuffer.lockJobsForActivator(timeLimit, 100, 0)
985
+ activatorProxyLock.release()
986
+ activatorLock.release()
987
+ if res is None:
988
+ _logger.debug(f"# of jobs to be activated with rule for {res} ")
989
+ else:
990
+ _logger.debug(f"# of jobs to be activated with rule for {len(res)} ")
991
+ if res is None or len(res) == 0:
992
+ break
993
+ # run thread
994
+ actThr = ActivatorWithRuleThr(activatorLock, activatorProxyLock, res, activatorThreadPool)
995
+ actThr.start()
996
+ # wait
997
+ activatorThreadPool.join()
998
+
999
+ # thread to delete sub datasets
1000
+ class SubDeleter(threading.Thread):
1001
+ def __init__(self, lock, proxyLock, datasets, pool):
1002
+ threading.Thread.__init__(self)
1003
+ self.datasets = datasets
1004
+ self.lock = lock
1005
+ self.proxyLock = proxyLock
1006
+ self.pool = pool
1007
+ self.pool.add(self)
1008
+
1009
+ def run(self):
1010
+ self.lock.acquire()
1011
+ try:
1012
+ for vuid, name, modDate in self.datasets:
1013
+ # check just in case
1014
+ if re.search("_sub\d+$", name) is None:
1015
+ _logger.debug(f"skip non sub {name}")
1016
+ continue
1017
+ _logger.debug(f"delete sub {name}")
1018
+ if name.startswith("user.") or name.startswith("group.") or name.startswith("hc_test."):
1019
+ dsExists = False
1020
+ else:
1021
+ dsExists = True
1022
+ # get PandaIDs
1023
+ self.proxyLock.acquire()
1024
+ retF, resF = taskBuffer.querySQLS(
1025
+ "SELECT /*+ index(tab FILESTABLE4_DESTDBLOCK_IDX) */ DISTINCT PandaID FROM ATLAS_PANDA.filesTable4 tab WHERE destinationDBlock=:destinationDBlock ",
1026
+ {":destinationDBlock": name},
1027
+ )
1028
+ self.proxyLock.release()
1029
+ if retF is None:
1030
+ _logger.error(f"SQL error for sub {name}")
1031
+ continue
1032
+ else:
1033
+ _logger.debug(f"sub {name} has {len(resF)} jobs")
1034
+ self.proxyLock.acquire()
1035
+ # check jobs
1036
+ sqlP = "SELECT jobStatus FROM ATLAS_PANDA.jobsArchived4 WHERE PandaID=:PandaID "
1037
+ sqlP += "UNION "
1038
+ sqlP += "SELECT jobStatus FROM ATLAS_PANDAARCH.jobsArchived WHERE PandaID=:PandaID AND modificationTime>CURRENT_DATE-30 "
1039
+ allDone = True
1040
+ for (pandaID,) in resF:
1041
+ retP, resP = taskBuffer.querySQLS(sqlP, {":PandaID": pandaID})
1042
+ if len(resP) == 0:
1043
+ _logger.debug(f"skip delete sub {name} PandaID={pandaID} not found")
1044
+ allDone = False
1045
+ break
1046
+ jobStatus = resP[0][0]
1047
+ if jobStatus not in [
1048
+ "finished",
1049
+ "failed",
1050
+ "cancelled",
1051
+ "closed",
1052
+ ]:
1053
+ _logger.debug(f"skip delete sub {name} PandaID={pandaID} is active {jobStatus}")
1054
+ allDone = False
1055
+ break
1056
+ self.proxyLock.release()
1057
+ if allDone:
1058
+ _logger.debug(f"deleting sub {name}")
1059
+ try:
1060
+ rucioAPI.erase_dataset(name, grace_period=4)
1061
+ status = True
1062
+ except Exception:
1063
+ errtype, errvalue = sys.exc_info()[:2]
1064
+ out = f"{errtype} {errvalue}"
1065
+ _logger.error(f"{name} failed to erase with {out}")
1066
+ else:
1067
+ _logger.debug(f"wait sub {name}")
1068
+ continue
1069
+ # update dataset
1070
+ self.proxyLock.acquire()
1071
+ varMap = {}
1072
+ varMap[":vuid"] = vuid
1073
+ varMap[":ost1"] = "completed"
1074
+ varMap[":ost2"] = "cleanup"
1075
+ varMap[":newStatus"] = "deleted"
1076
+ taskBuffer.querySQLS(
1077
+ "UPDATE ATLAS_PANDA.Datasets SET status=:newStatus,modificationdate=CURRENT_DATE WHERE vuid=:vuid AND status IN (:ost1,:ost2) ",
1078
+ varMap,
1079
+ )
1080
+ self.proxyLock.release()
1081
+ _logger.debug(f"end {name} ")
1082
+ except Exception:
1083
+ errStr = traceback.format_exc()
1084
+ _logger.error(errStr)
1085
+ self.pool.remove(self)
1086
+ self.lock.release()
1087
+
1088
+ # delete sub datasets
1089
+ _logger.debug("==== delete sub datasets ====")
1090
+ timeLimitU = naive_utcnow() - datetime.timedelta(minutes=30)
1091
+ timeLimitL = naive_utcnow() - datetime.timedelta(days=14)
1092
+ subdeleteLock = threading.Semaphore(5)
1093
+ subdeleteProxyLock = threading.Lock()
1094
+ subdeleteThreadPool = ThreadPool()
1095
+ maxRows = 5000
1096
+ while True:
1097
+ # lock
1098
+ subdeleteLock.acquire()
1099
+ # get datasets
1100
+ varMap = {}
1101
+ varMap[":limitU"] = timeLimitU
1102
+ varMap[":limitL"] = timeLimitL
1103
+ varMap[":type"] = "output"
1104
+ varMap[":subtype"] = "sub"
1105
+ varMap[":st1"] = "completed"
1106
+ varMap[":st2"] = "cleanup"
1107
+ sqlQuery = (
1108
+ "type=:type AND subType=:subtype AND status IN (:st1,:st2) AND (creationdate BETWEEN :limitL AND :limitU) AND (modificationdate BETWEEN :limitL AND :limitU) AND rownum <= %s"
1109
+ % maxRows
1110
+ )
1111
+ subdeleteProxyLock.acquire()
1112
+ res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
1113
+ if res is None:
1114
+ _logger.debug(f"# of sub datasets to be deleted {res}")
1115
+ else:
1116
+ _logger.debug(f"# of sub datasets to be deleted {len(res)}")
1117
+ if res is None or len(res) == 0:
1118
+ subdeleteProxyLock.release()
1119
+ subdeleteLock.release()
1120
+ break
1121
+ subdeleteProxyLock.release()
1122
+ # release
1123
+ subdeleteLock.release()
1124
+ # run subdeleter
1125
+ iRows = 0
1126
+ nRows = 500
1127
+ while iRows < len(res):
1128
+ subdeleter = SubDeleter(
1129
+ subdeleteLock,
1130
+ subdeleteProxyLock,
1131
+ res[iRows : iRows + nRows],
1132
+ subdeleteThreadPool,
1133
+ )
1134
+ subdeleter.start()
1135
+ iRows += nRows
1136
+ subdeleteThreadPool.join()
1137
+ if len(res) < 100:
1138
+ break
1139
+
1140
+ # release memory
1141
+ del siteMapper
1142
+ del deletedDisList
1143
+
1144
+ _memoryCheck("end")
1145
+
1146
+ # stop taskBuffer if created inside this script
1147
+ taskBuffer.cleanup(requester=requester_id)
1148
+
1149
+ _logger.debug("===================== end =====================")
1150
+
1151
+
1152
+ # run
1153
+ if __name__ == "__main__":
1154
+ main()