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,100 @@
1
+ # List of methods that can be executed by the clients
2
+
3
+ allowed_methods = []
4
+
5
+ # methods from pandaserver.taskbuffer.Utils
6
+ allowed_methods += [
7
+ "isAlive",
8
+ "putFile",
9
+ "deleteFile",
10
+ "getServer",
11
+ "updateLog",
12
+ "fetchLog",
13
+ "touchFile",
14
+ "getVomsAttr",
15
+ "putEventPickingRequest",
16
+ "getAttr",
17
+ "uploadLog",
18
+ "put_checkpoint",
19
+ "delete_checkpoint",
20
+ "put_file_recovery_request",
21
+ "put_workflow_request",
22
+ ]
23
+ # methods from pandaserver.jobdispatcher.JobDispatcher
24
+ allowed_methods += [
25
+ "getJob",
26
+ "updateJob",
27
+ "getStatus",
28
+ "getEventRanges",
29
+ "updateEventRange",
30
+ "getKeyPair",
31
+ "updateEventRanges",
32
+ "getProxy",
33
+ "get_access_token",
34
+ "get_token_key",
35
+ "getCommands",
36
+ "ackCommands",
37
+ "checkJobStatus",
38
+ "checkEventsAvailability",
39
+ "updateJobsInBulk",
40
+ "getResourceTypes",
41
+ "updateWorkerPilotStatus",
42
+ "get_max_worker_id",
43
+ "get_events_status",
44
+ ]
45
+
46
+ # methods from pandaserver.userinterface.UserIF
47
+ allowed_methods += [
48
+ "submitJobs",
49
+ "getJobStatus",
50
+ "killJobs",
51
+ "reassignJobs",
52
+ "getJobStatisticsPerSite",
53
+ "getFullJobStatus",
54
+ "getScriptOfflineRunning",
55
+ "setDebugMode",
56
+ "insertSandboxFileInfo",
57
+ "checkSandboxFile",
58
+ "insertTaskParams",
59
+ "killTask",
60
+ "finishTask",
61
+ "getJediTasksInTimeRange",
62
+ "getJediTaskDetails",
63
+ "retryTask",
64
+ "changeTaskPriority",
65
+ "reassignTask",
66
+ "changeTaskAttributePanda",
67
+ "pauseTask",
68
+ "resumeTask",
69
+ "increaseAttemptNrPanda",
70
+ "killUnfinishedJobs",
71
+ "changeTaskSplitRulePanda",
72
+ "changeTaskModTimePanda",
73
+ "avalancheTask",
74
+ "getPandaIDsWithTaskID",
75
+ "reactivateTask",
76
+ "getTaskStatus",
77
+ "reassignShare",
78
+ "getTaskParamsMap",
79
+ "updateWorkers",
80
+ "harvesterIsAlive",
81
+ "reportWorkerStats_jobtype",
82
+ "getWorkerStats",
83
+ "addHarvesterDialogs",
84
+ "setNumSlotsForWP",
85
+ "reloadInput",
86
+ "enableJumboJobs",
87
+ "updateServiceMetrics",
88
+ "getUserJobMetadata",
89
+ "getJumboJobDatasets",
90
+ "sweepPQ",
91
+ "get_job_statistics_per_site_label_resource",
92
+ "relay_idds_command",
93
+ "send_command_to_job",
94
+ "execute_idds_workflow_command",
95
+ "set_user_secret",
96
+ "get_user_secrets",
97
+ "get_ban_users",
98
+ "get_files_in_datasets",
99
+ "release_task",
100
+ ]
@@ -0,0 +1,152 @@
1
+ import base64
2
+ import datetime
3
+ from threading import Lock
4
+
5
+ import jwt
6
+ import requests
7
+ from cryptography.hazmat.backends import default_backend
8
+ from cryptography.hazmat.primitives import serialization
9
+ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
10
+ from jwt.exceptions import InvalidTokenError
11
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
12
+
13
+
14
+ def decode_value(val):
15
+ if isinstance(val, str):
16
+ val = val.encode()
17
+ decoded = base64.urlsafe_b64decode(val + b"==")
18
+ return int.from_bytes(decoded, "big")
19
+
20
+
21
+ def rsa_pem_from_jwk(jwk):
22
+ public_num = RSAPublicNumbers(n=decode_value(jwk["n"]), e=decode_value(jwk["e"]))
23
+ public_key = public_num.public_key(default_backend())
24
+ pem = public_key.public_bytes(
25
+ encoding=serialization.Encoding.PEM,
26
+ format=serialization.PublicFormat.SubjectPublicKeyInfo,
27
+ )
28
+ return pem
29
+
30
+
31
+ def get_jwk(kid, jwks):
32
+ for jwk in jwks.get("keys", []):
33
+ if jwk.get("kid") == kid:
34
+ return jwk
35
+ raise InvalidTokenError("JWK not found for kid={0}".format(kid, str(jwks)))
36
+
37
+
38
+ # token decoder
39
+ class TokenDecoder:
40
+ # constructor
41
+ def __init__(self, refresh_interval=10):
42
+ self.lock = Lock()
43
+ self.data = {}
44
+ self.refresh_interval = refresh_interval
45
+
46
+ # get cached data
47
+ def get_data(self, url, log_stream):
48
+ try:
49
+ with self.lock:
50
+ if url not in self.data or naive_utcnow() - self.data[url]["last_update"] > datetime.timedelta(minutes=self.refresh_interval):
51
+ log_stream.debug(f"to refresh {url}")
52
+ tmp_data = requests.get(url).json()
53
+ log_stream.debug("refreshed")
54
+ self.data[url] = {
55
+ "data": tmp_data,
56
+ "last_update": naive_utcnow(),
57
+ }
58
+ return self.data[url]["data"]
59
+ except Exception as e:
60
+ log_stream.error(f"failed to refresh with {str(e)}")
61
+ raise
62
+
63
+ # decode and verify JWT token
64
+ def deserialize_token(self, token, auth_config, vo, log_stream, legacy_token_issuers):
65
+ try:
66
+ # check audience
67
+ unverified = jwt.decode(token, verify=False, options={"verify_signature": False})
68
+ conf_key = None
69
+ audience = None
70
+ if "aud" in unverified:
71
+ audience = unverified["aud"]
72
+ if audience in auth_config:
73
+ conf_key = audience
74
+ if not conf_key:
75
+ # use sub as config key for access token
76
+ conf_key = unverified["sub"]
77
+ discovery_endpoint = auth_config[conf_key]["oidc_config_url"]
78
+ # decode headers
79
+ headers = jwt.get_unverified_header(token)
80
+ # get key id
81
+ if headers is None or "kid" not in headers:
82
+ raise jwt.exceptions.InvalidTokenError("cannot extract kid from headers")
83
+ kid = headers["kid"]
84
+ # retrieve OIDC configuration and JWK set
85
+ oidc_config = self.get_data(discovery_endpoint, log_stream)
86
+ jwks = self.get_data(oidc_config["jwks_uri"], log_stream)
87
+ # get JWK and public key
88
+ jwk = get_jwk(kid, jwks)
89
+ public_key = rsa_pem_from_jwk(jwk)
90
+ # decode token only with RS256
91
+ if unverified["iss"] and unverified["iss"] != oidc_config["issuer"] and oidc_config["issuer"].startswith(unverified["iss"]):
92
+ # iss is missing the last '/' in access tokens
93
+ issuer = unverified["iss"]
94
+ else:
95
+ issuer = oidc_config["issuer"]
96
+ if legacy_token_issuers:
97
+ issuers = list(dict.fromkeys([issuer] + legacy_token_issuers))
98
+ else:
99
+ issuers = [issuer]
100
+ decoded = None
101
+ err_msg = None
102
+ for tmp_issuer in issuers:
103
+ try:
104
+ decoded = jwt.decode(
105
+ token,
106
+ public_key,
107
+ verify=True,
108
+ algorithms="RS256",
109
+ audience=audience,
110
+ issuer=tmp_issuer,
111
+ )
112
+ break
113
+ except jwt.exceptions.InvalidIssuerError as e:
114
+ err_msg = str(e)
115
+ if not decoded:
116
+ raise jwt.exceptions.InvalidTokenError(f"failed to decode: {err_msg}")
117
+ if vo is not None:
118
+ decoded["vo"] = vo
119
+ else:
120
+ decoded["vo"] = auth_config[conf_key]["vo"]
121
+ return decoded
122
+ except Exception:
123
+ raise
124
+
125
+
126
+ # get an access token with client_credentials flow
127
+ def get_access_token(token_endpoint: str, client_id: str, client_secret: str, scope: str = None, timeout: int = 180) -> tuple[bool, str]:
128
+ """
129
+ Get an access token with client_credentials flow
130
+
131
+ :param token_endpoint: URL for token request
132
+ :param client_id: client ID
133
+ :param client_secret: client secret
134
+ :param scope: space separated string of scopes
135
+ :param timeout: timeout in seconds
136
+
137
+ :return: (True, access_token) or (False, error_str)
138
+ """
139
+ try:
140
+ token_request = {
141
+ "grant_type": "client_credentials",
142
+ "client_id": client_id,
143
+ "client_secret": client_secret,
144
+ }
145
+ if scope:
146
+ token_request["scope"] = scope
147
+ token_response = requests.post(token_endpoint, data=token_request, timeout=timeout)
148
+ token_response.raise_for_status()
149
+ return True, token_response.json()["access_token"]
150
+ except Exception as e:
151
+ error_str = f"failed to get access token with {str(e)}"
152
+ return False, error_str
@@ -0,0 +1,196 @@
1
+ from pandaserver.config import panda_config
2
+ from pandaserver.srvcore.CoreUtils import CacheDict
3
+
4
+ if panda_config.token_authType is None:
5
+ pass
6
+ elif panda_config.token_authType == "scitokens":
7
+ import scitokens
8
+ else:
9
+ from pandaserver.srvcore.oidc_utils import TokenDecoder
10
+
11
+ token_decoder = TokenDecoder()
12
+
13
+ import traceback
14
+
15
+ import jwt
16
+
17
+ cache_dict = CacheDict()
18
+
19
+
20
+ # decode token
21
+ def decode_token(serialized_token, env, tmp_log):
22
+ authenticated = False
23
+ message_str = None
24
+ subprocess_env = {}
25
+ try:
26
+ vo = None
27
+ role = None
28
+ if panda_config.token_authType == "oidc":
29
+ if "HTTP_ORIGIN" in env:
30
+ # two formats, vo:role and vo.role
31
+ vo = env["HTTP_ORIGIN"]
32
+ # only vo.role for auth filename which is a key of auth_vo_dict
33
+ else:
34
+ vo = None
35
+ # only vo.role for auth filename which is a key of auth_vo_dict
36
+ vo_role = vo.replace(":", ".")
37
+ token = token_decoder.deserialize_token(serialized_token, panda_config.auth_config, vo, tmp_log, panda_config.legacy_token_issuers)
38
+ # extract role
39
+ if "vo" in token:
40
+ vo_raw = token["vo"] # Original input value of vo
41
+ tmp_log.debug(f"Raw VO from token: {vo_raw}")
42
+
43
+ if vo_raw.startswith("vo."):
44
+ # Handle vo names starting with "vo."
45
+ if ":" in vo_raw:
46
+ vo, role = vo_raw.split(":", 1) # Split by ':' into vo and role
47
+ else:
48
+ vo, role = vo_raw, None # No role present
49
+ else:
50
+ # Handle cases without "vo." prefix
51
+ if ":" in vo_raw:
52
+ vo, role = vo_raw.split(":", 1)
53
+ elif "." in vo_raw:
54
+ parts = vo_raw.rsplit(".", 1) # Split only on the last '.'
55
+ vo, role = parts[0], parts[1]
56
+ else:
57
+ vo, role = vo_raw, None # Single part, no role
58
+
59
+ tmp_log.debug(f"Parsed VO: {vo}, role: {role}")
60
+
61
+ # check vo
62
+ if vo not in panda_config.auth_policies:
63
+ tmp_log.error(f"VO '{vo}' not found in auth_policies: {list(panda_config.auth_policies.keys())}")
64
+ message_str = f"Unknown vo : {vo}"
65
+ else:
66
+ # groups claim name based on JWT profile
67
+ groups_claim_name = "groups"
68
+ if vo_role in panda_config.auth_vo_dict:
69
+ jwt_profile = panda_config.auth_vo_dict[vo_role].get("jwt_profile")
70
+ if jwt_profile == "wlcg":
71
+ groups_claim_name = "wlcg.groups"
72
+ # robot
73
+ if vo_role in panda_config.auth_vo_dict and "robot_ids" in panda_config.auth_vo_dict[vo_role]:
74
+ robot_ids = panda_config.auth_vo_dict[vo_role].get("robot_ids")
75
+ if isinstance(robot_ids, str):
76
+ robot_ids = robot_ids.split(",")
77
+ if not robot_ids:
78
+ robot_ids = []
79
+ robot_ids = [i for i in robot_ids if i]
80
+ if token["sub"] in robot_ids:
81
+ if groups_claim_name not in token:
82
+ if role:
83
+ token[groups_claim_name] = [f"{vo}/{role}"]
84
+ else:
85
+ token[groups_claim_name] = [f"{vo}"]
86
+ if "name" not in token:
87
+ token["name"] = f"robot {role}"
88
+ # check role
89
+ if role:
90
+ if f"{vo}/{role}" not in token[groups_claim_name] and f"/{vo}/{role}" not in token[groups_claim_name]:
91
+ message_str = f"Not a member of the {vo}/{role} group"
92
+ else:
93
+ subprocess_env["PANDA_OIDC_VO"] = vo
94
+ subprocess_env["PANDA_OIDC_GROUP"] = role
95
+ subprocess_env["PANDA_OIDC_ROLE"] = role
96
+ authenticated = True
97
+ else:
98
+ for member_string, member_info in panda_config.auth_policies[vo]:
99
+ if member_string in token[groups_claim_name] or f"/{member_string}" in token[groups_claim_name]:
100
+ subprocess_env["PANDA_OIDC_VO"] = vo
101
+ subprocess_env["PANDA_OIDC_GROUP"] = member_info["group"]
102
+ subprocess_env["PANDA_OIDC_ROLE"] = member_info["role"]
103
+ authenticated = True
104
+ break
105
+ if not authenticated:
106
+ message_str = f"Not a member of the {vo} group"
107
+ else:
108
+ token = scitokens.SciToken.deserialize(serialized_token, audience=panda_config.token_audience)
109
+
110
+ # check issuer
111
+ if "iss" not in token:
112
+ message_str = "Issuer is undefined in the token"
113
+ else:
114
+ if panda_config.token_authType == "scitokens":
115
+ items = token.claims()
116
+ else:
117
+ items = token.items()
118
+ for c, v in items:
119
+ subprocess_env[f"PANDA_OIDC_CLAIM_{str(c)}"] = str(v)
120
+ # use sub and scope as DN and FQAN
121
+ if "SSL_CLIENT_S_DN" not in env:
122
+ if "name" in token:
123
+ subprocess_env["SSL_CLIENT_S_DN"] = " ".join([t[:1].upper() + t[1:].lower() for t in str(token["name"]).split()])
124
+ if "preferred_username" in token:
125
+ subprocess_env["SSL_CLIENT_S_DN"] += f"/CN=nickname:{token['preferred_username']}"
126
+ else:
127
+ subprocess_env["SSL_CLIENT_S_DN"] = str(token["sub"])
128
+ i = 0
129
+ for scope in token.get("scope", "").split():
130
+ if scope.startswith("role:"):
131
+ subprocess_env[f"GRST_CRED_AUTH_TOKEN_{i}"] = "VOMS " + str(scope.split(":")[-1])
132
+ i += 1
133
+ if role:
134
+ subprocess_env[f"GRST_CRED_AUTH_TOKEN_{i}"] = f"VOMS /{vo}/Role={role}"
135
+ i += 1
136
+ else:
137
+ # protection against cached decisions that miss x509-related variables due to token+x509 access
138
+ subprocess_env["SSL_CLIENT_S_DN"] = env["SSL_CLIENT_S_DN"]
139
+ for key in env:
140
+ if key.startswith("GRST_CRED_") or key.startswith("GRST_CONN_"):
141
+ subprocess_env[key] = env[key]
142
+
143
+ except jwt.exceptions.InvalidTokenError as e:
144
+ message_str = f"Invalid token. {str(e)}"
145
+ except Exception as e:
146
+ message_str = f"Token decode failure. {str(e)} {traceback.format_exc()}"
147
+ return {"authenticated": authenticated, "message": message_str, "subprocess_env": subprocess_env}
148
+
149
+
150
+ # PanDA request object
151
+ class PandaRequest:
152
+ def __init__(self, env, tmp_log):
153
+ # environment
154
+ self.subprocess_env = env
155
+ # header
156
+ self.headers_in = {}
157
+ # authentication
158
+ self.authenticated = True
159
+ # message
160
+ self.message = None
161
+
162
+ # content-length
163
+ if "CONTENT_LENGTH" in self.subprocess_env:
164
+ self.headers_in["content-length"] = self.subprocess_env["CONTENT_LENGTH"]
165
+
166
+ # tokens
167
+ try:
168
+ if panda_config.token_authType in ["scitokens", "oidc"] and "HTTP_AUTHORIZATION" in env:
169
+ serialized_token = env["HTTP_AUTHORIZATION"].split()[1]
170
+ decision_key = f"""{serialized_token} : {env.get("HTTP_ORIGIN", None)} : {env.get("SSL_CLIENT_S_DN", None)}"""
171
+ cached_decision = cache_dict.get(decision_key, tmp_log, decode_token, serialized_token, env, tmp_log)
172
+ self.authenticated = cached_decision["authenticated"]
173
+ self.message = cached_decision["message"]
174
+ if self.message:
175
+ tmp_log.error(f"""{self.message} - Origin: {env.get("HTTP_ORIGIN", None)}, Token: {env["HTTP_AUTHORIZATION"]}""")
176
+ self.subprocess_env.update(cached_decision["subprocess_env"])
177
+ except Exception as e:
178
+ self.message = f"Failed to instantiate reqeust object. {str(e)}"
179
+ tmp_log.error(
180
+ f"""{self.message} - Origin: {env.get("HTTP_ORIGIN", None)}, Token: {env.get("HTTP_AUTHORIZATION", None)}\n{traceback.format_exc()}"""
181
+ )
182
+
183
+ # get remote host
184
+ def get_remote_host(self):
185
+ if "REMOTE_HOST" in self.subprocess_env:
186
+ return self.subprocess_env["REMOTE_HOST"]
187
+ return ""
188
+
189
+ # accept json
190
+ def acceptJson(self):
191
+ try:
192
+ if "HTTP_ACCEPT" in self.subprocess_env:
193
+ return "application/json" in self.subprocess_env["HTTP_ACCEPT"]
194
+ except Exception:
195
+ pass
196
+ return False
@@ -0,0 +1,42 @@
1
+ import datetime
2
+ import json
3
+
4
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
5
+
6
+
7
+ # make message
8
+ def make_message(msg_type, **kwargs):
9
+ msg_dict = {"msg_type": msg_type}
10
+ msg_dict.update(kwargs)
11
+ msg_dict["timestamp"] = int(naive_utcnow().timestamp())
12
+ return json.dumps(msg_dict)
13
+
14
+
15
+ # send a job message
16
+ def send_job_message(msg_queue, msg_topic, task_id, job_id):
17
+ # make message
18
+ msg = make_message("get_job", taskid=task_id, jobid=job_id)
19
+ # use job ID for selector
20
+ headers = {"type": job_id}
21
+ # send message to topic first
22
+ msg_topic.send(msg)
23
+ # send the same message to queue
24
+ msg_queue.send(msg, headers=headers)
25
+
26
+
27
+ # delete a job message
28
+ def delete_job_message(msg_queue, job_id, time_out=10):
29
+ # job ID for selector
30
+ headers = {"selector": "type='{0}' OR JMSType='{0}'".format(job_id)}
31
+ # subscribe to remove job messages
32
+ msg_queue.add_remover(headers, time_out)
33
+ # delete old removers
34
+ msg_queue.purge_removers()
35
+
36
+
37
+ # send a task message
38
+ def send_task_message(msg_topic, command_str, task_id):
39
+ # make message
40
+ msg = make_message(f"{command_str}_task", taskid=task_id)
41
+ # send message to topic
42
+ msg_topic.send(msg)