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,841 @@
1
+ import atexit
2
+ import datetime
3
+ import json
4
+ import socket
5
+ import sys
6
+ import time
7
+ import traceback
8
+ from contextlib import contextmanager
9
+
10
+ from pandacommon.pandalogger.LogWrapper import LogWrapper
11
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
12
+
13
+ from pandaserver.config import panda_config
14
+ from pandaserver.taskbuffer.JediTaskSpec import (
15
+ push_status_changes as task_push_status_changes,
16
+ )
17
+ from pandaserver.taskbuffer.JobSpec import (
18
+ push_status_changes as job_push_status_changes,
19
+ )
20
+
21
+ if panda_config.backend == "oracle":
22
+ import oracledb
23
+
24
+ varNUMBER = oracledb.NUMBER
25
+ else:
26
+ varNUMBER = int
27
+
28
+ # topics in SQL_QUEUE
29
+ SQL_QUEUE_TOPIC_async_dataset_update = "async_dataset_update"
30
+
31
+
32
+ # Internal caching of a result. Use only for information with low update frequency and low memory footprint
33
+ def memoize(f):
34
+ memo = {}
35
+ kwd_mark = object()
36
+
37
+ def helper(self, *args, **kwargs):
38
+ now = datetime.datetime.now()
39
+ key = args + (kwd_mark,) + tuple(sorted(kwargs.items()))
40
+ if key not in memo or memo[key]["timestamp"] < now - datetime.timedelta(hours=1):
41
+ tmp_data = {"value": f(self, *args, **kwargs), "timestamp": now}
42
+ memo[key] = tmp_data
43
+ return memo[key]["value"]
44
+
45
+ return helper
46
+
47
+
48
+ # convert dict to bind variable dict
49
+ def convert_dict_to_bind_vars(item):
50
+ ret = dict()
51
+ for k in item:
52
+ ret[f":{k}"] = item[k]
53
+ return ret
54
+
55
+
56
+ # Base class for DB proxy modules
57
+ class BaseModule:
58
+ # constructor
59
+ def __init__(self, log_stream: LogWrapper):
60
+ self._log_stream = log_stream
61
+ self.conn = None
62
+ self.cur = None
63
+ self.mb_proxy_dict = None
64
+ self.useOtherError = False
65
+ self.backend = panda_config.backend
66
+ # retry count
67
+ self.nTry = 5
68
+ # hostname
69
+ self.myHostName = socket.getfqdn()
70
+ self.backend = panda_config.backend
71
+ # host name
72
+ self.hostname = None
73
+ # composite modules
74
+ self.composite_modules = {}
75
+
76
+ # typical input cache
77
+ self.typical_input_cache = {}
78
+
79
+ # list of work queues
80
+ self.workQueueMap = None
81
+ # update time for work queue map
82
+ self.updateTimeForWorkQueue = None
83
+
84
+ # mb proxy for JEDI
85
+ self.jedi_mb_proxy_dict = None
86
+ self.jedi_mb_proxy_dict_setter = None
87
+
88
+ # JEDI config
89
+ self.jedi_config = None
90
+
91
+ # set JEDI attributes
92
+ def set_jedi_attributes(self, jedi_config, jedi_mb_proxy_dict_setter):
93
+ self.jedi_config = jedi_config
94
+ self.jedi_mb_proxy_dict_setter = jedi_mb_proxy_dict_setter
95
+
96
+ # abstract method to commit
97
+ def connect(self, **kwargs):
98
+ """
99
+ Commit the transaction
100
+ """
101
+ raise NotImplementedError("connect is not implemented")
102
+
103
+ # commit
104
+ def _commit(self):
105
+ try:
106
+ self.conn.commit()
107
+ return True
108
+ except Exception:
109
+ self._log_stream.error("commit error")
110
+ return False
111
+
112
+ # rollback
113
+ def _rollback(self, useOtherError=False):
114
+ return_value = True
115
+ # rollback
116
+ err_code = None
117
+ self._log_stream.debug("rollback")
118
+ try:
119
+ self.conn.rollback()
120
+ except Exception:
121
+ self._log_stream.error("rollback error")
122
+ return_value = False
123
+ # reconnect if needed
124
+ try:
125
+ err_type, err_value = sys.exc_info()[:2]
126
+ # get error code for postgres
127
+ if self.backend == "postgres":
128
+ try:
129
+ err_code = err_value.pgcode
130
+ except Exception:
131
+ pass
132
+ # get ORA ErrorCode
133
+ if err_code is None:
134
+ err_code = str(err_value).split()[0]
135
+ err_code = err_code[:-1]
136
+ err_msg = f"rollback EC:{err_code} {err_value}"
137
+ self._log_stream.debug(err_msg)
138
+ # error codes for connection error
139
+ if self.backend == "oracle":
140
+ error_list_for_reconnect = [
141
+ "ORA-01012",
142
+ "ORA-01033",
143
+ "ORA-01034",
144
+ "ORA-01089",
145
+ "ORA-03113",
146
+ "ORA-03114",
147
+ "ORA-12203",
148
+ "ORA-12500",
149
+ "ORA-12571",
150
+ "ORA-03135",
151
+ "ORA-25402",
152
+ ]
153
+ # other errors are apparently given when connection lost contact
154
+ if useOtherError:
155
+ error_list_for_reconnect += ["ORA-01861", "ORA-01008"]
156
+ elif self.backend == "postgres":
157
+ import psycopg2.errorcodes as psycopg_errorcodes
158
+
159
+ error_list_for_reconnect = [
160
+ psycopg_errorcodes.CONNECTION_EXCEPTION,
161
+ psycopg_errorcodes.SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION,
162
+ psycopg_errorcodes.CONNECTION_DOES_NOT_EXIST,
163
+ psycopg_errorcodes.SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,
164
+ psycopg_errorcodes.CONNECTION_FAILURE,
165
+ psycopg_errorcodes.READ_ONLY_SQL_TRANSACTION,
166
+ ]
167
+ else:
168
+ # mysql error codes for connection error
169
+ import MySQLdb
170
+ from MySQLdb.constants.CR import (
171
+ CONN_HOST_ERROR,
172
+ CONNECTION_ERROR,
173
+ LOCALHOST_CONNECTION,
174
+ SERVER_LOST,
175
+ )
176
+ from MySQLdb.constants.ER import (
177
+ ACCESS_DENIED_ERROR,
178
+ DBACCESS_DENIED_ERROR,
179
+ ILLEGAL_VALUE_FOR_TYPE,
180
+ SERVER_SHUTDOWN,
181
+ )
182
+
183
+ error_list_for_reconnect = [
184
+ ACCESS_DENIED_ERROR,
185
+ DBACCESS_DENIED_ERROR,
186
+ SERVER_SHUTDOWN,
187
+ CONNECTION_ERROR,
188
+ CONN_HOST_ERROR,
189
+ LOCALHOST_CONNECTION,
190
+ SERVER_LOST,
191
+ ]
192
+ # other errors are apparently given when connection lost contact
193
+ if useOtherError:
194
+ error_list_for_reconnect += [ILLEGAL_VALUE_FOR_TYPE]
195
+ if err_code in error_list_for_reconnect:
196
+ # reconnect
197
+ reconnect_stat = self.connect(reconnect=True)
198
+ self._log_stream.debug(f"rollback reconnected {reconnect_stat}")
199
+ except Exception:
200
+ pass
201
+ # return
202
+ return return_value
203
+
204
+ # add composite module
205
+ def add_composite_module(self, module_name, module):
206
+ self.composite_modules[module_name] = module
207
+
208
+ # get composite module
209
+ def get_composite_module(self, module_name):
210
+ return self.composite_modules.get(module_name, None)
211
+
212
+ # dump error message
213
+ def dump_error_message(self, tmp_log: LogWrapper):
214
+ """
215
+ Dump error message to the log
216
+
217
+ :param tmp_log: log wrapper
218
+ """
219
+ # error
220
+ err_type, err_value = sys.exc_info()[:2]
221
+ err_str = f"{err_type.__name__} {err_value}"
222
+ err_str.strip()
223
+ err_str += " "
224
+ err_str += traceback.format_exc()
225
+ tmp_log.error(err_str)
226
+
227
+ # create logger with tag
228
+ def create_tagged_logger(self, comment: str, tag: str = None) -> LogWrapper:
229
+ """
230
+ Create logger from function comment and tag
231
+
232
+ param comment: comment of the function
233
+ param tag: tag to add to the method name
234
+ return: logger
235
+ """
236
+ method_name = comment.split(" ")[-2].split(".")[-1]
237
+ if tag is None:
238
+ tag = naive_utcnow().strftime("%Y-%m-%d/%H:%M:%S.%f")
239
+ method_name += f" < {tag} >"
240
+ tmp_log = LogWrapper(self._log_stream, method_name)
241
+ return tmp_log
242
+
243
+ # get configuration value. cached for an hour
244
+ @memoize
245
+ def getConfigValue(self, component, key, app="pandaserver", vo=None):
246
+ comment = " /* DBProxy.getConfigValue */"
247
+ tmp_log = self.create_tagged_logger(comment)
248
+ varMap = {":component": component, ":key": key, ":app": app}
249
+ sql = """
250
+ SELECT value, value_json, type FROM ATLAS_PANDA.CONFIG
251
+ WHERE component=:component
252
+ AND key=:key
253
+ AND app=:app
254
+ """
255
+
256
+ # If VO is specified, select only the config values for this VO or VO independent values
257
+ if vo:
258
+ varMap[":vo"] = vo
259
+ sql += "AND (vo=:vo or vo IS NULL)"
260
+
261
+ self.cur.execute(sql + comment, varMap)
262
+
263
+ try:
264
+ value_str, value_json_str, type = self.cur.fetchone()
265
+ except TypeError:
266
+ error_message = f"Specified key={key} not found for component={component} app={app}"
267
+ tmp_log.debug(error_message)
268
+ return None
269
+
270
+ try:
271
+ if type.lower() in ("str", "string"):
272
+ return value_str
273
+ elif type.lower() in ("int", "integer"):
274
+ return int(value_str)
275
+ elif type.lower() == "float":
276
+ return float(value_str)
277
+ elif type.lower() in ("bool", "boolean"):
278
+ if value_str.lower() == "true":
279
+ return True
280
+ else:
281
+ return False
282
+ elif type.lower() == "json":
283
+ return json.loads(value_json_str)
284
+ else:
285
+ raise ValueError
286
+ except json.decoder.JSONDecodeError:
287
+ tmp_log.debug(f"Could not decode. Value_json: {value_json_str}, Type: {type}")
288
+ return None
289
+ except ValueError:
290
+ tmp_log.debug(f"Wrong value/type pair. Value: {value_str}, Type: {type}")
291
+ return None
292
+ except Exception as e:
293
+ tmp_log.debug(f"Unexpected error: {str(e)}")
294
+ raise e
295
+
296
+ def getvalue_corrector(self, value):
297
+ """
298
+ Needed to support old and new versions of cx_Oracle
299
+ :return:
300
+ """
301
+ if isinstance(value, list): # cx_Oracle version >= 6.3
302
+ return value[0]
303
+ else: # cx_Oracle version < 6.3
304
+ return value
305
+
306
+ # get mb proxy
307
+ def get_mb_proxy(self, channel):
308
+ if self.mb_proxy_dict is None:
309
+ try:
310
+ if hasattr(panda_config, "mq_configFile") and panda_config.mq_configFile:
311
+ # delay import to open logger file inside python daemon
312
+ from pandaserver.taskbuffer.PanDAMsgProcessor import MsgProcAgent
313
+
314
+ out_q_list = [
315
+ "panda_jobstatus",
316
+ "panda_jedi",
317
+ "panda_pilot_topic",
318
+ "panda_pilot_queue",
319
+ ]
320
+ mp_agent = MsgProcAgent(config_file=panda_config.mq_configFile)
321
+ mb_proxy_dict = mp_agent.start_passive_mode(in_q_list=[], out_q_list=out_q_list)
322
+ # stop with atexit
323
+ atexit.register(mp_agent.stop_passive_mode)
324
+ # return
325
+ self.mb_proxy_dict = mb_proxy_dict
326
+ except Exception:
327
+ comment = " /* DBProxy.get_mb_proxy */"
328
+ tmp_log = self.create_tagged_logger(comment)
329
+ self.dump_error_message(tmp_log)
330
+ self.mb_proxy_dict = {}
331
+ if not self.mb_proxy_dict or channel not in self.mb_proxy_dict["out"]:
332
+ return None
333
+ return self.mb_proxy_dict["out"][channel]
334
+
335
+ # close connection
336
+ def close_connection(self):
337
+ if self.conn:
338
+ try:
339
+ self.conn.close()
340
+ except Exception:
341
+ pass
342
+ return
343
+
344
+ # cleanup
345
+ def cleanup(self):
346
+ comment = " /* DBProxy.cleanup */"
347
+ tmp_log = self.create_tagged_logger(comment)
348
+ tmp_log.debug("start")
349
+ self.close_connection()
350
+ atexit.unregister(self.close_connection)
351
+ tmp_log.debug("done")
352
+
353
+ # query an SQL
354
+ def querySQL(self, sql, arraySize=1000):
355
+ comment = " /* DBProxy.querySQL */"
356
+ tmp_log = self.create_tagged_logger(comment)
357
+ try:
358
+ tmp_log.debug(f"SQL={sql} ")
359
+ # begin transaction
360
+ self.conn.begin()
361
+ self.cur.arraysize = arraySize
362
+ self.cur.execute(sql + comment)
363
+ res = self.cur.fetchall()
364
+ # commit
365
+ if not self._commit():
366
+ raise RuntimeError("Commit error")
367
+ return res
368
+ except Exception:
369
+ # roll back
370
+ self._rollback(self.useOtherError)
371
+ self.dump_error_message(tmp_log)
372
+ return None
373
+
374
+ # query an SQL return Status
375
+ def querySQLS(self, sql, varMap, arraySize=1000):
376
+ comment = " /* DBProxy.querySQLS */"
377
+ tmp_log = self.create_tagged_logger(comment)
378
+ try:
379
+ tmp_log.debug(f"SQL={sql} vapMap={varMap} ")
380
+ # begin transaction
381
+ self.conn.begin()
382
+ self.cur.arraysize = arraySize
383
+ ret = self.cur.execute(sql + comment, varMap)
384
+ if ret:
385
+ ret = True
386
+ if sql.startswith("INSERT") or sql.startswith("UPDATE") or sql.startswith("DELETE"):
387
+ res = self.cur.rowcount
388
+ else:
389
+ res = self.cur.fetchall()
390
+ # commit
391
+ if not self._commit():
392
+ raise RuntimeError("Commit error")
393
+ return ret, res
394
+ except Exception as e:
395
+ # roll back
396
+ self._rollback(self.useOtherError)
397
+ self.dump_error_message(tmp_log)
398
+ return -1, None
399
+
400
+ # execute an SQL return with executemany
401
+ def executemanySQL(self, sql, varMaps, arraySize=1000):
402
+ comment = " /* DBProxy.executemanySQL */"
403
+ try:
404
+ # begin transaction
405
+ self.conn.begin()
406
+ self.cur.arraysize = arraySize
407
+ ret = self.cur.executemany(sql + comment, varMaps)
408
+ if sql.startswith("INSERT") or sql.startswith("UPDATE") or sql.startswith("DELETE"):
409
+ res = self.cur.rowcount
410
+ else:
411
+ raise RuntimeError("Operation unsupported. Only INSERT, UPDATE, DELETE are allowed")
412
+ # commit
413
+ if not self._commit():
414
+ raise RuntimeError("Commit error")
415
+ return res
416
+ except Exception as e:
417
+ # roll back
418
+ self._rollback(self.useOtherError)
419
+ tmp_log = self.create_tagged_logger(comment)
420
+ tmp_log.error(f"{sql} {str(varMaps)}")
421
+ self.dump_error_message(tmp_log)
422
+ return None
423
+
424
+ # get CLOB
425
+ def getClobObj(self, sql, varMap, arraySize=10000, use_commit=True):
426
+ comment = " /* DBProxy.getClobObj */"
427
+ try:
428
+ # begin transaction
429
+ if use_commit:
430
+ self.conn.begin()
431
+ self.cur.arraysize = arraySize
432
+ ret = self.cur.execute(sql + comment, varMap)
433
+ if ret:
434
+ ret = True
435
+ res = []
436
+ for items in self.cur:
437
+ resItem = []
438
+ for item in items:
439
+ # read CLOB
440
+ try:
441
+ itemRead = item.read()
442
+ except AttributeError:
443
+ itemRead = item
444
+ resItem.append(itemRead)
445
+ # append
446
+ res.append(resItem)
447
+ # commit
448
+ if use_commit:
449
+ if not self._commit():
450
+ raise RuntimeError("Commit error")
451
+ return ret, res
452
+ except Exception as e:
453
+ # roll back
454
+ if use_commit:
455
+ self._rollback()
456
+ tmp_log = self.create_tagged_logger(comment)
457
+ tmp_log.error(f"{sql} {str(varMap)}")
458
+ self.dump_error_message(tmp_log)
459
+ return -1, None
460
+
461
+ # wake up connection
462
+ def wakeUp(self):
463
+ comment = " /* DBProxy.wakeUp */"
464
+ tmp_log = self.create_tagged_logger(comment)
465
+ for iTry in range(5):
466
+ try:
467
+ # check if the connection is working
468
+ self.conn.ping()
469
+ return
470
+ except Exception:
471
+ tmp_log.error(f"{iTry} : connection is dead")
472
+ self.dump_error_message(tmp_log)
473
+ # wait for reconnection
474
+ time.sleep(1)
475
+ self.connect(reconnect=True)
476
+
477
+ # transaction as a context manager
478
+ @contextmanager
479
+ def transaction(self, name: str | None = None, tmp_log=None):
480
+ """
481
+ Context manager for transaction
482
+
483
+ Args:
484
+ name (str, optional): name of the transaction to be shown in the log
485
+ tmp_log (LogWrapper, optional): logger to use. If None, a new logger will be created
486
+
487
+ Yields:
488
+ Any: the cursor object for executing SQL commands
489
+ Any: the logger object for logging in DBProxy
490
+ """
491
+ comment = " /* DBProxy.transaction */"
492
+ try:
493
+ if tmp_log is None:
494
+ tmp_log = self.create_tagged_logger(comment, tag=name)
495
+ tmp_log.debug("transaction start")
496
+ # begin transaction
497
+ self.conn.begin()
498
+ # cursor and logger for the with block
499
+ yield (self.cur, tmp_log)
500
+ # commit transaction
501
+ if not self._commit():
502
+ raise RuntimeError("commit error")
503
+ tmp_log.debug("transaction done")
504
+ except Exception as e:
505
+ # roll back
506
+ self._rollback()
507
+ self.dump_error_message(tmp_log)
508
+ raise e
509
+
510
+ # record status change
511
+ def recordStatusChange(self, pandaID, jobStatus, jobInfo=None, infoMap={}, useCommit=True, no_late_bulk_exec=True, extracted_sqls=None):
512
+ comment = " /* DBProxy.recordStatusChange */"
513
+ tmp_log = self.create_tagged_logger(comment)
514
+ # check config
515
+ if not hasattr(panda_config, "record_statuschange") or panda_config.record_statuschange is not True:
516
+ return
517
+ # get job info
518
+ varMap = {}
519
+ varMap[":PandaID"] = pandaID
520
+ varMap[":jobStatus"] = jobStatus
521
+ varMap[":modificationHost"] = self.myHostName
522
+ if jobInfo is not None:
523
+ varMap[":computingSite"] = jobInfo.computingSite
524
+ varMap[":cloud"] = jobInfo.cloud
525
+ varMap[":prodSourceLabel"] = jobInfo.prodSourceLabel
526
+ elif infoMap is not None:
527
+ varMap[":computingSite"] = infoMap["computingSite"]
528
+ varMap[":cloud"] = infoMap["cloud"]
529
+ varMap[":prodSourceLabel"] = infoMap["prodSourceLabel"]
530
+ else:
531
+ # no info
532
+ return
533
+ # convert NULL to None
534
+ for tmpKey in varMap:
535
+ if varMap[tmpKey] == "NULL":
536
+ varMap[tmpKey] = None
537
+ # insert
538
+ sql = "INSERT INTO ATLAS_PANDA.jobs_StatusLog "
539
+ sql += "(PandaID,modificationTime,jobStatus,prodSourceLabel,cloud,computingSite,modificationHost,modiftime_extended) "
540
+ sql += "VALUES (:PandaID,CURRENT_DATE,:jobStatus,:prodSourceLabel,:cloud,:computingSite,:modificationHost,CURRENT_TIMESTAMP) "
541
+ try:
542
+ # start transaction
543
+ if no_late_bulk_exec:
544
+ if useCommit:
545
+ self.conn.begin()
546
+ self.cur.execute(sql + comment, varMap)
547
+ # commit
548
+ if useCommit:
549
+ if not self._commit():
550
+ raise RuntimeError("Commit error")
551
+ else:
552
+ extracted_sqls.setdefault("state_change", {"sql": sql + comment, "vars": []})
553
+ extracted_sqls["state_change"]["vars"].append(varMap)
554
+ except Exception:
555
+ # roll back
556
+ if useCommit and no_late_bulk_exec:
557
+ self._rollback()
558
+ self.dump_error_message(tmp_log)
559
+ if not useCommit:
560
+ raise RuntimeError("recordStatusChange failed")
561
+ return
562
+
563
+ def push_job_status_message(
564
+ self,
565
+ job_spec,
566
+ panda_id,
567
+ status,
568
+ jedi_task_id=None,
569
+ special_handling=None,
570
+ extra_data=None,
571
+ ):
572
+ comment = " /* DBProxy.push_job_status_message */"
573
+ if not (hasattr(panda_config, "mq_configFile") and panda_config.mq_configFile):
574
+ # skip if not configured
575
+ return
576
+ to_push = False
577
+ if special_handling is not None:
578
+ to_push = job_push_status_changes(special_handling)
579
+ elif job_spec is not None:
580
+ to_push = job_spec.push_status_changes()
581
+ # only run if to push status change
582
+ if not to_push:
583
+ return
584
+ # skip statuses unnecessary to push
585
+ if status in ["sent", "holding", "merging"]:
586
+ return
587
+ # skip if no mb to push to
588
+ mb_proxy = self.get_mb_proxy("panda_jobstatus")
589
+ if not mb_proxy:
590
+ return
591
+ if to_push:
592
+ tmp_log = self.create_tagged_logger(comment)
593
+ # push job status change
594
+ try:
595
+ now_time = naive_utcnow()
596
+ now_ts = int(now_time.timestamp())
597
+ # init
598
+ inputs = []
599
+ computingsite = None
600
+ error_tmp_dict = {}
601
+ # info from job spec
602
+ if job_spec is not None:
603
+ # task id
604
+ if jedi_task_id is None:
605
+ jedi_task_id = job_spec.jediTaskID
606
+ # inputs
607
+ if job_spec.Files is not None:
608
+ for file_spec in job_spec.Files:
609
+ if file_spec.type in ["input", "pseudo_input"]:
610
+ inputs.append(file_spec.lfn)
611
+ # computing site
612
+ if job_spec.computingSite is not None:
613
+ computingsite = job_spec.computingSite
614
+ # error codes and diags
615
+ error_tmp_dict["piloterrorcode"] = job_spec.pilotErrorCode
616
+ error_tmp_dict["exeerrorcode"] = job_spec.exeErrorCode
617
+ error_tmp_dict["superrorcode"] = job_spec.supErrorCode
618
+ error_tmp_dict["ddmerrorcode"] = job_spec.ddmErrorCode
619
+ error_tmp_dict["brokerageerrorcode"] = job_spec.brokerageErrorCode
620
+ error_tmp_dict["jobdispatchererrorcode"] = job_spec.jobDispatcherErrorCode
621
+ error_tmp_dict["taskbuffererrorcode"] = job_spec.taskBufferErrorCode
622
+ error_tmp_dict["piloterrordiag"] = job_spec.pilotErrorDiag
623
+ error_tmp_dict["exeerrordiag"] = job_spec.exeErrorDiag
624
+ error_tmp_dict["superrordiag"] = job_spec.supErrorDiag
625
+ error_tmp_dict["ddmerrordiag"] = job_spec.ddmErrorDiag
626
+ error_tmp_dict["brokerageerrordiag"] = job_spec.brokerageErrorDiag
627
+ error_tmp_dict["jobdispatchererrordiag"] = job_spec.jobDispatcherErrorDiag
628
+ error_tmp_dict["taskbuffererrordiag"] = job_spec.taskBufferErrorDiag
629
+ # message
630
+ orig_msg_dict = {
631
+ "msg_type": "job_status",
632
+ "jobid": panda_id,
633
+ "taskid": jedi_task_id,
634
+ "status": status,
635
+ "timestamp": now_ts,
636
+ }
637
+ update_msg_dict = {
638
+ "computingsite": computingsite,
639
+ "inputs": inputs if inputs else None,
640
+ }
641
+ update_msg_dict.update(error_tmp_dict)
642
+ msg_dict = update_msg_dict.copy()
643
+ if extra_data:
644
+ msg_dict.update(extra_data)
645
+ msg_dict.update(orig_msg_dict)
646
+ msg = json.dumps(msg_dict)
647
+ if mb_proxy.got_disconnected:
648
+ mb_proxy.restart()
649
+ mb_proxy.send(msg)
650
+ tmp_log.debug(f"sent message: {msg}")
651
+ except Exception:
652
+ self.dump_error_message(tmp_log)
653
+
654
+ def insert_to_query_pool(self, topic, panda_id, task_id, sql, var_map, exec_order):
655
+ comment = " /* DBProxy.insert_to_query_pool */"
656
+ sqlI = (
657
+ "INSERT INTO {}.SQL_QUEUE (topic,PandaID,jediTaskID,creationTime,data,execution_order) "
658
+ "VALUES(:topic,:PandaID,:taskID,:creationTime,:data,:execution_order) ".format(panda_config.schemaPANDA)
659
+ )
660
+ varMap = {
661
+ ":topic": topic,
662
+ ":PandaID": panda_id,
663
+ ":taskID": task_id,
664
+ ":creationTime": naive_utcnow(),
665
+ ":execution_order": exec_order,
666
+ ":data": json.dumps((sql, var_map)),
667
+ }
668
+ self.cur.execute(sqlI + comment, varMap)
669
+
670
+ # check if exception is from NOWAIT
671
+ def isNoWaitException(self, errValue):
672
+ # for oracle
673
+ ora_err_code = str(errValue).split()[0]
674
+ ora_err_code = ora_err_code[:-1]
675
+ if ora_err_code == "ORA-00054":
676
+ return True
677
+ # for postgres
678
+ if type(errValue).__name__ == "LockNotAvailable":
679
+ return True
680
+ return False
681
+
682
+ # set super status
683
+ def setSuperStatus_JEDI(self, jediTaskID, superStatus):
684
+ comment = " /* JediDBProxy.setSuperStatus_JEDI */"
685
+ tmpLog = self.create_tagged_logger(comment, f"jediTaskID={jediTaskID}")
686
+ retTasks = []
687
+ try:
688
+ # sql to set super status
689
+ sqlCT = f"UPDATE {panda_config.schemaJEDI}.JEDI_Tasks "
690
+ sqlCT += "SET superStatus=:superStatus "
691
+ sqlCT += "WHERE jediTaskID=:jediTaskID "
692
+ # set super status
693
+ varMap = {}
694
+ varMap[":jediTaskID"] = jediTaskID
695
+ varMap[":superStatus"] = superStatus
696
+ self.cur.execute(sqlCT + comment, varMap)
697
+ return True
698
+ except Exception:
699
+ # error
700
+ self.dump_error_message(tmpLog)
701
+ return False
702
+
703
+ # set DEFT status
704
+ def setDeftStatus_JEDI(self, jediTaskID, taskStatus):
705
+ comment = " /* JediDBProxy.setDeftStatus_JEDI */"
706
+ tmpLog = self.create_tagged_logger(comment, f"jediTaskID={jediTaskID}")
707
+ try:
708
+ sqlD = f"UPDATE {panda_config.schemaDEFT}.T_TASK "
709
+ sqlD += "SET status=:status,timeStamp=CURRENT_DATE "
710
+ sqlD += "WHERE taskID=:jediTaskID "
711
+ varMap = {}
712
+ varMap[":status"] = taskStatus
713
+ varMap[":jediTaskID"] = jediTaskID
714
+ tmpLog.debug(sqlD + comment + str(varMap))
715
+ self.cur.execute(sqlD + comment, varMap)
716
+ return True
717
+ except Exception:
718
+ # error
719
+ self.dump_error_message(tmpLog)
720
+ return False
721
+
722
+ # task status logging
723
+ def record_task_status_change(self, jedi_task_id):
724
+ comment = " /* JediDBProxy.record_task_status_change */"
725
+ tmpLog = self.create_tagged_logger(comment, f"jediTaskID={jedi_task_id}")
726
+ tmpLog.debug("start")
727
+ varMap = dict()
728
+ varMap[":jediTaskID"] = jedi_task_id
729
+ varMap[":modificationHost"] = socket.getfqdn()
730
+ # sql
731
+ sqlNS = (
732
+ "INSERT INTO {0}.TASKS_STATUSLOG "
733
+ "(jediTaskID,modificationTime,status,modificationHost,attemptNr,reason) "
734
+ "SELECT jediTaskID,CURRENT_TIMESTAMP,status,:modificationHost,attemptNr,"
735
+ "SUBSTR(errorDialog,0,255) "
736
+ "FROM {0}.JEDI_Tasks WHERE jediTaskID=:jediTaskID "
737
+ ).format(panda_config.schemaJEDI)
738
+ self.cur.execute(sqlNS + comment, varMap)
739
+ tmpLog.debug("done")
740
+
741
+ # push task status message
742
+ def push_task_status_message(self, task_spec, jedi_task_id, status, split_rule=None):
743
+ to_push = False
744
+ if task_spec is not None:
745
+ to_push = task_spec.push_status_changes()
746
+ elif split_rule is not None:
747
+ to_push = task_push_status_changes(split_rule)
748
+ # only run if to push status change
749
+ if not to_push:
750
+ return
751
+ # skip statuses unnecessary to push
752
+ # if status in ['pending']:
753
+ # return
754
+ comment = " /* JediDBProxy.push_task_status_message */"
755
+ tmpLog = self.create_tagged_logger(comment, f"jediTaskID={jedi_task_id}")
756
+ tmpLog.debug("start")
757
+ # send task status messages to mq
758
+ try:
759
+ now_time = naive_utcnow()
760
+ now_ts = int(now_time.timestamp())
761
+ msg_dict = {
762
+ "msg_type": "task_status",
763
+ "taskid": jedi_task_id,
764
+ "status": status,
765
+ "timestamp": now_ts,
766
+ }
767
+ msg = json.dumps(msg_dict)
768
+ if self.jedi_mb_proxy_dict is None:
769
+ self.jedi_mb_proxy_dict = self.jedi_mb_proxy_dict_setter()
770
+ if self.jedi_mb_proxy_dict is None:
771
+ tmpLog.debug("Failed to get mb_proxy of internal MQs. Skipped ")
772
+ return
773
+ try:
774
+ mb_proxy = self.jedi_mb_proxy_dict["out"]["jedi_jobtaskstatus"]
775
+ except KeyError as e:
776
+ tmpLog.warning(f"Skipped due to {e} ; jedi_mb_proxy_dict is {self.jedi_mb_proxy_dict}")
777
+ return
778
+ if mb_proxy.got_disconnected:
779
+ mb_proxy.restart()
780
+ mb_proxy.send(msg)
781
+ except Exception:
782
+ self.dump_error_message(tmpLog)
783
+ tmpLog.debug("done")
784
+
785
+ # push message to message processors which triggers functions of agents
786
+ def push_task_trigger_message(self, msg_type, jedi_task_id, data_dict=None, priority=None, task_spec=None):
787
+ comment = " /* JediDBProxy.push_task_trigger_message */"
788
+ tmpLog = self.create_tagged_logger(comment, f"msg_type={msg_type} jediTaskID={jedi_task_id}")
789
+ tmpLog.debug("start")
790
+ # send task status messages to mq
791
+ try:
792
+ now_time = naive_utcnow()
793
+ now_ts = int(now_time.timestamp())
794
+ # get mbproxy
795
+ msg_dict = {}
796
+ if data_dict:
797
+ msg_dict.update(data_dict)
798
+ msg_dict.update(
799
+ {
800
+ "msg_type": msg_type,
801
+ "taskid": jedi_task_id,
802
+ "timestamp": now_ts,
803
+ }
804
+ )
805
+ msg = json.dumps(msg_dict)
806
+ if self.jedi_mb_proxy_dict is None:
807
+ self.jedi_mb_proxy_dict = self.jedi_mb_proxy_dict_setter()
808
+ if self.jedi_mb_proxy_dict is None:
809
+ tmpLog.debug("Failed to get mb_proxy of internal MQs. Skipped ")
810
+ return
811
+ try:
812
+ mq_name = msg_type
813
+ mb_proxy = self.jedi_mb_proxy_dict["out"][mq_name]
814
+ except KeyError as e:
815
+ tmpLog.warning(f"Skipped due to {e} ; jedi_mb_proxy_dict is {self.jedi_mb_proxy_dict}")
816
+ return
817
+ if mb_proxy.got_disconnected:
818
+ mb_proxy.restart()
819
+ # message priority
820
+ msg_priority = None
821
+ if priority is not None:
822
+ msg_priority = priority
823
+ elif task_spec is not None:
824
+ try:
825
+ if task_spec.prodSourceLabel == "user":
826
+ if task_spec.gshare in ["User Analysis", "Express Analysis"]:
827
+ msg_priority = 2
828
+ else:
829
+ msg_priority = 1
830
+ except AttributeError:
831
+ pass
832
+ # send message
833
+ if msg_priority is not None:
834
+ mb_proxy.send(msg, priority=msg_priority)
835
+ else:
836
+ mb_proxy.send(msg)
837
+ except Exception:
838
+ self.dump_error_message(tmpLog)
839
+ return
840
+ tmpLog.debug("done")
841
+ return True