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,2882 @@
1
+ import datetime
2
+ import json
3
+ import re
4
+ import shlex
5
+ import sys
6
+ import time
7
+ import traceback
8
+ from contextlib import contextmanager
9
+ from threading import Lock
10
+
11
+ from pandacommon.pandalogger.LogWrapper import LogWrapper
12
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
13
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
14
+
15
+ from pandaserver.brokerage.SiteMapper import SiteMapper
16
+ from pandaserver.config import panda_config
17
+ from pandaserver.dataservice.closer import Closer
18
+ from pandaserver.dataservice.setupper import Setupper
19
+ from pandaserver.srvcore import CoreUtils
20
+ from pandaserver.taskbuffer import ErrorCode, EventServiceUtils, JobUtils, ProcessGroups
21
+ from pandaserver.taskbuffer.DBProxyPool import DBProxyPool
22
+
23
+ _logger = PandaLogger().getLogger("TaskBuffer")
24
+
25
+
26
+ class TaskBuffer:
27
+ """
28
+ task queue
29
+
30
+ """
31
+
32
+ # constructor
33
+ def __init__(self):
34
+ self.proxyPool = None
35
+ self.lock = Lock()
36
+ self.nDBConnection = None
37
+
38
+ # save the requester for monitoring/logging purposes
39
+ self.start_time = time.time()
40
+
41
+ # site mapper
42
+ self.site_mapper = None
43
+ # update time for site mapper
44
+ self.last_update_site_mapper = None
45
+
46
+ def __repr__(self):
47
+ return "TaskBuffer"
48
+
49
+ # initialize
50
+ def init(self, dbname, dbpass, nDBConnection=10, useTimeout=False, requester=None):
51
+ # acquire lock
52
+ self.lock.acquire()
53
+ self.nDBConnection = nDBConnection
54
+
55
+ # create Proxy Pool
56
+ if self.proxyPool is None:
57
+ _logger.info(f"creating DBProxyPool with n_connections={nDBConnection} on behalf of {requester}")
58
+ self.start_time = time.time()
59
+ self.proxyPool = DBProxyPool(dbname, dbpass, nDBConnection, useTimeout)
60
+
61
+ # release lock
62
+ self.lock.release()
63
+
64
+ # cleanup
65
+ def cleanup(self, requester=None):
66
+ if self.proxyPool:
67
+ try:
68
+ pool_duration = time.time() - self.start_time
69
+ except TypeError:
70
+ pool_duration = -1 # duration unknown
71
+
72
+ _logger.info(f"destroying DBProxyPool after n_seconds={pool_duration} on behalf of {requester}")
73
+ self.proxyPool.cleanup()
74
+
75
+ # transaction as a context manager
76
+ # CANNOT be used with ConBridge or TaskBufferInterface which uses multiprocess.pipe
77
+ @contextmanager
78
+ def transaction(self, name=None, tmp_log=None):
79
+ with self.proxyPool.get() as proxy:
80
+ with proxy.transaction(name, tmp_log) as txn:
81
+ if txn is None:
82
+ raise RuntimeError(f"Failed to start transaction {name}")
83
+ # yield the transaction
84
+ yield txn
85
+
86
+ # get number of database connections
87
+ def get_num_connections(self):
88
+ return self.nDBConnection
89
+
90
+ # get SiteMapper
91
+ def get_site_mapper(self):
92
+ time_now = naive_utcnow()
93
+ if self.last_update_site_mapper is None or datetime.datetime.now(datetime.timezone.utc).replace(
94
+ tzinfo=None
95
+ ) - self.last_update_site_mapper > datetime.timedelta(minutes=10):
96
+ self.site_mapper = SiteMapper(self)
97
+ self.last_update_site_mapper = time_now
98
+ return self.site_mapper
99
+
100
+ # check production role
101
+ def checkProdRole(self, fqans):
102
+ for fqan in fqans:
103
+ # check production role
104
+ match = re.search("/([^/]+)/Role=production", fqan)
105
+ if match is not None:
106
+ return True, match.group(1)
107
+ return False, None
108
+
109
+ # get priority parameters for user
110
+ def getPrioParameters(self, jobs, user, fqans, userDefinedWG, validWorkingGroup):
111
+ priorityOffset = 0
112
+ serNum = 0
113
+ weight = None
114
+ prio_reduction = True
115
+ # get boosted users and groups
116
+ boost_dict = {}
117
+ # get DB proxy
118
+ with self.proxyPool.get() as proxy:
119
+ # check production role
120
+ withProdRole, workingGroup = self.checkProdRole(fqans)
121
+ if withProdRole and jobs != []:
122
+ # check dataset name
123
+ for tmpFile in jobs[-1].Files:
124
+ if tmpFile.type in ["output", "log"] and not (tmpFile.lfn.startswith("group") or tmpFile.lfn.startswith("panda.um.group")):
125
+ # reset
126
+ withProdRole, workingGroup = False, None
127
+ break
128
+ # reset nJob/weight for HC
129
+ if jobs != []:
130
+ if jobs[0].processingType in [
131
+ "hammercloud",
132
+ "gangarobot",
133
+ "hammercloud-fax",
134
+ ] or jobs[
135
+ 0
136
+ ].processingType.startswith("gangarobot-"):
137
+ serNum = 0
138
+ weight = 0.0
139
+ elif jobs[0].processingType in ["gangarobot", "gangarobot-pft"]:
140
+ priorityOffset = 3000
141
+ elif jobs[0].processingType in ["hammercloud-fax"]:
142
+ priorityOffset = 1001
143
+ else:
144
+ # get users and groups to boost job priorities
145
+ boost_dict = proxy.get_dict_to_boost_job_prio(jobs[-1].VO)
146
+ if boost_dict:
147
+ prodUserName = CoreUtils.clean_user_id(user)
148
+ # check boost list
149
+ if userDefinedWG and validWorkingGroup:
150
+ if "group" in boost_dict and jobs[-1].workingGroup in boost_dict["group"]:
151
+ priorityOffset = boost_dict["group"][jobs[-1].workingGroup]
152
+ weight = 0.0
153
+ prio_reduction = False
154
+ else:
155
+ if "user" in boost_dict and prodUserName in boost_dict["user"]:
156
+ priorityOffset = boost_dict["user"][prodUserName]
157
+ weight = 0.0
158
+ prio_reduction = False
159
+ # check quota
160
+ if weight is None:
161
+ weight = proxy.checkQuota(user)
162
+ # get nJob
163
+ if jobs == []:
164
+ serNum = proxy.getNumberJobsUser(user, workingGroup=userDefinedWG)
165
+ elif userDefinedWG and validWorkingGroup:
166
+ # check if group privileged
167
+ isSU, isGU = proxy.isSuperUser(jobs[0].workingGroup)
168
+ if not isSU:
169
+ serNum = proxy.getNumberJobsUser(user, workingGroup=jobs[0].workingGroup)
170
+ else:
171
+ # set high priority for production role
172
+ serNum = 0
173
+ weight = 0.0
174
+ priorityOffset = 2000
175
+ else:
176
+ serNum = proxy.getNumberJobsUser(user, workingGroup=None)
177
+
178
+ return (
179
+ withProdRole,
180
+ workingGroup,
181
+ priorityOffset,
182
+ serNum,
183
+ weight,
184
+ prio_reduction,
185
+ )
186
+
187
+ # store Jobs into DB
188
+ def storeJobs(
189
+ self,
190
+ jobs,
191
+ user,
192
+ joinThr=False,
193
+ fqans=[],
194
+ hostname="",
195
+ checkSpecialHandling=True,
196
+ toPending=False,
197
+ oldPandaIDs=None,
198
+ relationType=None,
199
+ userVO="atlas",
200
+ esJobsetMap=None,
201
+ getEsJobsetMap=False,
202
+ unprocessedMap=None,
203
+ bulk_job_insert=False,
204
+ trust_user=False,
205
+ ):
206
+ try:
207
+ tmpLog = LogWrapper(_logger, f"storeJobs <{CoreUtils.clean_user_id(user)} nJobs={len(jobs)}>")
208
+ tmpLog.debug(f"start toPending={toPending}")
209
+ # check quota for priority calculation
210
+ weight = 0.0
211
+ userJobID = -1
212
+ userJobsetID = -1
213
+ userStatus = True
214
+ priorityOffset = 0
215
+ userCountry = None
216
+ useExpress = False
217
+ nExpressJobs = 0
218
+ useDebugMode = False
219
+ siteMapper = self.get_site_mapper()
220
+
221
+ # check ban user
222
+ if not trust_user and len(jobs) > 0:
223
+ # get DB proxy
224
+ with self.proxyPool.get() as proxy:
225
+ # check user status
226
+ tmpStatus = proxy.checkBanUser(user, jobs[0].prodSourceLabel)
227
+ # return if DN is blocked
228
+ if not tmpStatus:
229
+ tmpLog.debug(f"end 1 since DN {user} is blocked")
230
+ if getEsJobsetMap:
231
+ return [], None, unprocessedMap
232
+ return []
233
+
234
+ tmpLog.debug(f"checked ban user")
235
+ # set parameters for user jobs
236
+ if (
237
+ len(jobs) > 0
238
+ and (jobs[0].prodSourceLabel in JobUtils.analy_sources + JobUtils.list_ptest_prod_sources)
239
+ and (not jobs[0].processingType in ["merge", "unmerge"])
240
+ ):
241
+ # get DB proxy
242
+ with self.proxyPool.get() as proxy:
243
+ # get JobID and status
244
+ userJobID, userJobsetID, userStatus = proxy.getUserParameter(user, jobs[0].jobDefinitionID, jobs[0].jobsetID)
245
+
246
+ # check quota for express jobs
247
+ if "express" in jobs[0].specialHandling:
248
+ expressQuota = proxy.getExpressJobs(user)
249
+ if expressQuota is not None and expressQuota["status"] and expressQuota["quota"] > 0:
250
+ nExpressJobs = expressQuota["quota"]
251
+ if nExpressJobs > 0:
252
+ useExpress = True
253
+ # debug mode
254
+ if jobs[0].is_debug_mode() or jobs[-1].is_debug_mode():
255
+ useDebugMode = True
256
+
257
+ # extract country group
258
+ for tmpFQAN in fqans:
259
+ match = re.search("^/atlas/([^/]+)/", tmpFQAN)
260
+ if match is not None:
261
+ tmpCountry = match.group(1)
262
+ # use country code or usatlas
263
+ if len(tmpCountry) == 2:
264
+ userCountry = tmpCountry
265
+ break
266
+ # usatlas
267
+ if tmpCountry in ["usatlas"]:
268
+ userCountry = "us"
269
+ break
270
+ tmpLog.debug(f"set user job parameters")
271
+
272
+ # return if DN is blocked
273
+ if not userStatus:
274
+ tmpLog.debug(f"end 2 since {user} DN is blocked")
275
+ if getEsJobsetMap:
276
+ return ([], None, unprocessedMap)
277
+ return []
278
+ # extract VO
279
+ for tmpFQAN in fqans:
280
+ match = re.search("^/([^/]+)/", tmpFQAN)
281
+ if match is not None:
282
+ userVO = match.group(1)
283
+ break
284
+
285
+ # get number of jobs currently in PandaDB
286
+ serNum = 0
287
+ userDefinedWG = False
288
+ validWorkingGroup = False
289
+ usingBuild = False
290
+ withProdRole = False
291
+ workingGroup = None
292
+ prio_reduction = True
293
+ if len(jobs) > 0 and (jobs[0].prodSourceLabel in JobUtils.analy_sources) and (not jobs[0].processingType in ["merge", "unmerge"]):
294
+ # extract user's working group from FQANs
295
+ userWorkingGroupList = []
296
+ for tmpFQAN in fqans:
297
+ match = re.search("/([^/]+)/Role=production", tmpFQAN)
298
+ if match is not None:
299
+ userWorkingGroupList.append(match.group(1))
300
+ # check workingGroup
301
+ if jobs[0].workingGroup not in ["", None, "NULL"]:
302
+ userDefinedWG = True
303
+ # check with FQANs
304
+ if jobs[0].workingGroup in userWorkingGroupList:
305
+ validWorkingGroup = True
306
+ # using build for analysis
307
+ if jobs[0].prodSourceLabel == "panda":
308
+ usingBuild = True
309
+ # get priority parameters for user
310
+ (
311
+ withProdRole,
312
+ workingGroup,
313
+ priorityOffset,
314
+ serNum,
315
+ weight,
316
+ prio_reduction,
317
+ ) = self.getPrioParameters(jobs, user, fqans, userDefinedWG, validWorkingGroup)
318
+ tmpLog.debug(f"workingGroup={jobs[0].workingGroup} serNum={serNum} weight={weight} pOffset={priorityOffset} reduction={prio_reduction}")
319
+ tmpLog.debug(f"got prio parameters")
320
+ # get DB proxy
321
+ with self.proxyPool.get() as proxy:
322
+ tmpLog.debug(f"got proxy")
323
+ # get total number of files
324
+ totalNumFiles = 0
325
+ for job in jobs:
326
+ totalNumFiles += len(job.Files)
327
+ # bulk fetch PandaIDs
328
+ new_panda_ids = proxy.bulk_fetch_panda_ids(len(jobs))
329
+ tmpLog.debug(f"got PandaIDs")
330
+ # bulk fetch fileIDs
331
+ fileIDPool = []
332
+ if totalNumFiles > 0:
333
+ fileIDPool = sorted(proxy.bulkFetchFileIDsPanda(totalNumFiles))
334
+ # loop over all jobs
335
+ ret = []
336
+ newJobs = []
337
+ if esJobsetMap is None:
338
+ esJobsetMap = {}
339
+ try:
340
+ tmpLog.debug(f"jediTaskID={jobs[0].jediTaskID} len(esJobsetMap)={len(esJobsetMap)} nJobs={len(jobs)}")
341
+ except Exception:
342
+ pass
343
+ job_ret_list = []
344
+ params_for_bulk_insert = []
345
+ special_handling_list = []
346
+ num_original_event_service_jobs = 0
347
+ for idxJob, job in enumerate(jobs):
348
+ # set PandaID
349
+ job.PandaID = new_panda_ids[idxJob]
350
+ # set JobID. keep original JobID when retry
351
+ if (
352
+ userJobID != -1
353
+ and job.prodSourceLabel in JobUtils.analy_sources
354
+ and (job.attemptNr in [0, "0", "NULL"] or (job.jobExecutionID not in [0, "0", "NULL"]) or job.lockedby == "jedi")
355
+ and (not jobs[0].processingType in ["merge", "unmerge"])
356
+ ):
357
+ job.jobDefinitionID = userJobID
358
+ # set jobsetID
359
+ if job.prodSourceLabel in JobUtils.analy_sources + JobUtils.list_ptest_prod_sources:
360
+ job.jobsetID = userJobsetID
361
+ # set relocation flag
362
+ if job.computingSite != "NULL" and job.relocationFlag != 2:
363
+ job.relocationFlag = 1
364
+ # protection agains empty jobParameters
365
+ if job.jobParameters in ["", None, "NULL"]:
366
+ job.jobParameters = " "
367
+ # set country group and nJobs (=taskID)
368
+ if job.prodSourceLabel in JobUtils.analy_sources:
369
+ if job.lockedby != "jedi":
370
+ job.countryGroup = userCountry
371
+ # set workingGroup
372
+ if not validWorkingGroup:
373
+ if withProdRole:
374
+ # set country group if submitted with production role
375
+ job.workingGroup = workingGroup
376
+ else:
377
+ if userDefinedWG:
378
+ # reset invalid working group
379
+ job.workingGroup = None
380
+ # set nJobs (=taskID)
381
+ if usingBuild:
382
+ tmpNumBuild = 1
383
+ tmpNunRun = len(jobs) - 1
384
+ else:
385
+ tmpNumBuild = 0
386
+ tmpNunRun = len(jobs)
387
+ # encode
388
+ job.taskID = tmpNumBuild + (tmpNunRun << 1)
389
+ # set hostname
390
+ if hostname != "":
391
+ job.creationHost = hostname
392
+
393
+ # process and set the job_label
394
+ if not job.job_label or job.job_label not in (
395
+ JobUtils.PROD_PS,
396
+ JobUtils.ANALY_PS,
397
+ ):
398
+ tmpSiteSpec = siteMapper.getSite(job.computingSite)
399
+ queue_type = tmpSiteSpec.type
400
+ if queue_type == "analysis":
401
+ job.job_label = JobUtils.ANALY_PS
402
+ elif queue_type == "production":
403
+ job.job_label = JobUtils.PROD_PS
404
+ elif queue_type == "unified":
405
+ if job.prodSourceLabel in JobUtils.analy_sources:
406
+ job.job_label = JobUtils.ANALY_PS
407
+ else:
408
+ # set production as default if not specified and neutral prodsourcelabel
409
+ job.job_label = JobUtils.PROD_PS
410
+ else: # e.g. type = special
411
+ job.job_label = JobUtils.PROD_PS
412
+
413
+ # extract file info, change specialHandling for event service
414
+ origSH = job.specialHandling
415
+ (
416
+ eventServiceInfo,
417
+ job.specialHandling,
418
+ esIndex,
419
+ ) = EventServiceUtils.decodeFileInfo(job.specialHandling)
420
+ origEsJob = False
421
+ if eventServiceInfo != {}:
422
+ # set jobsetID
423
+ if esIndex in esJobsetMap:
424
+ job.jobsetID = esJobsetMap[esIndex]
425
+ else:
426
+ origEsJob = True
427
+ num_original_event_service_jobs += 1
428
+ esJobsetMap[esIndex] = None
429
+ # sort files since file order is important for positional event number
430
+ job.sortFiles()
431
+ if oldPandaIDs is not None and len(oldPandaIDs) > idxJob:
432
+ jobOldPandaIDs = oldPandaIDs[idxJob]
433
+ else:
434
+ jobOldPandaIDs = None
435
+ # check events for jumbo jobs
436
+ isOK = True
437
+ if EventServiceUtils.isJumboJob(job):
438
+ hasReadyEvents = proxy.hasReadyEvents(job.jediTaskID)
439
+ if hasReadyEvents is False:
440
+ isOK = False
441
+ # insert job to DB
442
+ if not isOK:
443
+ # skip since there is no ready event
444
+ job.PandaID = None
445
+ if not bulk_job_insert:
446
+ tmp_ret_i = proxy.insertNewJob(
447
+ job,
448
+ user,
449
+ serNum,
450
+ weight,
451
+ priorityOffset,
452
+ userVO,
453
+ toPending,
454
+ origEsJob,
455
+ eventServiceInfo,
456
+ oldPandaIDs=jobOldPandaIDs,
457
+ relationType=relationType,
458
+ fileIDPool=fileIDPool,
459
+ origSpecialHandling=origSH,
460
+ unprocessedMap=unprocessedMap,
461
+ prio_reduction=prio_reduction,
462
+ )
463
+ if unprocessedMap is not None:
464
+ tmp_ret_i, unprocessedMap = tmp_ret_i
465
+ else:
466
+ # keep parameters for late bulk execution
467
+ params_for_bulk_insert.append(
468
+ [
469
+ [job, user, serNum, weight, priorityOffset, userVO, toPending],
470
+ {
471
+ "origEsJob": origEsJob,
472
+ "eventServiceInfo": eventServiceInfo,
473
+ "oldPandaIDs": jobOldPandaIDs,
474
+ "relationType": relationType,
475
+ "fileIDPool": fileIDPool,
476
+ "origSpecialHandling": origSH,
477
+ "unprocessedMap": unprocessedMap,
478
+ "prio_reduction": prio_reduction,
479
+ },
480
+ {"esIndex": esIndex},
481
+ ]
482
+ )
483
+ special_handling_list.append(origSH)
484
+ tmp_ret_i = True
485
+ if tmp_ret_i and origEsJob and not bulk_job_insert:
486
+ # mapping of jobsetID for event service
487
+ esJobsetMap[esIndex] = job.jobsetID
488
+ job_ret_list.append([job, tmp_ret_i])
489
+ serNum += 1
490
+ try:
491
+ fileIDPool = fileIDPool[len(job.Files) :]
492
+ except Exception:
493
+ fileIDPool = []
494
+ # bulk insert
495
+ if bulk_job_insert:
496
+ # get jobset IDs for event service jobs
497
+ if num_original_event_service_jobs > 0:
498
+ new_jobset_ids = proxy.bulk_fetch_panda_ids(num_original_event_service_jobs)
499
+ else:
500
+ new_jobset_ids = []
501
+ tmp_ret, job_ret_list, es_jobset_map = proxy.bulk_insert_new_jobs(
502
+ jobs[0].jediTaskID, params_for_bulk_insert, new_jobset_ids, special_handling_list
503
+ )
504
+ if not tmp_ret:
505
+ raise RuntimeError("bulk job insert failed")
506
+ # update esJobsetMap
507
+ esJobsetMap.update(es_jobset_map)
508
+
509
+ # check returns
510
+ for job, tmp_ret_i in job_ret_list:
511
+ if not tmp_ret_i:
512
+ # reset if failed
513
+ job.PandaID = None
514
+ else:
515
+ # append
516
+ newJobs.append(job)
517
+ if job.prodSourceLabel in JobUtils.analy_sources + JobUtils.list_ptest_prod_sources:
518
+ ret.append((job.PandaID, job.jobDefinitionID, {"jobsetID": job.jobsetID}))
519
+ else:
520
+ ret.append((job.PandaID, job.jobDefinitionID, job.jobName))
521
+ # set up dataset
522
+ if not toPending:
523
+ if joinThr:
524
+ thr = Setupper(
525
+ self,
526
+ newJobs,
527
+ )
528
+ thr.start()
529
+ thr.join()
530
+ else:
531
+ # cannot use 'thr =' because it may trigger garbage collector
532
+ Setupper(
533
+ self,
534
+ newJobs,
535
+ ).start()
536
+ # return jobIDs
537
+ tmpLog.debug("end successfully")
538
+ if getEsJobsetMap:
539
+ return (ret, esJobsetMap, unprocessedMap)
540
+ return ret
541
+ except Exception as e:
542
+ tmpLog.error(f"{str(e)} {traceback.format_exc()}")
543
+ errStr = "ERROR: ServerError with storeJobs"
544
+ if getEsJobsetMap:
545
+ return (errStr, None, unprocessedMap)
546
+ return errStr
547
+
548
+ # lock jobs for reassign
549
+ def lockJobsForReassign(
550
+ self,
551
+ tableName,
552
+ timeLimit,
553
+ statList,
554
+ labels,
555
+ processTypes,
556
+ sites,
557
+ clouds,
558
+ useJEDI=False,
559
+ onlyReassignable=False,
560
+ useStateChangeTime=False,
561
+ getEventService=False,
562
+ ):
563
+ # get DB proxy
564
+ with self.proxyPool.get() as proxy:
565
+ # exec
566
+ res = proxy.lockJobsForReassign(
567
+ tableName,
568
+ timeLimit,
569
+ statList,
570
+ labels,
571
+ processTypes,
572
+ sites,
573
+ clouds,
574
+ useJEDI,
575
+ onlyReassignable,
576
+ useStateChangeTime,
577
+ getEventService,
578
+ )
579
+ return res
580
+
581
+ # get a DB configuration value
582
+ def getConfigValue(self, component, key, app="pandaserver", vo=None, default=None):
583
+ # get DB proxy
584
+ with self.proxyPool.get() as proxy:
585
+ # exec
586
+ res = proxy.getConfigValue(component, key, app, vo)
587
+ if res is None and default is not None:
588
+ res = default
589
+ return res
590
+
591
+ # lock jobs for finisher
592
+ def lockJobsForFinisher(self, timeNow, rownum, highPrio):
593
+ # get DB proxy
594
+ with self.proxyPool.get() as proxy:
595
+ # exec
596
+ res = proxy.lockJobsForFinisher(timeNow, rownum, highPrio)
597
+ return res
598
+
599
+ # lock jobs for activator
600
+ def lockJobsForActivator(self, timeLimit, rownum, prio):
601
+ # get DB proxy
602
+ with self.proxyPool.get() as proxy:
603
+ # exec
604
+ res = proxy.lockJobsForActivator(timeLimit, rownum, prio)
605
+ return res
606
+
607
+ # update overall job information
608
+ def updateJobs(
609
+ self,
610
+ jobs,
611
+ inJobsDefined,
612
+ oldJobStatusList=None,
613
+ extraInfo=None,
614
+ async_dataset_update=False,
615
+ ):
616
+ # get DB proxy
617
+ with self.proxyPool.get() as proxy:
618
+ # loop over all jobs
619
+ returns = []
620
+ ddmIDs = []
621
+ ddmAttempt = 0
622
+ newMover = None
623
+ for idxJob, job in enumerate(jobs):
624
+ # update DB
625
+ tmpddmIDs = []
626
+ if oldJobStatusList is not None and idxJob < len(oldJobStatusList):
627
+ oldJobStatus = oldJobStatusList[idxJob]
628
+ else:
629
+ oldJobStatus = None
630
+ # check for jumbo jobs
631
+ if EventServiceUtils.isJumboJob(job):
632
+ if job.jobStatus in ["defined", "assigned", "activated"]:
633
+ pass
634
+ else:
635
+ # check if there are done events
636
+ hasDone = proxy.hasDoneEvents(job.jediTaskID, job.PandaID, job)
637
+ if hasDone:
638
+ job.jobStatus = "finished"
639
+ else:
640
+ if job.pilotErrorCode in [1144, "1144"]:
641
+ job.jobStatus = "cancelled"
642
+ else:
643
+ job.jobStatus = "failed"
644
+ if job.taskBufferErrorDiag in ["", "NULL", None]:
645
+ job.taskBufferErrorDiag = f"set {job.jobStatus} since no successful events"
646
+ job.taskBufferErrorCode = ErrorCode.EC_EventServiceNoEvent
647
+ if job.jobStatus in ["finished", "failed", "cancelled"]:
648
+ if async_dataset_update and job.jediTaskID not in [None, "NULL"]:
649
+ async_params = {
650
+ "exec_order": 0,
651
+ "PandaID": job.PandaID,
652
+ "jediTaskID": job.jediTaskID,
653
+ }
654
+ else:
655
+ async_params = None
656
+ ret, tmpddmIDs, ddmAttempt, newMover = proxy.archiveJob(job, inJobsDefined, extraInfo=extraInfo, async_params=async_params)
657
+ if async_params is not None and ret:
658
+ proxy.async_update_datasets(job.PandaID)
659
+ else:
660
+ ret = proxy.updateJob(job, inJobsDefined, oldJobStatus=oldJobStatus, extraInfo=extraInfo)
661
+ returns.append(ret)
662
+ # collect IDs for reassign
663
+ if ret:
664
+ ddmIDs += tmpddmIDs
665
+ # retry mover
666
+ if newMover is not None:
667
+ self.storeJobs([newMover], None, joinThr=True)
668
+ # reassign jobs when ddm failed
669
+ if ddmIDs != []:
670
+ self.reassignJobs(ddmIDs, ddmAttempt, joinThr=True)
671
+
672
+ return returns
673
+
674
+ # update job jobStatus only
675
+ def updateJobStatus(self, jobID, jobStatus, param, updateStateChange=False, attemptNr=None):
676
+ # get DB proxy
677
+ with self.proxyPool.get() as proxy:
678
+ # update DB and buffer
679
+ ret, post_action = proxy.updateJobStatus(jobID, jobStatus, param, updateStateChange, attemptNr)
680
+ # take post-action
681
+ if post_action:
682
+ # get semaphore for job cloning with runonce
683
+ if post_action["action"] == "get_event":
684
+ event_ret = proxy.getEventRanges(post_action["pandaID"], post_action["jobsetID"], post_action["jediTaskID"], 1, True, False, None)
685
+ if not event_ret:
686
+ proxy.killJob(post_action["pandaID"], "job cloning", "", True)
687
+ ret = "tobekilled"
688
+ # get secrets for debug mode
689
+ if isinstance(ret, str) and "debug" in ret:
690
+ tmpS, secrets = proxy.get_user_secrets(panda_config.pilot_secrets)
691
+ if tmpS and secrets:
692
+ ret = {"command": ret, "secrets": secrets}
693
+ return ret
694
+
695
+ # update worker status by the pilot
696
+ def updateWorkerPilotStatus(self, workerID, harvesterID, status, node_id):
697
+ # get DB proxy
698
+ with self.proxyPool.get() as proxy:
699
+ # update DB and buffer
700
+ ret = proxy.updateWorkerPilotStatus(workerID, harvesterID, status, node_id)
701
+ return ret
702
+
703
+ def update_worker_node(
704
+ self,
705
+ site,
706
+ panda_queue,
707
+ host_name,
708
+ cpu_model,
709
+ cpu_model_normalized,
710
+ n_logical_cpus,
711
+ n_sockets,
712
+ cores_per_socket,
713
+ threads_per_core,
714
+ cpu_architecture,
715
+ cpu_architecture_level,
716
+ clock_speed,
717
+ total_memory,
718
+ total_local_disk,
719
+ ):
720
+ # get DB proxy
721
+ with self.proxyPool.get() as proxy:
722
+ # update DB and buffer
723
+ ret = proxy.update_worker_node(
724
+ site,
725
+ panda_queue,
726
+ host_name,
727
+ cpu_model,
728
+ cpu_model_normalized,
729
+ n_logical_cpus,
730
+ n_sockets,
731
+ cores_per_socket,
732
+ threads_per_core,
733
+ cpu_architecture,
734
+ cpu_architecture_level,
735
+ clock_speed,
736
+ total_memory,
737
+ total_local_disk,
738
+ )
739
+ return ret
740
+
741
+ def update_worker_node_gpu(
742
+ self,
743
+ site: str,
744
+ host_name: str,
745
+ vendor: str,
746
+ model: str,
747
+ count: int,
748
+ vram: int,
749
+ architecture: str,
750
+ framework: str,
751
+ framework_version: str,
752
+ driver_version: str,
753
+ ):
754
+ # get DB proxy
755
+ with self.proxyPool.get() as proxy:
756
+ # update DB and buffer
757
+ ret = proxy.update_worker_node_gpu(
758
+ site,
759
+ host_name,
760
+ vendor,
761
+ model,
762
+ count,
763
+ vram,
764
+ architecture,
765
+ framework,
766
+ framework_version,
767
+ driver_version,
768
+ )
769
+ return ret
770
+
771
+ def get_architecture_level_map(self):
772
+ # get DB proxy
773
+ with self.proxyPool.get() as proxy:
774
+ ret = proxy.get_architecture_level_map()
775
+ return ret
776
+
777
+ # finalize pending analysis jobs
778
+ def finalizePendingJobs(self, prodUserName, jobDefinitionID, waitLock=False):
779
+ # get DB proxy
780
+ with self.proxyPool.get() as proxy:
781
+ # update DB
782
+ ret = proxy.finalizePendingJobs(prodUserName, jobDefinitionID, waitLock)
783
+ return ret
784
+
785
+ # retry job
786
+ def retryJob(
787
+ self,
788
+ jobID,
789
+ param,
790
+ failedInActive=False,
791
+ changeJobInMem=False,
792
+ inMemJob=None,
793
+ getNewPandaID=False,
794
+ attemptNr=None,
795
+ recoverableEsMerge=False,
796
+ ):
797
+ # get DB proxy
798
+ with self.proxyPool.get() as proxy:
799
+ # update DB
800
+ ret = proxy.retryJob(
801
+ jobID,
802
+ param,
803
+ failedInActive,
804
+ changeJobInMem,
805
+ inMemJob,
806
+ getNewPandaID,
807
+ attemptNr,
808
+ recoverableEsMerge,
809
+ )
810
+ return ret
811
+
812
+ # activate jobs
813
+ def activateJobs(self, jobs):
814
+ # get DB proxy
815
+ with self.proxyPool.get() as proxy:
816
+ # loop over all jobs
817
+ returns = []
818
+ for job in jobs:
819
+ # update DB
820
+ ret = proxy.activateJob(job)
821
+ returns.append(ret)
822
+ return returns
823
+
824
+ # send jobs to jobsWaiting
825
+ def keepJobs(self, jobs):
826
+ # get DB proxy
827
+ with self.proxyPool.get() as proxy:
828
+ # loop over all jobs
829
+ returns = []
830
+ for job in jobs:
831
+ # update DB
832
+ ret = proxy.keepJob(job)
833
+ returns.append(ret)
834
+ return returns
835
+
836
+ # archive jobs
837
+ def archiveJobs(self, jobs, inJobsDefined, fromJobsWaiting=False):
838
+ # get DB proxy
839
+ with self.proxyPool.get() as proxy:
840
+ # loop over all jobs
841
+ returns = []
842
+ for job in jobs:
843
+ # update DB
844
+ ret = proxy.archiveJob(job, inJobsDefined, fromJobsWaiting=fromJobsWaiting)
845
+ returns.append(ret[0])
846
+ return returns
847
+
848
+ # set debug mode
849
+ def setDebugMode(self, dn, pandaID, prodManager, modeOn, workingGroup):
850
+ # get DB proxy
851
+ with self.proxyPool.get() as proxy:
852
+ # check the number of debug jobs
853
+ hitLimit = False
854
+ if modeOn is True:
855
+ if prodManager:
856
+ limitNum = None
857
+ elif workingGroup is not None:
858
+ jobList = proxy.getActiveDebugJobs(workingGroup=workingGroup)
859
+ limitNum = ProcessGroups.maxDebugWgJobs
860
+ else:
861
+ jobList = proxy.getActiveDebugJobs(dn=dn)
862
+ limitNum = ProcessGroups.maxDebugJobs
863
+ if limitNum and len(jobList) >= limitNum:
864
+ # exceeded
865
+ retStr = "You already hit the limit on the maximum number of debug subjobs "
866
+ retStr += f"({limitNum} jobs). "
867
+ retStr += "Please set the debug mode off for one of the following PandaIDs : "
868
+ for tmpID in jobList:
869
+ retStr += f"{tmpID},"
870
+ retStr = retStr[:-1]
871
+ hitLimit = True
872
+ if not hitLimit:
873
+ # execute
874
+ retStr = proxy.setDebugMode(dn, pandaID, prodManager, modeOn, workingGroup)
875
+ return retStr
876
+
877
+ # get jobs
878
+ def getJobs(
879
+ self,
880
+ nJobs,
881
+ siteName,
882
+ prodSourceLabel,
883
+ mem,
884
+ diskSpace,
885
+ node,
886
+ timeout,
887
+ computingElement,
888
+ prodUserID,
889
+ taskID,
890
+ background,
891
+ resourceType,
892
+ harvester_id,
893
+ worker_id,
894
+ schedulerID,
895
+ jobType,
896
+ is_gu,
897
+ via_topic,
898
+ remaining_time,
899
+ ):
900
+ # get DBproxy
901
+ with self.proxyPool.get() as proxy:
902
+ # get waiting jobs
903
+ t_before = time.time()
904
+ jobs, nSent = proxy.getJobs(
905
+ nJobs,
906
+ siteName,
907
+ prodSourceLabel,
908
+ mem,
909
+ diskSpace,
910
+ node,
911
+ timeout,
912
+ computingElement,
913
+ prodUserID,
914
+ taskID,
915
+ background,
916
+ resourceType,
917
+ harvester_id,
918
+ worker_id,
919
+ schedulerID,
920
+ jobType,
921
+ is_gu,
922
+ via_topic,
923
+ remaining_time,
924
+ )
925
+ t_after = time.time()
926
+ t_total = t_after - t_before
927
+ _logger.debug(f"getJobs : took {t_total}s for {siteName} nJobs={nJobs} prodSourceLabel={prodSourceLabel}")
928
+
929
+ # get secret
930
+ secrets_map = {}
931
+ for job in jobs:
932
+ if job.use_secrets() and job.prodUserName not in secrets_map:
933
+ # get secret
934
+ with self.proxyPool.get() as proxy:
935
+ tmp_status, secret = proxy.get_user_secrets(job.prodUserName)
936
+ if not tmp_status:
937
+ secret = None
938
+ secrets_map[job.prodUserName] = secret
939
+ if job.is_debug_mode():
940
+ if panda_config.pilot_secrets not in secrets_map:
941
+ # get secret
942
+ with self.proxyPool.get() as proxy:
943
+ tmp_status, secret = proxy.get_user_secrets(panda_config.pilot_secrets)
944
+ if not tmp_status:
945
+ secret = None
946
+ secrets_map[panda_config.pilot_secrets] = secret
947
+
948
+ return jobs + [nSent, {}, secrets_map]
949
+
950
+ # get job status
951
+ def getJobStatus(
952
+ self,
953
+ jobIDs,
954
+ fromDefined=True,
955
+ fromActive=True,
956
+ fromArchived=True,
957
+ fromWaiting=True,
958
+ ):
959
+ # get DBproxy
960
+ with self.proxyPool.get() as proxy:
961
+ retStatus = []
962
+ # peek at job
963
+ for jobID in jobIDs:
964
+ res = proxy.peekJob(jobID, fromDefined, fromActive, fromArchived, fromWaiting)
965
+ if res:
966
+ retStatus.append(res.jobStatus)
967
+ else:
968
+ retStatus.append(None)
969
+ return retStatus
970
+
971
+ # peek at jobs
972
+ def peekJobs(
973
+ self,
974
+ jobIDs,
975
+ fromDefined=True,
976
+ fromActive=True,
977
+ fromArchived=True,
978
+ fromWaiting=True,
979
+ forAnal=False,
980
+ use_json=False,
981
+ ):
982
+ # get proxy
983
+ with self.proxyPool.get() as proxy:
984
+ retJobs = []
985
+ # peek at job
986
+ for jobID in jobIDs:
987
+ res = proxy.peekJob(jobID, fromDefined, fromActive, fromArchived, fromWaiting, forAnal)
988
+ if res:
989
+ if use_json:
990
+ retJobs.append(res.to_dict())
991
+ else:
992
+ retJobs.append(res)
993
+ else:
994
+ retJobs.append(None)
995
+ return retJobs
996
+
997
+ # get PandaIDs with TaskID
998
+ def getPandaIDsWithTaskID(self, jediTaskID: int, scout_only: bool = False, unsuccessful_only: bool = False) -> list[int]:
999
+ """Get PanDA job IDs associated with a JEDI task.
1000
+
1001
+ Args:
1002
+ jediTaskID: JEDI task ID.
1003
+ scout_only: When True, return only scout job IDs.
1004
+ unsuccessful_only: When True, return only job IDs with status failed, cancelled, or closed.
1005
+
1006
+ Returns:
1007
+ list[int]: PanDA job IDs for the task.
1008
+ """
1009
+ # get DBproxy
1010
+ with self.proxyPool.get() as proxy:
1011
+ # exec
1012
+ retJobs = proxy.getPandaIDsWithTaskID(jediTaskID, scout_only=scout_only, unsuccessful_only=unsuccessful_only)
1013
+ return retJobs
1014
+
1015
+ # get full job status
1016
+ def getFullJobStatus(self, jobIDs, fromDefined=True, fromActive=True, fromArchived=True, fromWaiting=True, forAnal=True, days=30, use_json=False):
1017
+ retJobMap = {}
1018
+
1019
+ # peek at job
1020
+ for jobID in jobIDs:
1021
+ # get DBproxy for each job to avoid occupying connection for long time
1022
+ with self.proxyPool.get() as proxy:
1023
+ # peek job
1024
+ res = proxy.peekJob(jobID, fromDefined, fromActive, fromArchived, fromWaiting, forAnal)
1025
+ retJobMap[jobID] = res
1026
+
1027
+ # get IDs
1028
+ for jobID in jobIDs:
1029
+ if retJobMap[jobID] is None:
1030
+ # get ArchiveDBproxy
1031
+ with self.proxyPool.get() as proxy:
1032
+ # peek job
1033
+ res = proxy.peekJobLog(jobID, days)
1034
+ retJobMap[jobID] = res
1035
+
1036
+ # sort
1037
+ retJobs = []
1038
+ for jobID in jobIDs:
1039
+ if use_json:
1040
+ if retJobMap[jobID] is None:
1041
+ retJobs.append(None)
1042
+ else:
1043
+ retJobs.append(retJobMap[jobID].to_dict())
1044
+ else:
1045
+ retJobs.append(retJobMap[jobID])
1046
+
1047
+ return retJobs
1048
+
1049
+ # get script for offline running
1050
+ def getScriptOfflineRunning(self, pandaID, days=None):
1051
+ try:
1052
+ # get job
1053
+ tmpJobs = self.getFullJobStatus([pandaID], days=days)
1054
+ if tmpJobs == [] or tmpJobs[0] is None:
1055
+ errStr = f"ERROR: Cannot get PandaID={pandaID} in DB "
1056
+ if days is None:
1057
+ errStr += "for the last 30 days. You may add &days=N to the URL"
1058
+ else:
1059
+ errStr += f"for the last {days} days. You may change &days=N in the URL"
1060
+ return errStr
1061
+ tmpJob = tmpJobs[0]
1062
+ # user job
1063
+ isUser = False
1064
+ for trf in [
1065
+ "runAthena",
1066
+ "runGen",
1067
+ "runcontainer",
1068
+ "runMerge",
1069
+ "buildJob",
1070
+ "buildGen",
1071
+ ]:
1072
+ if trf in tmpJob.transformation:
1073
+ isUser = True
1074
+ break
1075
+ # check prodSourceLabel
1076
+ if tmpJob.prodSourceLabel == "user":
1077
+ isUser = True
1078
+ if isUser:
1079
+ tmpAtls = [tmpJob.AtlasRelease]
1080
+ tmpRels = [re.sub("^AnalysisTransforms-*", "", tmpJob.homepackage)]
1081
+ tmpPars = [tmpJob.jobParameters]
1082
+ tmpTrfs = [tmpJob.transformation]
1083
+ else:
1084
+ # release and trf
1085
+ tmpAtls = tmpJob.AtlasRelease.split("\n")
1086
+ tmpRels = tmpJob.homepackage.split("\n")
1087
+ tmpPars = tmpJob.jobParameters.split("\n")
1088
+ tmpTrfs = tmpJob.transformation.split("\n")
1089
+ if not (len(tmpRels) == len(tmpPars) == len(tmpTrfs)):
1090
+ return "ERROR: The number of releases or parameters or trfs is inconsistent with others"
1091
+ # construct script
1092
+ scrStr = (
1093
+ "#!/bin/bash\n\n"
1094
+ "# To rerun the job interactively :\n"
1095
+ "# 1) download this script\n"
1096
+ "# 2) chmod +x ./<this script>\n"
1097
+ "# 3) setupATLAS\n"
1098
+ "# 4) ./<this script>\n\n"
1099
+ "temp_file=$(mktemp)\n"
1100
+ 'cat << EOF > "$temp_file"\n\n'
1101
+ "source ${ATLAS_LOCAL_ROOT_BASE}/user/atlasLocalSetup.sh\n"
1102
+ "lsetup rucio\n\n"
1103
+ "#retrieve inputs\n\n"
1104
+ )
1105
+ # collect inputs
1106
+ dsFileMap = {}
1107
+ for tmpFile in tmpJob.Files:
1108
+ if tmpFile.type == "input":
1109
+ if tmpFile.dataset not in dsFileMap:
1110
+ dsFileMap[tmpFile.dataset] = []
1111
+ if tmpFile.lfn not in dsFileMap[tmpFile.dataset]:
1112
+ dsFileMap[tmpFile.dataset].append(tmpFile.scope + ":" + tmpFile.lfn)
1113
+ # get
1114
+ for tmpDS in dsFileMap:
1115
+ tmpFileList = dsFileMap[tmpDS]
1116
+ for tmpLFN in tmpFileList:
1117
+ scrStr += f"rucio download {tmpLFN} --no-subdir\n"
1118
+ if isUser:
1119
+ scrStr += "\n#get trf\n"
1120
+ scrStr += f"wget {tmpTrfs[0]}\n"
1121
+ scrStr += f"chmod +x {tmpTrfs[0].split('/')[-1]}\n"
1122
+ scrStr += "\n#transform commands\n\n"
1123
+ for tmpIdx, tmpRel in enumerate(tmpRels):
1124
+ # asetup
1125
+ atlRel = re.sub("Atlas-", "", tmpAtls[tmpIdx])
1126
+ atlTags = re.split("[/_]", tmpRel)
1127
+ if "" in atlTags:
1128
+ atlTags.remove("")
1129
+ if atlRel != "" and atlRel not in atlTags and (re.search("^\d+\.\d+\.\d+$", atlRel) is None or isUser):
1130
+ atlTags.append(atlRel)
1131
+ try:
1132
+ cmtConfig = [s for s in tmpJob.cmtConfig.split("@") if s][-1]
1133
+ except Exception:
1134
+ cmtConfig = ""
1135
+ scrStr += f"asetup --platform={tmpJob.cmtConfig.split('@')[0]} {','.join(atlTags)}\n"
1136
+ # athenaMP
1137
+ if tmpJob.coreCount not in ["NULL", None] and tmpJob.coreCount > 1:
1138
+ scrStr += f"export ATHENA_PROC_NUMBER={tmpJob.coreCount}\n"
1139
+ scrStr += f"export ATHENA_CORE_NUMBER={tmpJob.coreCount}\n"
1140
+ # add double quotes for zsh
1141
+ tmpParamStr = tmpPars[tmpIdx]
1142
+ tmpSplitter = shlex.shlex(tmpParamStr, posix=True)
1143
+ tmpSplitter.whitespace = " "
1144
+ tmpSplitter.whitespace_split = True
1145
+ # loop for params
1146
+ for tmpItem in tmpSplitter:
1147
+ tmpMatch = re.search("^(-[^=]+=)(.+)$", tmpItem)
1148
+ if tmpMatch is not None:
1149
+ tmpArgName = tmpMatch.group(1)
1150
+ tmpArgVal = tmpMatch.group(2)
1151
+ tmpArgIdx = tmpParamStr.find(tmpArgName) + len(tmpArgName)
1152
+ # add "
1153
+ if tmpParamStr[tmpArgIdx] != '"':
1154
+ tmpParamStr = tmpParamStr.replace(tmpMatch.group(0), tmpArgName + '"' + tmpArgVal + '"')
1155
+ # run trf
1156
+ if isUser:
1157
+ scrStr += "./"
1158
+ tmpParamStr += " --debug"
1159
+ scrStr += f"{tmpTrfs[tmpIdx].split('/')[-1]} {tmpParamStr}\n\n"
1160
+ scrStr += "EOF\n\n" 'chmod +x "$temp_file"\n'
1161
+ scrStr += 'source ${ATLAS_LOCAL_ROOT_BASE}/user/atlasLocalSetup.sh -c %s -r "$temp_file"\n' % cmtConfig
1162
+ scrStr += 'rm "$temp_file"\n'
1163
+ return scrStr
1164
+ except Exception as e:
1165
+ _logger.error(f"getScriptOfflineRunning : {str(e)} {traceback.format_exc()}")
1166
+ return f"ERROR: ServerError in getScriptOfflineRunning with {str(e)}"
1167
+
1168
+ # kill jobs
1169
+ def killJobs(self, ids, user, code, prodManager, wgProdRole=[], killOptions=[]):
1170
+ tmp_log = LogWrapper(_logger, "killJobs")
1171
+ tmp_log.debug(f"start for {len(ids)} IDs")
1172
+ # get DBproxy
1173
+ with self.proxyPool.get() as proxy:
1174
+ rets = []
1175
+ # kill jobs
1176
+ pandaIDforCloserMap = {}
1177
+ for id in ids:
1178
+ # retry event service merge
1179
+ toKill = True
1180
+ if "keepUnmerged" in killOptions:
1181
+ tmpJobSpec = proxy.peekJob(id, True, True, False, False, False)
1182
+ if tmpJobSpec is not None:
1183
+ if EventServiceUtils.isEventServiceMerge(tmpJobSpec):
1184
+ # retry ES merge jobs not to discard events
1185
+ proxy.retryJob(
1186
+ id,
1187
+ {},
1188
+ getNewPandaID=True,
1189
+ attemptNr=tmpJobSpec.attemptNr,
1190
+ recoverableEsMerge=True,
1191
+ )
1192
+ elif EventServiceUtils.isEventServiceJob(tmpJobSpec) and not EventServiceUtils.isJobCloningJob(tmpJobSpec):
1193
+ # get number of started events
1194
+ nEvt = proxy.getNumStartedEvents(tmpJobSpec)
1195
+ # not to kill jobset if there are started events
1196
+ if nEvt is not None and nEvt > 0:
1197
+ # set sub status if any
1198
+ for killOpt in killOptions:
1199
+ if killOpt.startswith("jobSubStatus"):
1200
+ tmpJobSpec.jobSubStatus = killOpt.split("=")[-1]
1201
+ break
1202
+ # trigger ppE for ES jobs to properly trigger subsequent procedures
1203
+ ret = proxy.archiveJob(
1204
+ tmpJobSpec,
1205
+ tmpJobSpec.jobStatus in ["defined", "assigned"],
1206
+ )
1207
+ toKill = False
1208
+ userInfo = {"prodSourceLabel": None}
1209
+ if toKill:
1210
+ ret, userInfo = proxy.killJob(id, user, code, prodManager, True, wgProdRole, killOptions)
1211
+ rets.append(ret)
1212
+ if ret and userInfo["prodSourceLabel"] in ["user", "managed", "test"]:
1213
+ jobIDKey = (
1214
+ userInfo["prodUserID"],
1215
+ userInfo["jobDefinitionID"],
1216
+ userInfo["jobsetID"],
1217
+ )
1218
+ if jobIDKey not in pandaIDforCloserMap:
1219
+ pandaIDforCloserMap[jobIDKey] = id
1220
+ # run Closer
1221
+ try:
1222
+ if pandaIDforCloserMap != {}:
1223
+ for pandaIDforCloser in pandaIDforCloserMap.values():
1224
+ tmpJobs = self.peekJobs([pandaIDforCloser])
1225
+ tmpJob = tmpJobs[0]
1226
+ if tmpJob is not None:
1227
+ tmpDestDBlocks = []
1228
+ # get destDBlock
1229
+ for tmpFile in tmpJob.Files:
1230
+ if tmpFile.type in ["output", "log"]:
1231
+ if tmpFile.destinationDBlock not in tmpDestDBlocks:
1232
+ tmpDestDBlocks.append(tmpFile.destinationDBlock)
1233
+ # run
1234
+ closer_process = Closer(self, tmpDestDBlocks, tmpJob)
1235
+ closer_process.run()
1236
+ except Exception as e:
1237
+ tmp_log.error(f"failed with {str(e)} {traceback.format_exc()}")
1238
+ tmp_log.debug("done")
1239
+ return rets
1240
+
1241
+ # reassign jobs
1242
+ def reassignJobs(
1243
+ self,
1244
+ ids,
1245
+ attempt=0,
1246
+ joinThr=False,
1247
+ forPending=False,
1248
+ firstSubmission=True,
1249
+ ):
1250
+ tmp_log = LogWrapper(_logger, "reassignJobs")
1251
+ tmp_log.debug(f"start for {len(ids)} IDs")
1252
+ # get DB proxy
1253
+ with self.proxyPool.get() as proxy:
1254
+ jobs = []
1255
+ # reset jobs
1256
+ n_reset_waiting = 0
1257
+ n_reset_defined = 0
1258
+ for tmp_id in ids:
1259
+ try:
1260
+ # try to reset a job in waiting table
1261
+ ret = proxy.resetJob(
1262
+ tmp_id,
1263
+ activeTable=False,
1264
+ forPending=True,
1265
+ )
1266
+ if ret is not None:
1267
+ jobs.append(ret)
1268
+ n_reset_waiting += 1
1269
+ # waiting jobs don't create sub or dis
1270
+ continue
1271
+ # try to reset a job in defined table
1272
+ ret = proxy.resetDefinedJob(tmp_id)
1273
+ if ret is not None:
1274
+ jobs.append(ret)
1275
+ n_reset_defined += 1
1276
+ continue
1277
+ except Exception as e:
1278
+ tmp_log.error(f"failed with {str(e)} {traceback.format_exc()}")
1279
+ tmp_log.debug(f"got {len(jobs)} IDs in total: {n_reset_waiting} from Waiting, {n_reset_defined} from Defined")
1280
+ # trigger subsequent agent
1281
+ if jobs:
1282
+ if joinThr:
1283
+ thr = Setupper(
1284
+ self,
1285
+ jobs,
1286
+ resubmit=True,
1287
+ first_submission=firstSubmission,
1288
+ )
1289
+ thr.start()
1290
+ thr.join()
1291
+ else:
1292
+ # cannot use 'thr =' because it may trigger a garbage collector
1293
+ Setupper(
1294
+ self,
1295
+ jobs,
1296
+ resubmit=True,
1297
+ first_submission=firstSubmission,
1298
+ ).start()
1299
+ tmp_log.debug("done")
1300
+
1301
+ return True
1302
+
1303
+ # update input files and return corresponding PandaIDs
1304
+ def updateInFilesReturnPandaIDs(self, dataset, status, fileLFN=""):
1305
+ # get DBproxy
1306
+ with self.proxyPool.get() as proxy:
1307
+ retList = []
1308
+ # query PandaID
1309
+ retList = proxy.updateInFilesReturnPandaIDs(dataset, status, fileLFN)
1310
+ return retList
1311
+
1312
+ # update input files for jobs at certain sites and return corresponding PandaIDs
1313
+ def update_input_files_at_sites_and_get_panda_ids(self, filename: str, sites: list) -> list:
1314
+ with self.proxyPool.get() as proxy:
1315
+ ret = proxy.update_input_files_at_sites_and_get_panda_ids(filename, sites)
1316
+ return ret
1317
+
1318
+ # update output files and return corresponding PandaIDs
1319
+ def updateOutFilesReturnPandaIDs(self, dataset, fileLFN=""):
1320
+ # get DBproxy
1321
+ with self.proxyPool.get() as proxy:
1322
+ retList = []
1323
+ # query PandaID
1324
+ retList = proxy.updateOutFilesReturnPandaIDs(dataset, fileLFN)
1325
+ return retList
1326
+
1327
+ # get _dis datasets associated to _sub
1328
+ def getAssociatedDisDatasets(self, subDsName):
1329
+ # get DBproxy
1330
+ with self.proxyPool.get() as proxy:
1331
+ retList = []
1332
+ # query
1333
+ retList = proxy.getAssociatedDisDatasets(subDsName)
1334
+ return retList
1335
+
1336
+ # insert sandbox file info
1337
+ def insertSandboxFileInfo(self, userName, hostName, fileName, fileSize, checkSum):
1338
+ # get DBproxy
1339
+ with self.proxyPool.get() as proxy:
1340
+ # exec
1341
+ ret = proxy.insertSandboxFileInfo(userName, hostName, fileName, fileSize, checkSum)
1342
+ return ret
1343
+
1344
+ # get and lock sandbox files
1345
+ def getLockSandboxFiles(self, time_limit, n_files):
1346
+ # get DBproxy
1347
+ with self.proxyPool.get() as proxy:
1348
+ # exec
1349
+ ret = proxy.getLockSandboxFiles(time_limit, n_files)
1350
+ return ret
1351
+
1352
+ # check duplicated sandbox file
1353
+ def checkSandboxFile(self, userName, fileSize, checkSum):
1354
+ # get DBproxy
1355
+ with self.proxyPool.get() as proxy:
1356
+ # exec
1357
+ ret = proxy.checkSandboxFile(userName, fileSize, checkSum)
1358
+ return ret
1359
+
1360
+ # insert datasets
1361
+ def insertDatasets(self, datasets):
1362
+ # get DBproxy
1363
+ with self.proxyPool.get() as proxy:
1364
+ retList = []
1365
+ # insert
1366
+ for dataset in datasets:
1367
+ ret = proxy.insertDataset(dataset)
1368
+ retList.append(ret)
1369
+ return retList
1370
+
1371
+ # get and lock dataset with a query
1372
+ def getLockDatasets(self, sqlQuery, varMapGet, modTimeOffset="", getVersion=False):
1373
+ # get DBproxy
1374
+ with self.proxyPool.get() as proxy:
1375
+ # query Dataset
1376
+ ret = proxy.getLockDatasets(sqlQuery, varMapGet, modTimeOffset, getVersion)
1377
+ return ret
1378
+
1379
+ # query Dataset
1380
+ def queryDatasetWithMap(self, map):
1381
+ # get DBproxy
1382
+ with self.proxyPool.get() as proxy:
1383
+ # query Dataset
1384
+ ret = proxy.queryDatasetWithMap(map)
1385
+ return ret
1386
+
1387
+ # set GUIDs
1388
+ def setGUIDs(self, files):
1389
+ # get DBproxy
1390
+ with self.proxyPool.get() as proxy:
1391
+ # set GUIDs
1392
+ ret = proxy.setGUIDs(files)
1393
+ return ret
1394
+
1395
+ # update dataset
1396
+ def updateDatasets(self, datasets, withLock=False, withCriteria="", criteriaMap={}):
1397
+ # get DBproxy
1398
+ with self.proxyPool.get() as proxy:
1399
+ # update Dataset
1400
+ retList = proxy.updateDataset(datasets, withLock, withCriteria, criteriaMap)
1401
+ return retList
1402
+
1403
+ # trigger cleanup of internal datasets used by a task
1404
+ def trigger_cleanup_internal_datasets(self, task_id: int) -> bool:
1405
+ with self.proxyPool.get() as proxy:
1406
+ ret = proxy.trigger_cleanup_internal_datasets(task_id)
1407
+ return ret
1408
+
1409
+ # count the number of files with map
1410
+ def countFilesWithMap(self, map):
1411
+ # get DBproxy
1412
+ with self.proxyPool.get() as proxy:
1413
+ # query files
1414
+ ret = proxy.countFilesWithMap(map)
1415
+ return ret
1416
+
1417
+ # get serial number for dataset
1418
+ def getSerialNumber(self, datasetname, definedFreshFlag=None):
1419
+ # get DBproxy
1420
+ with self.proxyPool.get() as proxy:
1421
+ # get serial number
1422
+ ret = proxy.getSerialNumber(datasetname, definedFreshFlag)
1423
+ return ret
1424
+
1425
+ # add metadata
1426
+ def addMetadata(self, ids, metadataList, newStatusList):
1427
+ # get DBproxy
1428
+ with self.proxyPool.get() as proxy:
1429
+ # add metadata
1430
+ index = 0
1431
+ retList = []
1432
+ for id in ids:
1433
+ ret = proxy.addMetadata(id, metadataList[index], newStatusList[index])
1434
+ retList.append(ret)
1435
+ index += 1
1436
+ return retList
1437
+
1438
+ # add stdout
1439
+ def addStdOut(self, id, stdout):
1440
+ # get DBproxy
1441
+ with self.proxyPool.get() as proxy:
1442
+ # add
1443
+ ret = proxy.addStdOut(id, stdout)
1444
+ return ret
1445
+
1446
+ # extract scope from dataset name
1447
+ def extractScope(self, name):
1448
+ # get DBproxy
1449
+ with self.proxyPool.get() as proxy:
1450
+ # get
1451
+ ret = proxy.extractScope(name)
1452
+ return ret
1453
+
1454
+ # get job statistics
1455
+ def getJobStatistics(self):
1456
+ # get DBproxy
1457
+ with self.proxyPool.get() as proxy:
1458
+ # get serial number
1459
+ ret = proxy.getJobStatistics()
1460
+ return ret
1461
+
1462
+ # get detailed job statistics with resource_type and prodsourcelabel
1463
+ def getDetailedJobStatistics(self):
1464
+ with self.proxyPool.get() as proxy:
1465
+ ret = proxy.getDetailedJobStatistics()
1466
+ return ret
1467
+
1468
+ # get job statistics for ExtIF. Source type is analysis or production
1469
+ def getJobStatisticsForExtIF(self, sourcetype=None):
1470
+ # get DBproxy
1471
+ with self.proxyPool.get() as proxy:
1472
+ # get serial number
1473
+ ret = proxy.getJobStatisticsForExtIF(sourcetype)
1474
+ return ret
1475
+
1476
+ # get job statistics for Bamboo
1477
+ def getJobStatisticsForBamboo(self):
1478
+ # get DBproxy
1479
+ with self.proxyPool.get() as proxy:
1480
+ # get serial number
1481
+ ret = proxy.getJobStatisticsPerProcessingType()
1482
+ return ret
1483
+
1484
+ # update site data
1485
+ def updateSiteData(self, hostID, pilotRequests, interval=3):
1486
+ # get DBproxy
1487
+ with self.proxyPool.get() as proxy:
1488
+ # get serial number
1489
+ ret = proxy.updateSiteData(hostID, pilotRequests, interval)
1490
+ return ret
1491
+
1492
+ # get current site data
1493
+ def getCurrentSiteData(self):
1494
+ # get DBproxy
1495
+ with self.proxyPool.get() as proxy:
1496
+ # get serial number
1497
+ ret = proxy.getCurrentSiteData()
1498
+ return ret
1499
+
1500
+ # insert nRunning in site data
1501
+ def insertnRunningInSiteData(self):
1502
+ # get DBproxy
1503
+ with self.proxyPool.get() as proxy:
1504
+ # get serial number
1505
+ ret = proxy.insertnRunningInSiteData()
1506
+ return ret
1507
+
1508
+ # get site info
1509
+ def getSiteInfo(self):
1510
+ # get DBproxy
1511
+ with self.proxyPool.get() as proxy:
1512
+ # get site info
1513
+ ret = proxy.getSiteInfo()
1514
+ return ret
1515
+
1516
+ # get cloud list
1517
+ def get_cloud_list(self):
1518
+ # get DBproxy
1519
+ with self.proxyPool.get() as proxy:
1520
+ # get cloud list
1521
+ ret = proxy.get_cloud_list()
1522
+ return ret
1523
+
1524
+ # get special dispatcher parameters
1525
+ def get_special_dispatch_params(self):
1526
+ # get DBproxy
1527
+ with self.proxyPool.get() as proxy:
1528
+ # exec
1529
+ ret = proxy.get_special_dispatch_params()
1530
+ return ret
1531
+
1532
+ # get email address
1533
+ def getEmailAddr(self, name, withDN=False, withUpTime=False):
1534
+ # get DBproxy
1535
+ with self.proxyPool.get() as proxy:
1536
+ # get
1537
+ ret = proxy.getEmailAddr(name, withDN, withUpTime)
1538
+ return ret
1539
+
1540
+ # set email address for a user
1541
+ def setEmailAddr(self, userName, emailAddr):
1542
+ # get DBproxy
1543
+ with self.proxyPool.get() as proxy:
1544
+ # set
1545
+ ret = proxy.setEmailAddr(userName, emailAddr)
1546
+ return ret
1547
+
1548
+ # get banned users
1549
+ def get_ban_users(self):
1550
+ # get DBproxy
1551
+ with self.proxyPool.get() as proxy:
1552
+ # get
1553
+ ret = proxy.get_ban_users()
1554
+ return ret
1555
+
1556
+ # register a token key
1557
+ def register_token_key(self, client_name, lifetime):
1558
+ # get DBproxy
1559
+ with self.proxyPool.get() as proxy:
1560
+ # register proxy key
1561
+ ret = proxy.register_token_key(client_name, lifetime)
1562
+ return ret
1563
+
1564
+ # query an SQL return Status
1565
+ def querySQLS(self, sql, varMap, arraySize=1000):
1566
+ # get DBproxy
1567
+ with self.proxyPool.get() as proxy:
1568
+ # get
1569
+ ret = proxy.querySQLS(sql, varMap, arraySize)
1570
+ return ret
1571
+
1572
+ # query an SQL
1573
+ def querySQL(self, sql, varMap, arraySize=1000):
1574
+ # get DBproxy
1575
+ with self.proxyPool.get() as proxy:
1576
+ # get
1577
+ ret = proxy.querySQLS(sql, varMap, arraySize)[1]
1578
+ return ret
1579
+
1580
+ # execute an SQL return with executemany
1581
+ def executemanySQL(self, sql, varMaps, arraySize=1000):
1582
+ # get DBproxy
1583
+ with self.proxyPool.get() as proxy:
1584
+ # get
1585
+ ret = proxy.executemanySQL(sql, varMaps, arraySize)
1586
+ return ret
1587
+
1588
+ # check quota
1589
+ def checkQuota(self, dn):
1590
+ # query an SQL return Status
1591
+ with self.proxyPool.get() as proxy:
1592
+ # get
1593
+ ret = proxy.checkQuota(dn)
1594
+ return ret
1595
+
1596
+ # insert TaskParams
1597
+ def insertTaskParamsPanda(self, taskParams, user, prodRole, fqans=[], parent_tid=None, properErrorCode=False, allowActiveTask=False, decode=True):
1598
+ # query an SQL return Status
1599
+ with self.proxyPool.get() as proxy:
1600
+ # check user status
1601
+ tmpStatus = proxy.checkBanUser(user, None, True)
1602
+ if tmpStatus is True:
1603
+ # exec
1604
+ ret = proxy.insertTaskParamsPanda(taskParams, user, prodRole, fqans, parent_tid, properErrorCode, allowActiveTask, decode)
1605
+ elif tmpStatus == 1:
1606
+ ret = False, "Failed to update DN in PandaDB"
1607
+ elif tmpStatus == 2:
1608
+ ret = False, "Failed to insert user info to PandaDB"
1609
+ else:
1610
+ ret = False, f"The following DN is banned: DN={user}"
1611
+ return ret
1612
+
1613
+ # send command to task
1614
+ def sendCommandTaskPanda(
1615
+ self,
1616
+ jediTaskID,
1617
+ dn,
1618
+ prodRole,
1619
+ comStr,
1620
+ comComment=None,
1621
+ useCommit=True,
1622
+ properErrorCode=False,
1623
+ comQualifier=None,
1624
+ broadcast=False,
1625
+ ):
1626
+ # query an SQL return Status
1627
+ with self.proxyPool.get() as proxy:
1628
+ # exec
1629
+ ret = proxy.sendCommandTaskPanda(
1630
+ jediTaskID,
1631
+ dn,
1632
+ prodRole,
1633
+ comStr,
1634
+ comComment,
1635
+ useCommit,
1636
+ properErrorCode,
1637
+ comQualifier,
1638
+ broadcast,
1639
+ )
1640
+ return ret
1641
+
1642
+ # update unmerged datasets to trigger merging
1643
+ def updateUnmergedDatasets(self, job, finalStatusDS, updateCompleted=False):
1644
+ # get proxy
1645
+ with self.proxyPool.get() as proxy:
1646
+ # exec
1647
+ ret = proxy.updateUnmergedDatasets(job, finalStatusDS, updateCompleted)
1648
+ return ret
1649
+
1650
+ # get active JediTasks in a time range
1651
+ def getJediTasksInTimeRange(self, dn, timeRangeStr, fullFlag=False, minTaskID=None, task_type="user"):
1652
+ # check DN
1653
+ if dn in ["NULL", "", "None", None]:
1654
+ return {}
1655
+ # check timeRange
1656
+ match = re.match("^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$", timeRangeStr)
1657
+ if match is None:
1658
+ return {}
1659
+ timeRange = datetime.datetime(
1660
+ year=int(match.group(1)),
1661
+ month=int(match.group(2)),
1662
+ day=int(match.group(3)),
1663
+ hour=int(match.group(4)),
1664
+ minute=int(match.group(5)),
1665
+ second=int(match.group(6)),
1666
+ )
1667
+ # max range is 3 months
1668
+ maxRange = naive_utcnow() - datetime.timedelta(days=30)
1669
+ if timeRange < maxRange:
1670
+ timeRange = maxRange
1671
+ # get proxy
1672
+ with self.proxyPool.get() as proxy:
1673
+ # exec
1674
+ ret = proxy.getJediTasksInTimeRange(dn, timeRange, fullFlag, minTaskID, task_type)
1675
+ return ret
1676
+
1677
+ # get details of JediTask
1678
+ def getJediTaskDetails(self, jediTaskID, fullFlag, withTaskInfo):
1679
+ # get proxy
1680
+ with self.proxyPool.get() as proxy:
1681
+ # exec
1682
+ ret = proxy.getJediTaskDetails(jediTaskID, fullFlag, withTaskInfo)
1683
+ return ret
1684
+
1685
+ # get task details as a plain dict (read-only, no lock)
1686
+ def get_task_details_json(self, jedi_task_id, resolve_parent=False, include_resolve_status=False):
1687
+ with self.proxyPool.get() as proxy:
1688
+ return proxy.get_task_details_json(
1689
+ jedi_task_id,
1690
+ resolve_parent=resolve_parent,
1691
+ include_resolve_status=include_resolve_status,
1692
+ )
1693
+
1694
+ # get a list of even ranges for a PandaID
1695
+ def getEventRanges(self, pandaID, jobsetID, jediTaskID, nRanges, acceptJson, scattered, segment_id):
1696
+ # get proxy
1697
+ with self.proxyPool.get() as proxy:
1698
+ # exec
1699
+ ret = proxy.getEventRanges(pandaID, jobsetID, jediTaskID, nRanges, acceptJson, scattered, segment_id)
1700
+ return ret
1701
+
1702
+ # update an even range
1703
+ def updateEventRange(self, eventRangeID, eventStatus, cpuCore, cpuConsumptionTime, objstoreID=None):
1704
+ eventDict = {}
1705
+ eventDict["eventRangeID"] = eventRangeID
1706
+ eventDict["eventStatus"] = eventStatus
1707
+ eventDict["cpuCore"] = cpuCore
1708
+ eventDict["cpuConsumptionTime"] = cpuConsumptionTime
1709
+ eventDict["objstoreID"] = objstoreID
1710
+ # get proxy
1711
+ with self.proxyPool.get() as proxy:
1712
+ # exec
1713
+ ret = proxy.updateEventRanges([eventDict])
1714
+ # extract return
1715
+ try:
1716
+ retVal = ret[0][0]
1717
+ except Exception:
1718
+ retVal = False
1719
+
1720
+ return retVal, json.dumps(ret[1])
1721
+
1722
+ # update even ranges
1723
+ def updateEventRanges(self, eventRanges, version=0):
1724
+ # decode json
1725
+ try:
1726
+ eventRanges = json.loads(eventRanges)
1727
+ except Exception:
1728
+ return json.dumps("ERROR : failed to convert eventRanges with json")
1729
+ # get proxy
1730
+ with self.proxyPool.get() as proxy:
1731
+ # exec
1732
+ ret = proxy.updateEventRanges(eventRanges, version)
1733
+ if version != 0:
1734
+ return ret
1735
+ return json.dumps(ret[0]), json.dumps(ret[1])
1736
+
1737
+ # change task priority
1738
+ def changeTaskPriorityPanda(self, jediTaskID, newPriority):
1739
+ # get proxy
1740
+ with self.proxyPool.get() as proxy:
1741
+ # exec
1742
+ ret = proxy.changeTaskPriorityPanda(jediTaskID, newPriority)
1743
+ return ret
1744
+
1745
+ # throttle user jobs
1746
+ def throttleUserJobs(self, prodUserName, workingGroup, get_dict=False):
1747
+ # get proxy
1748
+ with self.proxyPool.get() as proxy:
1749
+ # exec
1750
+ ret = proxy.throttleUserJobs(prodUserName, workingGroup, get_dict)
1751
+ return ret
1752
+
1753
+ # unthrottle user jobs
1754
+ def unThrottleUserJobs(self, prodUserName, workingGroup, get_dict=False):
1755
+ # get proxy
1756
+ with self.proxyPool.get() as proxy:
1757
+ # exec
1758
+ ret = proxy.unThrottleUserJobs(prodUserName, workingGroup, get_dict)
1759
+ return ret
1760
+
1761
+ # get throttled users
1762
+ def getThrottledUsers(self):
1763
+ # get proxy
1764
+ with self.proxyPool.get() as proxy:
1765
+ # exec
1766
+ ret = proxy.getThrottledUsers()
1767
+ return ret
1768
+
1769
+ # get the list of jobdefIDs for failed jobs in a task
1770
+ def getJobdefIDsForFailedJob(self, jediTaskID):
1771
+ # get proxy
1772
+ with self.proxyPool.get() as proxy:
1773
+ # exec
1774
+ ret = proxy.getJobdefIDsForFailedJob(jediTaskID)
1775
+ return ret
1776
+
1777
+ # change task attribute
1778
+ def changeTaskAttributePanda(self, jediTaskID, attrName, attrValue):
1779
+ # get proxy
1780
+ with self.proxyPool.get() as proxy:
1781
+ # exec
1782
+ ret = proxy.changeTaskAttributePanda(jediTaskID, attrName, attrValue)
1783
+ return ret
1784
+
1785
+ # change split rule for task
1786
+ def changeTaskSplitRulePanda(self, jediTaskID, attrName, attrValue):
1787
+ # get proxy
1788
+ with self.proxyPool.get() as proxy:
1789
+ # exec
1790
+ ret = proxy.changeTaskSplitRulePanda(jediTaskID, attrName, attrValue)
1791
+ return ret
1792
+
1793
+ # increase attempt number for unprocessed files
1794
+ def increaseAttemptNrPanda(self, jediTaskID, increasedNr):
1795
+ # get proxy
1796
+ with self.proxyPool.get() as proxy:
1797
+ # exec
1798
+ ret = proxy.increaseAttemptNrPanda(jediTaskID, increasedNr)
1799
+ return ret
1800
+
1801
+ # get jediTaskID from taskName
1802
+ def getTaskIDwithTaskNameJEDI(self, userName, taskName):
1803
+ # get proxy
1804
+ with self.proxyPool.get() as proxy:
1805
+ # exec
1806
+ ret = proxy.getTaskIDwithTaskNameJEDI(userName, taskName)
1807
+ return ret
1808
+
1809
+ # update error dialog for a jediTaskID
1810
+ def updateTaskErrorDialogJEDI(self, jediTaskID, msg):
1811
+ # get proxy
1812
+ with self.proxyPool.get() as proxy:
1813
+ # exec
1814
+ ret = proxy.updateTaskErrorDialogJEDI(jediTaskID, msg)
1815
+ return ret
1816
+
1817
+ # update modificationtime for a jediTaskID to trigger subsequent process
1818
+ def updateTaskModTimeJEDI(self, jediTaskID, newStatus=None):
1819
+ # get proxy
1820
+ with self.proxyPool.get() as proxy:
1821
+ # exec
1822
+ ret = proxy.updateTaskModTimeJEDI(jediTaskID, newStatus)
1823
+ return ret
1824
+
1825
+ # check input file status
1826
+ def checkInputFileStatusInJEDI(self, jobSpec):
1827
+ # get proxy
1828
+ with self.proxyPool.get() as proxy:
1829
+ # exec
1830
+ ret = proxy.checkInputFileStatusInJEDI(jobSpec)
1831
+ return ret
1832
+
1833
+ # increase memory limit
1834
+ def increaseRamLimitJEDI(self, jediTaskID, jobRamCount):
1835
+ # get proxy
1836
+ with self.proxyPool.get() as proxy:
1837
+ # exec
1838
+ ret = proxy.increaseRamLimitJEDI(jediTaskID, jobRamCount)
1839
+ return ret
1840
+
1841
+ # increase memory limit
1842
+ def increaseRamLimitJobJEDI(self, job, jobRamCount, jediTaskID):
1843
+ # get proxy
1844
+ with self.proxyPool.get() as proxy:
1845
+ # exec
1846
+ ret = proxy.increaseRamLimitJobJEDI(job, jobRamCount, jediTaskID)
1847
+ return ret
1848
+
1849
+ # increase memory limit xtimes
1850
+ def increaseRamLimitJobJEDI_xtimes(self, job, jobRamCount, jediTaskID, attemptNr):
1851
+ # get proxy
1852
+ with self.proxyPool.get() as proxy:
1853
+ # exec
1854
+ ret = proxy.increaseRamLimitJobJEDI_xtimes(job, jobRamCount, jediTaskID, attemptNr)
1855
+ return ret
1856
+
1857
+ # reduce input per job
1858
+ def reduce_input_per_job(self, panda_id, jedi_task_id, attempt_nr, excluded_rules, steps, dry_mode=False):
1859
+ # get proxy
1860
+ with self.proxyPool.get() as proxy:
1861
+ # exec
1862
+ ret = proxy.reduce_input_per_job(panda_id, jedi_task_id, attempt_nr, excluded_rules, steps, dry_mode)
1863
+ return ret
1864
+
1865
+ # reset files in JEDI
1866
+ def resetFileStatusInJEDI(self, dn, prodManager, datasetName, lostFiles, recoverParent, simul=False):
1867
+ # get proxy
1868
+ with self.proxyPool.get() as proxy:
1869
+ # exec
1870
+ ret = proxy.resetFileStatusInJEDI(dn, prodManager, datasetName, lostFiles, recoverParent, simul)
1871
+ return ret
1872
+
1873
+ # copy file records
1874
+ def copy_file_records(self, new_lfns, file_spec):
1875
+ # get proxy
1876
+ with self.proxyPool.get() as proxy:
1877
+ # exec
1878
+ ret = proxy.copy_file_records(new_lfns, file_spec)
1879
+ return ret
1880
+
1881
+ # retry module: get the defined rules
1882
+ def getRetrialRules(self):
1883
+ # get proxy
1884
+ with self.proxyPool.get() as proxy:
1885
+ # exec
1886
+ ret = proxy.getRetrialRules()
1887
+ return ret
1888
+
1889
+ # retry module action: set max number of retries
1890
+ def setMaxAttempt(self, jobID, jediTaskID, files, attemptNr):
1891
+ # get proxy
1892
+ with self.proxyPool.get() as proxy:
1893
+ # exec
1894
+ ret = proxy.setMaxAttempt(jobID, jediTaskID, files, attemptNr)
1895
+ return ret
1896
+
1897
+ # error classification action: increase by one the max number of retries
1898
+ def increase_max_failure(self, job_id, task_id, files):
1899
+ # get proxy
1900
+ with self.proxyPool.get() as proxy:
1901
+ # exec
1902
+ ret = proxy.increase_max_failure(job_id, task_id, files)
1903
+ return ret
1904
+
1905
+ # retry module action: set maxAttempt to the current attemptNr to avoid further retries
1906
+ def setNoRetry(self, jobID, jediTaskID, files):
1907
+ # get proxy
1908
+ with self.proxyPool.get() as proxy:
1909
+ # exec
1910
+ ret = proxy.setNoRetry(jobID, jediTaskID, files)
1911
+ return ret
1912
+
1913
+ # retry module action: increase CPU Time
1914
+ def initialize_cpu_time_task(self, jobID, taskID, siteid, files, active):
1915
+ # get proxy
1916
+ with self.proxyPool.get() as proxy:
1917
+ # exec
1918
+ ret = proxy.initialize_cpu_time_task(jobID, taskID, siteid, files, active)
1919
+ return ret
1920
+
1921
+ # retry module action: recalculate the Task Parameters
1922
+ def requestTaskParameterRecalculation(self, taskID):
1923
+ # get proxy
1924
+ with self.proxyPool.get() as proxy:
1925
+ # exec
1926
+ ret = proxy.requestTaskParameterRecalculation(taskID)
1927
+ return ret
1928
+
1929
+ # add associate sub datasets for single consumer job
1930
+ def getDestDBlocksWithSingleConsumer(self, jediTaskID, PandaID, ngDatasets):
1931
+ # get proxy
1932
+ with self.proxyPool.get() as proxy:
1933
+ # exec
1934
+ ret = proxy.getDestDBlocksWithSingleConsumer(jediTaskID, PandaID, ngDatasets)
1935
+ return ret
1936
+
1937
+ # check validity of merge job
1938
+ def isValidMergeJob(self, pandaID, jediTaskID):
1939
+ # get proxy
1940
+ with self.proxyPool.get() as proxy:
1941
+ # exec
1942
+ ret = proxy.isValidMergeJob(pandaID, jediTaskID)
1943
+ return ret
1944
+
1945
+ # Configurator: insert network matrix data
1946
+ def insertNetworkMatrixData(self, data):
1947
+ # get proxy
1948
+ with self.proxyPool.get() as proxy:
1949
+ # exec
1950
+ ret = proxy.insertNetworkMatrixData(data)
1951
+ return ret
1952
+
1953
+ # Configurator: delete old network matrix data
1954
+ def deleteOldNetworkData(self):
1955
+ # get proxy
1956
+ with self.proxyPool.get() as proxy:
1957
+ # exec
1958
+ ret = proxy.deleteOldNetworkData()
1959
+ return ret
1960
+
1961
+ # get dispatch datasets per user
1962
+ def getDispatchDatasetsPerUser(self, vo, prodSourceLabel, onlyActive, withSize):
1963
+ # get proxy
1964
+ with self.proxyPool.get() as proxy:
1965
+ # exec
1966
+ ret = proxy.getDispatchDatasetsPerUser(vo, prodSourceLabel, onlyActive, withSize)
1967
+ return ret
1968
+
1969
+ # get task parameters
1970
+ def getTaskParamsPanda(self, jediTaskID):
1971
+ # get proxy
1972
+ with self.proxyPool.get() as proxy:
1973
+ # exec
1974
+ ret = proxy.getTaskParamsPanda(jediTaskID)
1975
+ return ret
1976
+
1977
+ # get task attributes
1978
+ def getTaskAttributesPanda(self, jediTaskID, attrs):
1979
+ # get proxy
1980
+ with self.proxyPool.get() as proxy:
1981
+ # exec
1982
+ ret = proxy.getTaskAttributesPanda(jediTaskID, attrs)
1983
+ return ret
1984
+
1985
+ # check for cloned jobs
1986
+ def checkClonedJob(self, jobSpec):
1987
+ # get proxy
1988
+ with self.proxyPool.get() as proxy:
1989
+ # exec
1990
+ ret = proxy.checkClonedJob(jobSpec)
1991
+ return ret
1992
+
1993
+ # get co-jumbo jobs to be finished
1994
+ def getCoJumboJobsToBeFinished(self, timeLimit, minPriority, maxJobs):
1995
+ # get proxy
1996
+ with self.proxyPool.get() as proxy:
1997
+ # exec
1998
+ ret = proxy.getCoJumboJobsToBeFinished(timeLimit, minPriority, maxJobs)
1999
+ return ret
2000
+
2001
+ # get number of events to be processed
2002
+ def getNumReadyEvents(self, jediTaskID):
2003
+ # get proxy
2004
+ with self.proxyPool.get() as proxy:
2005
+ # exec
2006
+ ret = proxy.getNumReadyEvents(jediTaskID)
2007
+ return ret
2008
+
2009
+ # check if task is applicable for jumbo jobs
2010
+ def isApplicableTaskForJumbo(self, jediTaskID):
2011
+ # get proxy
2012
+ with self.proxyPool.get() as proxy:
2013
+ # exec
2014
+ ret = proxy.isApplicableTaskForJumbo(jediTaskID)
2015
+ return ret
2016
+
2017
+ # cleanup jumbo jobs
2018
+ def cleanupJumboJobs(self, jediTaskID=None):
2019
+ # get proxy
2020
+ with self.proxyPool.get() as proxy:
2021
+ # exec
2022
+ ret = proxy.cleanupJumboJobs(jediTaskID)
2023
+ return ret
2024
+
2025
+ # convert ObjID to endpoint
2026
+ def convertObjIDtoEndPoint(self, srcFileName, ObjID):
2027
+ # get DB proxy
2028
+ with self.proxyPool.get() as proxy:
2029
+ # exec
2030
+ res = proxy.convertObjIDtoEndPoint(srcFileName, ObjID)
2031
+ return res
2032
+
2033
+ # get task status
2034
+ def getTaskStatus(self, jediTaskID):
2035
+ # get DB proxy
2036
+ with self.proxyPool.get() as proxy:
2037
+ # exec
2038
+ res = proxy.getTaskStatus(jediTaskID)
2039
+ return res
2040
+
2041
+ # get task status and superstatus
2042
+ def getTaskStatusSuperstatus(self, jediTaskID):
2043
+ # get DB proxy
2044
+ with self.proxyPool.get() as proxy:
2045
+ # exec
2046
+ res = proxy.getTaskStatusSuperstatus(jediTaskID)
2047
+ return res
2048
+
2049
+ # reactivate task
2050
+ def reactivateTask(self, jediTaskID, keep_attempt_nr=False, trigger_job_generation=False):
2051
+ # get DB proxy
2052
+ with self.proxyPool.get() as proxy:
2053
+ # exec
2054
+ res = proxy.reactivateTask(jediTaskID, keep_attempt_nr, trigger_job_generation)
2055
+ return res
2056
+
2057
+ # get event statistics
2058
+ def getEventStat(self, jediTaskID, PandaID):
2059
+ # get DB proxy
2060
+ with self.proxyPool.get() as proxy:
2061
+ # exec
2062
+ res = proxy.getEventStat(jediTaskID, PandaID)
2063
+ return res
2064
+
2065
+ # get nested dict of gshare names implying the tree structure
2066
+ def get_tree_of_gshare_names(self):
2067
+ # get DB proxy
2068
+ with self.proxyPool.get() as proxy:
2069
+ # exec
2070
+ res = proxy.get_tree_of_gshare_names()
2071
+ return res
2072
+
2073
+ # get the HS06 distribution for global shares
2074
+ def get_hs_distribution(self):
2075
+ # get DB proxy
2076
+ with self.proxyPool.get() as proxy:
2077
+ # exec
2078
+ res = proxy.get_hs_distribution()
2079
+ return res
2080
+
2081
+ # reassign share
2082
+ def reassignShare(self, jedi_task_ids, share_dest, reassign_running):
2083
+ # get DB proxy
2084
+ with self.proxyPool.get() as proxy:
2085
+ # exec
2086
+ res = proxy.reassignShare(jedi_task_ids, share_dest, reassign_running)
2087
+ return res
2088
+
2089
+ def is_valid_share(self, share_name):
2090
+ """
2091
+ Checks whether the share is a valid leave share
2092
+ """
2093
+ # get DB proxy
2094
+ with self.proxyPool.get() as proxy:
2095
+ # exec
2096
+ res = proxy.is_valid_share(share_name)
2097
+ return res
2098
+
2099
+ def get_share_for_task(self, task):
2100
+ """
2101
+ Return the share based on a task specification
2102
+ """
2103
+ # get DB proxy
2104
+ with self.proxyPool.get() as proxy:
2105
+ # exec
2106
+ res = proxy.get_share_for_task(task)
2107
+ return res
2108
+
2109
+ def get_share_for_job(self, job):
2110
+ """
2111
+ Return the share based on a task specification
2112
+ """
2113
+ # get DB proxy
2114
+ with self.proxyPool.get() as proxy:
2115
+ # exec
2116
+ res = proxy.get_share_for_job(job)
2117
+ return res
2118
+
2119
+ def getTaskParamsMap(self, jediTaskID):
2120
+ """
2121
+ Return the taskParamsMap
2122
+ """
2123
+ # get DB proxy
2124
+ with self.proxyPool.get() as proxy:
2125
+ # exec
2126
+ res = proxy.getTaskParamsPanda(jediTaskID)
2127
+ return res
2128
+
2129
+ def getCommands(self, harvester_id, n_commands):
2130
+ """
2131
+ Get n commands for a particular harvester instance
2132
+ """
2133
+ # get DB proxy
2134
+ with self.proxyPool.get() as proxy:
2135
+ # exec
2136
+ res = proxy.getCommands(harvester_id, n_commands)
2137
+ return res
2138
+
2139
+ def ackCommands(self, command_ids):
2140
+ """
2141
+ Acknowledge a list of command IDs
2142
+ """
2143
+ # get DB proxy
2144
+ with self.proxyPool.get() as proxy:
2145
+ # exec
2146
+ res = proxy.ackCommands(command_ids)
2147
+ return res
2148
+
2149
+ # send command to harvester or lock command
2150
+ def commandToHarvester(
2151
+ self,
2152
+ harvester_ID,
2153
+ command,
2154
+ ack_requested,
2155
+ status,
2156
+ lockInterval=None,
2157
+ comInterval=None,
2158
+ params=None,
2159
+ ):
2160
+ # get DB proxy
2161
+ with self.proxyPool.get() as proxy:
2162
+ # exec
2163
+ res = proxy.commandToHarvester(
2164
+ harvester_ID,
2165
+ command,
2166
+ ack_requested,
2167
+ status,
2168
+ lockInterval,
2169
+ comInterval,
2170
+ params,
2171
+ )
2172
+ return res
2173
+
2174
+ def getResourceTypes(self):
2175
+ """
2176
+ Get resource types (SCORE, MCORE, ...) and their definitions
2177
+ """
2178
+ # get DB proxy
2179
+ with self.proxyPool.get() as proxy:
2180
+ # exec
2181
+ res = proxy.load_resource_types(formatting="dict")
2182
+ return res
2183
+
2184
+ # report stat of workers
2185
+ def reportWorkerStats_jobtype(self, harvesterID, siteName, paramsList):
2186
+ # get DB proxy
2187
+ with self.proxyPool.get() as proxy:
2188
+ # exec
2189
+ res = proxy.reportWorkerStats_jobtype(harvesterID, siteName, paramsList)
2190
+ return res
2191
+
2192
+ # get command locks
2193
+ def getCommandLocksHarvester(self, harvester_ID, command, lockedBy, lockInterval, commandInterval):
2194
+ # get DB proxy
2195
+ with self.proxyPool.get() as proxy:
2196
+ # exec
2197
+ res = proxy.getCommandLocksHarvester(harvester_ID, command, lockedBy, lockInterval, commandInterval)
2198
+ return res
2199
+
2200
+ # release command lock
2201
+ def releaseCommandLockHarvester(self, harvester_ID, command, computingSite, resourceType, lockedBy):
2202
+ # get DB proxy
2203
+ with self.proxyPool.get() as proxy:
2204
+ # exec
2205
+ res = proxy.releaseCommandLockHarvester(harvester_ID, command, computingSite, resourceType, lockedBy)
2206
+ return res
2207
+
2208
+ # update workers
2209
+ def updateWorkers(self, harvesterID, data):
2210
+ """
2211
+ Update workers
2212
+ """
2213
+ # get DB proxy
2214
+ with self.proxyPool.get() as proxy:
2215
+ # exec
2216
+ res = proxy.updateWorkers(harvesterID, data)
2217
+ return res
2218
+
2219
+ # update workers
2220
+ def updateServiceMetrics(self, harvesterID, data):
2221
+ """
2222
+ Update workers
2223
+ """
2224
+ # get DB proxy
2225
+ with self.proxyPool.get() as proxy:
2226
+ # exec
2227
+ res = proxy.updateServiceMetrics(harvesterID, data)
2228
+ return res
2229
+
2230
+ # heartbeat for harvester
2231
+ def harvesterIsAlive(self, user, host, harvesterID, data):
2232
+ """
2233
+ update harvester instance information
2234
+ """
2235
+ # get DB proxy
2236
+ with self.proxyPool.get() as proxy:
2237
+ # exec
2238
+ res = proxy.harvesterIsAlive(user, host, harvesterID, data)
2239
+ return res
2240
+
2241
+ def storePilotLog(self, panda_id, pilot_log):
2242
+ """
2243
+ Store the pilot log in the pandalog table
2244
+ """
2245
+ # get DB proxy
2246
+ with self.proxyPool.get() as proxy:
2247
+ # exec
2248
+ res = proxy.storePilotLog(panda_id, pilot_log)
2249
+ return res
2250
+
2251
+ # read the resource types from the DB
2252
+ def load_resource_types(self):
2253
+ # get DBproxy
2254
+ with self.proxyPool.get() as proxy:
2255
+ # exec
2256
+ ret_val = proxy.load_resource_types()
2257
+ return ret_val
2258
+
2259
+ # get the resource of a task
2260
+ def get_resource_type_task(self, task_spec):
2261
+ # get DBproxy
2262
+ with self.proxyPool.get() as proxy:
2263
+ # exec
2264
+ ret_val = proxy.get_resource_type_task(task_spec)
2265
+ return ret_val
2266
+
2267
+ def reset_resource_type_task(self, jedi_task_id, use_commit=True):
2268
+ # get DBproxy
2269
+ with self.proxyPool.get() as proxy:
2270
+ # exec
2271
+ ret_val = proxy.reset_resource_type_task(jedi_task_id, use_commit)
2272
+ return ret_val
2273
+
2274
+ # get the resource of a task
2275
+ def get_resource_type_job(self, job_spec):
2276
+ # get DBproxy
2277
+ with self.proxyPool.get() as proxy:
2278
+ # exec
2279
+ ret_val = proxy.get_resource_type_job(job_spec)
2280
+ return ret_val
2281
+
2282
+ # check Job status
2283
+ def checkJobStatus(self, pandaIDs):
2284
+ try:
2285
+ pandaIDs = pandaIDs.split(",")
2286
+ except Exception:
2287
+ pandaIDs = []
2288
+ # get DBproxy
2289
+ with self.proxyPool.get() as proxy:
2290
+ # exec
2291
+ retList = []
2292
+ for pandaID in pandaIDs:
2293
+ ret = proxy.checkJobStatus(pandaID)
2294
+ retList.append(ret)
2295
+ return retList
2296
+
2297
+ # get stat of workers
2298
+ def getWorkerStats(self):
2299
+ # get DBproxy
2300
+ with self.proxyPool.get() as proxy:
2301
+ # exec
2302
+ ret = proxy.getWorkerStats()
2303
+ return ret
2304
+
2305
+ # get unified pilot streaming queues
2306
+ def ups_get_queues(self):
2307
+ # get DBproxy
2308
+ with self.proxyPool.get() as proxy:
2309
+ # exec
2310
+ ret = proxy.ups_get_queues()
2311
+ return ret
2312
+
2313
+ # load harvester worker stats
2314
+ def ups_load_worker_stats(self):
2315
+ # get DBproxy
2316
+ with self.proxyPool.get() as proxy:
2317
+ # exec
2318
+ ret = proxy.ups_load_worker_stats()
2319
+ return ret
2320
+
2321
+ # get the distribution of new workers to submit
2322
+ def get_average_memory_workers(self, queue, harvester_id, target):
2323
+ # get DBproxy
2324
+ with self.proxyPool.get() as proxy:
2325
+ # exec
2326
+ ret = proxy.get_average_memory_workers(queue, harvester_id, target)
2327
+ return ret
2328
+
2329
+ # get the distribution of new workers to submit
2330
+ def ups_new_worker_distribution(self, queue, worker_stats):
2331
+ # get DBproxy
2332
+ with self.proxyPool.get() as proxy:
2333
+ # exec
2334
+ ret = proxy.ups_new_worker_distribution(queue, worker_stats)
2335
+ return ret
2336
+
2337
+ # check event availability
2338
+ def checkEventsAvailability(self, pandaID, jobsetID, jediTaskID):
2339
+ # get DBproxy
2340
+ with self.proxyPool.get() as proxy:
2341
+ # exec
2342
+ ret = proxy.checkEventsAvailability(pandaID, jobsetID, jediTaskID)
2343
+ return ret
2344
+
2345
+ # get LNFs for jumbo job
2346
+ def getLFNsForJumbo(self, jediTaskID):
2347
+ # get DBproxy
2348
+ with self.proxyPool.get() as proxy:
2349
+ # exec
2350
+ ret = proxy.getLFNsForJumbo(jediTaskID)
2351
+ return ret
2352
+
2353
+ # get active job attribute
2354
+ def getActiveJobAttributes(self, pandaID, attrs):
2355
+ # get DBproxy
2356
+ with self.proxyPool.get() as proxy:
2357
+ # exec
2358
+ ret = proxy.getActiveJobAttributes(pandaID, attrs)
2359
+ return ret
2360
+
2361
+ # get original consumers
2362
+ def getOriginalConsumers(self, jediTaskID, jobsetID, pandaID):
2363
+ # get DBproxy
2364
+ with self.proxyPool.get() as proxy:
2365
+ # exec
2366
+ ret = proxy.getOriginalConsumers(jediTaskID, jobsetID, pandaID)
2367
+ return ret
2368
+
2369
+ # add harvester dialog messages
2370
+ def addHarvesterDialogs(self, harvesterID, dialogs):
2371
+ # get DBproxy
2372
+ with self.proxyPool.get() as proxy:
2373
+ # exec
2374
+ ret = proxy.addHarvesterDialogs(harvesterID, dialogs)
2375
+ return ret
2376
+
2377
+ # get job statistics per site and resource
2378
+ def getJobStatisticsPerSiteResource(self, timeWindow=None):
2379
+ # get DBproxy
2380
+ with self.proxyPool.get() as proxy:
2381
+ # exec
2382
+ ret = proxy.getJobStatisticsPerSiteResource(timeWindow)
2383
+ return ret
2384
+
2385
+ # get job statistics per site, source label, and resource type
2386
+ def get_job_statistics_per_site_label_resource(self, time_window=None):
2387
+ # get DBproxy
2388
+ with self.proxyPool.get() as proxy:
2389
+ # exec
2390
+ ret = proxy.get_job_statistics_per_site_label_resource(time_window)
2391
+ return ret
2392
+
2393
+ # set num slots for workload provisioning
2394
+ def setNumSlotsForWP(self, pandaQueueName, numSlots, gshare, resourceType, validPeriod):
2395
+ # get DBproxy
2396
+ with self.proxyPool.get() as proxy:
2397
+ # exec
2398
+ ret = proxy.setNumSlotsForWP(pandaQueueName, numSlots, gshare, resourceType, validPeriod)
2399
+ return ret
2400
+
2401
+ # enable jumbo jobs
2402
+ def enableJumboJobs(self, jediTaskID, totalJumboJobs, nJumboPerSite):
2403
+ # get DBproxy
2404
+ with self.proxyPool.get() as proxy:
2405
+ # exec
2406
+ ret = proxy.enableJumboJobs(jediTaskID, totalJumboJobs, nJumboPerSite)
2407
+ return ret
2408
+
2409
+ # enable event service
2410
+ def enableEventService(self, jediTaskID):
2411
+ # get DBproxy
2412
+ with self.proxyPool.get() as proxy:
2413
+ # exec
2414
+ ret = proxy.enableEventService(jediTaskID)
2415
+ return ret
2416
+
2417
+ # get JEDI file attributes
2418
+ def getJediFileAttributes(self, PandaID, jediTaskID, datasetID, fileID, attrs):
2419
+ # get DBproxy
2420
+ with self.proxyPool.get() as proxy:
2421
+ # exec
2422
+ ret = proxy.getJediFileAttributes(PandaID, jediTaskID, datasetID, fileID, attrs)
2423
+ return ret
2424
+
2425
+ # check if super user
2426
+ def isSuperUser(self, userName):
2427
+ # get DBproxy
2428
+ with self.proxyPool.get() as proxy:
2429
+ # exec
2430
+ ret = proxy.isSuperUser(userName)
2431
+ return ret
2432
+
2433
+ # get workers for a job
2434
+ def getWorkersForJob(self, PandaID):
2435
+ # get DBproxy
2436
+ with self.proxyPool.get() as proxy:
2437
+ # exec
2438
+ ret = proxy.getWorkersForJob(PandaID)
2439
+ return ret
2440
+
2441
+ # get user job metadata
2442
+ def getUserJobMetadata(self, jediTaskID):
2443
+ # get DBproxy
2444
+ with self.proxyPool.get() as proxy:
2445
+ # exec
2446
+ ret = proxy.getUserJobMetadata(jediTaskID)
2447
+ return ret
2448
+
2449
+ # get jumbo job datasets
2450
+ def getJumboJobDatasets(self, n_days, grace_period):
2451
+ # get DBproxy
2452
+ with self.proxyPool.get() as proxy:
2453
+ # exec
2454
+ ret = proxy.getJumboJobDatasets(n_days, grace_period)
2455
+ return ret
2456
+
2457
+ # get global shares status
2458
+ def getGShareStatus(self):
2459
+ # get DBproxy
2460
+ with self.proxyPool.get() as proxy:
2461
+ # exec
2462
+ ret = proxy.getGShareStatus()
2463
+ return ret
2464
+
2465
+ # get output datasets
2466
+ def getOutputDatasetsJEDI(self, panda_id):
2467
+ # get DBproxy
2468
+ with self.proxyPool.get() as proxy:
2469
+ # exec
2470
+ ret = proxy.getOutputDatasetsJEDI(panda_id)
2471
+ return ret
2472
+
2473
+ # update/insert JSON queue information into the scheconfig replica
2474
+ def upsertQueuesInJSONSchedconfig(self, schedconfig_dump):
2475
+ # get DBproxy
2476
+ with self.proxyPool.get() as proxy:
2477
+ # exec
2478
+ ret = proxy.upsertQueuesInJSONSchedconfig(schedconfig_dump)
2479
+ return ret
2480
+
2481
+ # update/insert SW tag information
2482
+ def loadSWTags(self, sw_tags):
2483
+ # get DBproxy
2484
+ with self.proxyPool.get() as proxy:
2485
+ # exec
2486
+ ret = proxy.loadSWTags(sw_tags)
2487
+ return ret
2488
+
2489
+ # generate a harvester command to clean up the workers of a site
2490
+ def sweepPQ(self, panda_queue_des, status_list_des, ce_list_des, submission_host_list_des):
2491
+ # get DBproxy
2492
+ with self.proxyPool.get() as proxy:
2493
+ # exec
2494
+ ret = proxy.sweepPQ(panda_queue_des, status_list_des, ce_list_des, submission_host_list_des)
2495
+ return ret
2496
+
2497
+ # lock process
2498
+ def lockProcess_PANDA(self, component, pid, time_limit=5, force=False):
2499
+ with self.proxyPool.get() as proxy:
2500
+ ret = proxy.lockProcess_PANDA(component, pid, time_limit, force)
2501
+ return ret
2502
+
2503
+ # unlock process
2504
+ def unlockProcess_PANDA(self, component, pid):
2505
+ with self.proxyPool.get() as proxy:
2506
+ ret = proxy.unlockProcess_PANDA(component, pid)
2507
+ return ret
2508
+
2509
+ # check process lock
2510
+ def checkProcessLock_PANDA(self, component, pid, time_limit, check_base=False):
2511
+ with self.proxyPool.get() as proxy:
2512
+ ret = proxy.checkProcessLock_PANDA(component, pid, time_limit, check_base)
2513
+ return ret
2514
+
2515
+ # insert job output report
2516
+ def insertJobOutputReport(self, panda_id, prod_source_label, job_status, attempt_nr, data):
2517
+ with self.proxyPool.get() as proxy:
2518
+ ret = proxy.insertJobOutputReport(panda_id, prod_source_label, job_status, attempt_nr, data)
2519
+ return ret
2520
+
2521
+ # deleted job output report
2522
+ def deleteJobOutputReport(self, panda_id, attempt_nr):
2523
+ with self.proxyPool.get() as proxy:
2524
+ ret = proxy.deleteJobOutputReport(panda_id, attempt_nr)
2525
+ return ret
2526
+
2527
+ # update data of job output report
2528
+ def updateJobOutputReport(self, panda_id, attempt_nr, data):
2529
+ with self.proxyPool.get() as proxy:
2530
+ ret = proxy.updateJobOutputReport(panda_id, attempt_nr, data)
2531
+ return ret
2532
+
2533
+ # get job output report
2534
+ def getJobOutputReport(self, panda_id, attempt_nr):
2535
+ with self.proxyPool.get() as proxy:
2536
+ ret = proxy.getJobOutputReport(panda_id, attempt_nr)
2537
+ return ret
2538
+
2539
+ # lock job output report
2540
+ def lockJobOutputReport(self, panda_id, attempt_nr, pid, time_limit, take_over_from=None):
2541
+ with self.proxyPool.get() as proxy:
2542
+ ret = proxy.lockJobOutputReport(panda_id, attempt_nr, pid, time_limit, take_over_from)
2543
+ return ret
2544
+
2545
+ # unlock job output report
2546
+ def unlockJobOutputReport(self, panda_id, attempt_nr, pid, lock_offset):
2547
+ with self.proxyPool.get() as proxy:
2548
+ ret = proxy.unlockJobOutputReport(panda_id, attempt_nr, pid, lock_offset)
2549
+ return ret
2550
+
2551
+ # list pandaID and attemptNr of job output report
2552
+ def listJobOutputReport(
2553
+ self,
2554
+ only_unlocked=False,
2555
+ time_limit=5,
2556
+ limit=999999,
2557
+ grace_period=3,
2558
+ labels=None,
2559
+ anti_labels=None,
2560
+ ):
2561
+ with self.proxyPool.get() as proxy:
2562
+ ret = proxy.listJobOutputReport(only_unlocked, time_limit, limit, grace_period, labels, anti_labels)
2563
+ return ret
2564
+
2565
+ # update problematic resource info for user
2566
+ def update_problematic_resource_info(self, user_name, jedi_task_id, resource, problem_type):
2567
+ with self.proxyPool.get() as proxy:
2568
+ ret = proxy.update_problematic_resource_info(user_name, jedi_task_id, resource, problem_type)
2569
+ return ret
2570
+
2571
+ # send command to a job
2572
+ def send_command_to_job(self, panda_id, com):
2573
+ with self.proxyPool.get() as proxy:
2574
+ ret = proxy.send_command_to_job(panda_id, com)
2575
+ return ret
2576
+
2577
+ # get workers with stale states and update them with pilot information
2578
+ def get_workers_to_synchronize(self):
2579
+ with self.proxyPool.get() as proxy:
2580
+ ret = proxy.get_workers_to_synchronize()
2581
+ return ret
2582
+
2583
+ # set user secret
2584
+ def set_user_secret(self, owner, key, value):
2585
+ with self.proxyPool.get() as proxy:
2586
+ ret = proxy.set_user_secret(owner, key, value)
2587
+ return ret
2588
+
2589
+ # get user secrets
2590
+ def get_user_secrets(self, owner, keys=None, get_json=False):
2591
+ with self.proxyPool.get() as proxy:
2592
+ ret = proxy.get_user_secrets(owner, keys, get_json)
2593
+ return ret
2594
+
2595
+ def configurator_write_sites(self, sites_list):
2596
+ with self.proxyPool.get() as proxy:
2597
+ ret = proxy.configurator_write_sites(sites_list)
2598
+ return ret
2599
+
2600
+ def configurator_write_panda_sites(self, panda_site_list):
2601
+ with self.proxyPool.get() as proxy:
2602
+ ret = proxy.configurator_write_panda_sites(panda_site_list)
2603
+ return ret
2604
+
2605
+ def configurator_write_ddm_endpoints(self, ddm_endpoint_list):
2606
+ with self.proxyPool.get() as proxy:
2607
+ ret = proxy.configurator_write_ddm_endpoints(ddm_endpoint_list)
2608
+ return ret
2609
+
2610
+ def configurator_write_panda_ddm_relations(self, relation_list):
2611
+ with self.proxyPool.get() as proxy:
2612
+ ret = proxy.configurator_write_panda_ddm_relations(relation_list)
2613
+ return ret
2614
+
2615
+ def configurator_read_sites(self):
2616
+ with self.proxyPool.get() as proxy:
2617
+ ret = proxy.configurator_read_sites()
2618
+ return ret
2619
+
2620
+ def configurator_read_panda_sites(self):
2621
+ with self.proxyPool.get() as proxy:
2622
+ ret = proxy.configurator_read_panda_sites()
2623
+ return ret
2624
+
2625
+ def configurator_read_ddm_endpoints(self):
2626
+ with self.proxyPool.get() as proxy:
2627
+ ret = proxy.configurator_read_ddm_endpoints()
2628
+ return ret
2629
+
2630
+ def configurator_read_cric_sites(self):
2631
+ with self.proxyPool.get() as proxy:
2632
+ ret = proxy.configurator_read_cric_sites()
2633
+ return ret
2634
+
2635
+ def configurator_read_cric_panda_sites(self):
2636
+ with self.proxyPool.get() as proxy:
2637
+ ret = proxy.configurator_read_cric_panda_sites()
2638
+ return ret
2639
+
2640
+ def configurator_delete_sites(self, sites_to_delete):
2641
+ with self.proxyPool.get() as proxy:
2642
+ ret = proxy.configurator_delete_sites(sites_to_delete)
2643
+ return ret
2644
+
2645
+ def configurator_delete_panda_sites(self, panda_sites_to_delete):
2646
+ with self.proxyPool.get() as proxy:
2647
+ ret = proxy.configurator_delete_panda_sites(panda_sites_to_delete)
2648
+ return ret
2649
+
2650
+ def configurator_delete_ddm_endpoints(self, ddm_endpoints_to_delete):
2651
+ with self.proxyPool.get() as proxy:
2652
+ ret = proxy.configurator_delete_ddm_endpoints(ddm_endpoints_to_delete)
2653
+ return ret
2654
+
2655
+ def carbon_write_region_emissions(self, emissions):
2656
+ with self.proxyPool.get() as proxy:
2657
+ ret = proxy.carbon_write_region_emissions(emissions)
2658
+ return ret
2659
+
2660
+ def carbon_aggregate_emissions(self):
2661
+ with self.proxyPool.get() as proxy:
2662
+ ret = proxy.carbon_aggregate_emissions()
2663
+ return ret
2664
+
2665
+ def get_files_in_datasets(self, task_id, dataset_types):
2666
+ with self.proxyPool.get() as proxy:
2667
+ ret = proxy.get_files_in_datasets(task_id, dataset_types)
2668
+ return ret
2669
+
2670
+ def get_max_worker_id(self, harvester_id):
2671
+ with self.proxyPool.get() as proxy:
2672
+ ret = proxy.get_max_worker_id(harvester_id)
2673
+ return ret
2674
+
2675
+ def get_events_status(self, ids):
2676
+ with self.proxyPool.get() as proxy:
2677
+ ret = proxy.get_events_status(ids)
2678
+ return ret
2679
+
2680
+ def async_update_datasets(self, panda_id):
2681
+ with self.proxyPool.get() as proxy:
2682
+ ret = proxy.async_update_datasets(panda_id)
2683
+ return ret
2684
+
2685
+ def set_workload_metrics(self, jedi_task_id, panda_id, metrics):
2686
+ with self.proxyPool.get() as proxy:
2687
+ ret = proxy.set_workload_metrics(jedi_task_id, panda_id, metrics, True)
2688
+ return ret
2689
+
2690
+ def get_workload_metrics(self, jedi_task_id, panda_id):
2691
+ with self.proxyPool.get() as proxy:
2692
+ ret = proxy.get_workload_metrics(jedi_task_id, panda_id)
2693
+ return ret
2694
+
2695
+ def get_jobs_metrics_in_task(self, jedi_task_id: int):
2696
+ with self.proxyPool.get() as proxy:
2697
+ ret = proxy.get_jobs_metrics_in_task(jedi_task_id)
2698
+ return ret
2699
+
2700
+ def enable_job_cloning(self, jedi_task_id, mode, multiplicity, num_sites):
2701
+ with self.proxyPool.get() as proxy:
2702
+ ret = proxy.enable_job_cloning(jedi_task_id, mode, multiplicity, num_sites)
2703
+ return ret
2704
+
2705
+ def disable_job_cloning(self, jedi_task_id):
2706
+ with self.proxyPool.get() as proxy:
2707
+ ret = proxy.disable_job_cloning(jedi_task_id)
2708
+ return ret
2709
+
2710
+ # gets statistics on the number of jobs with a specific status for each nucleus at each site
2711
+ def get_num_jobs_with_status_by_nucleus(self, vo, job_status):
2712
+ with self.proxyPool.get() as proxy:
2713
+ return proxy.get_num_jobs_with_status_by_nucleus(vo, job_status)
2714
+
2715
+ # ==== JEDI taskbuffer functions ===========================
2716
+
2717
+ # get JEDI task with jediTaskID
2718
+ def getTaskWithID_JEDI(self, jediTaskID, fullFlag=False, lockTask=False, pid=None, lockInterval=None, clearError=False):
2719
+ with self.proxyPool.get() as proxy:
2720
+ return proxy.getTaskWithID_JEDI(jediTaskID, fullFlag, lockTask, pid, lockInterval, clearError)
2721
+
2722
+ # update input files stage-in done (according to message from iDDS, called by other methods, etc.)
2723
+ def updateInputFilesStaged_JEDI(self, jeditaskid, scope, filenames_dict, chunk_size=500, by=None, check_scope=True):
2724
+ with self.proxyPool.get() as proxy:
2725
+ return proxy.updateInputFilesStaged_JEDI(jeditaskid, scope, filenames_dict, chunk_size, by, check_scope)
2726
+
2727
+ # ==== Data Carousel functions =============================
2728
+
2729
+ # query data carousel request ID by dataset
2730
+ def get_data_carousel_request_id_by_dataset_JEDI(self, dataset):
2731
+ with self.proxyPool.get() as proxy:
2732
+ return proxy.get_data_carousel_request_id_by_dataset_JEDI(dataset)
2733
+
2734
+ # insert data carousel requests
2735
+ def insert_data_carousel_requests_JEDI(self, task_id, dc_req_specs):
2736
+ with self.proxyPool.get() as proxy:
2737
+ return proxy.insert_data_carousel_requests_JEDI(task_id, dc_req_specs)
2738
+
2739
+ # update a data carousel request
2740
+ def update_data_carousel_request_JEDI(self, dc_req_spec):
2741
+ with self.proxyPool.get() as proxy:
2742
+ return proxy.update_data_carousel_request_JEDI(dc_req_spec)
2743
+
2744
+ # insert data carousel relations
2745
+ def insert_data_carousel_relations_JEDI(self, task_id, request_ids):
2746
+ with self.proxyPool.get() as proxy:
2747
+ return proxy.insert_data_carousel_relations_JEDI(task_id, request_ids)
2748
+
2749
+ # get data carousel queued requests and info of their related tasks
2750
+ def get_data_carousel_queued_requests_JEDI(self):
2751
+ with self.proxyPool.get() as proxy:
2752
+ return proxy.get_data_carousel_queued_requests_JEDI()
2753
+
2754
+ # get data carousel requests of tasks by task status
2755
+ def get_data_carousel_requests_by_task_status_JEDI(self, status_filter_list=None, status_exclusion_list=None):
2756
+ with self.proxyPool.get() as proxy:
2757
+ return proxy.get_data_carousel_requests_by_task_status_JEDI(status_filter_list=status_filter_list, status_exclusion_list=status_exclusion_list)
2758
+
2759
+ # get related tasks and their info of a data carousel request
2760
+ def get_related_tasks_of_data_carousel_request_JEDI(self, request_id, status_filter_list=None, status_exclusion_list=None):
2761
+ with self.proxyPool.get() as proxy:
2762
+ return proxy.get_related_tasks_of_data_carousel_request_JEDI(
2763
+ request_id, status_filter_list=status_filter_list, status_exclusion_list=status_exclusion_list
2764
+ )
2765
+
2766
+ # get data carousel staging requests
2767
+ def get_data_carousel_staging_requests_JEDI(self):
2768
+ with self.proxyPool.get() as proxy:
2769
+ return proxy.get_data_carousel_staging_requests_JEDI()
2770
+
2771
+ # delete data carousel requests
2772
+ def delete_data_carousel_requests_JEDI(self, request_id_list):
2773
+ with self.proxyPool.get() as proxy:
2774
+ return proxy.delete_data_carousel_requests_JEDI(request_id_list)
2775
+
2776
+ # clean up data carousel requests
2777
+ def clean_up_data_carousel_requests_JEDI(self, time_limit_days=30):
2778
+ with self.proxyPool.get() as proxy:
2779
+ return proxy.clean_up_data_carousel_requests_JEDI(time_limit_days)
2780
+
2781
+ # cancel a data carousel request
2782
+ def cancel_data_carousel_request_JEDI(self, request_id):
2783
+ with self.proxyPool.get() as proxy:
2784
+ return proxy.cancel_data_carousel_request_JEDI(request_id)
2785
+
2786
+ # retire a data carousel request
2787
+ def retire_data_carousel_request_JEDI(self, request_id):
2788
+ with self.proxyPool.get() as proxy:
2789
+ return proxy.retire_data_carousel_request_JEDI(request_id)
2790
+
2791
+ # resubmit a data carousel request
2792
+ def resubmit_data_carousel_request_JEDI(self, request_id, exclude_prev_dst=False):
2793
+ with self.proxyPool.get() as proxy:
2794
+ return proxy.resubmit_data_carousel_request_JEDI(request_id, exclude_prev_dst)
2795
+
2796
+ # ==== Workflow fucntions ==================================
2797
+
2798
+ def get_workflow(self, workflow_id):
2799
+ with self.proxyPool.get() as proxy:
2800
+ return proxy.get_workflow(workflow_id)
2801
+
2802
+ def get_workflow_step(self, step_id):
2803
+ with self.proxyPool.get() as proxy:
2804
+ return proxy.get_workflow_step(step_id)
2805
+
2806
+ def get_workflow_data(self, data_id):
2807
+ with self.proxyPool.get() as proxy:
2808
+ return proxy.get_workflow_data(data_id)
2809
+
2810
+ def get_workflow_data_by_name(self, name, workflow_id=None):
2811
+ with self.proxyPool.get() as proxy:
2812
+ return proxy.get_workflow_data_by_name(name, workflow_id)
2813
+
2814
+ def get_steps_of_workflow(self, workflow_id, status_filter_list=None, status_exclusion_list=None):
2815
+ with self.proxyPool.get() as proxy:
2816
+ return proxy.get_steps_of_workflow(workflow_id, status_filter_list, status_exclusion_list)
2817
+
2818
+ def get_data_of_workflow(self, workflow_id, status_filter_list=None, status_exclusion_list=None, type_filter_list=None):
2819
+ with self.proxyPool.get() as proxy:
2820
+ return proxy.get_data_of_workflow(workflow_id, status_filter_list, status_exclusion_list, type_filter_list)
2821
+
2822
+ def query_workflows(self, status_filter_list=None, status_exclusion_list=None, check_interval_sec=300):
2823
+ with self.proxyPool.get() as proxy:
2824
+ return proxy.query_workflows(status_filter_list, status_exclusion_list, check_interval_sec)
2825
+
2826
+ def lock_workflow(self, workflow_id, locked_by, lock_expiration_sec=120):
2827
+ with self.proxyPool.get() as proxy:
2828
+ return proxy.lock_workflow(workflow_id, locked_by, lock_expiration_sec)
2829
+
2830
+ def unlock_workflow(self, workflow_id, locked_by):
2831
+ with self.proxyPool.get() as proxy:
2832
+ return proxy.unlock_workflow(workflow_id, locked_by)
2833
+
2834
+ def lock_workflow_step(self, step_id, locked_by, lock_expiration_sec=120):
2835
+ with self.proxyPool.get() as proxy:
2836
+ return proxy.lock_workflow_step(step_id, locked_by, lock_expiration_sec)
2837
+
2838
+ def unlock_workflow_step(self, step_id, locked_by):
2839
+ with self.proxyPool.get() as proxy:
2840
+ return proxy.unlock_workflow_step(step_id, locked_by)
2841
+
2842
+ def lock_workflow_data(self, data_id, locked_by, lock_expiration_sec=120):
2843
+ with self.proxyPool.get() as proxy:
2844
+ return proxy.lock_workflow_data(data_id, locked_by, lock_expiration_sec)
2845
+
2846
+ def unlock_workflow_data(self, data_id, locked_by):
2847
+ with self.proxyPool.get() as proxy:
2848
+ return proxy.unlock_workflow_data(data_id, locked_by)
2849
+
2850
+ def insert_workflow(self, workflow_spec):
2851
+ with self.proxyPool.get() as proxy:
2852
+ return proxy.insert_workflow(workflow_spec)
2853
+
2854
+ def insert_workflow_step(self, wf_step_spec):
2855
+ with self.proxyPool.get() as proxy:
2856
+ return proxy.insert_workflow_step(wf_step_spec)
2857
+
2858
+ def insert_workflow_data(self, wf_data_spec):
2859
+ with self.proxyPool.get() as proxy:
2860
+ return proxy.insert_workflow_data(wf_data_spec)
2861
+
2862
+ def update_workflow(self, workflow_spec):
2863
+ with self.proxyPool.get() as proxy:
2864
+ return proxy.update_workflow(workflow_spec)
2865
+
2866
+ def update_workflow_step(self, wf_step_spec):
2867
+ with self.proxyPool.get() as proxy:
2868
+ return proxy.update_workflow_step(wf_step_spec)
2869
+
2870
+ def update_workflow_data(self, wf_data_spec):
2871
+ with self.proxyPool.get() as proxy:
2872
+ return proxy.update_workflow_data(wf_data_spec)
2873
+
2874
+ def upsert_workflow_entities(self, workflow_id, actions_dict=None, workflow_spec=None, step_specs=None, data_specs=None):
2875
+ with self.proxyPool.get() as proxy:
2876
+ return proxy.upsert_workflow_entities(workflow_id, actions_dict, workflow_spec, step_specs, data_specs)
2877
+
2878
+ # ==========================================================
2879
+
2880
+
2881
+ # Singleton
2882
+ taskBuffer = TaskBuffer()