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,1006 @@
1
+ """
2
+ job specification
3
+
4
+ """
5
+
6
+ import datetime
7
+ import json
8
+ import re
9
+
10
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
11
+
12
+ from pandaserver.taskbuffer.FileSpec import FileSpec
13
+
14
+ reserveChangedState = False
15
+
16
+
17
+ class JobSpec(object):
18
+ # attributes
19
+ _attributes = (
20
+ "PandaID",
21
+ "jobDefinitionID",
22
+ "schedulerID",
23
+ "pilotID",
24
+ "creationTime",
25
+ "creationHost",
26
+ "modificationTime",
27
+ "modificationHost",
28
+ "AtlasRelease",
29
+ "transformation",
30
+ "homepackage",
31
+ "prodSeriesLabel",
32
+ "prodSourceLabel",
33
+ "prodUserID",
34
+ "assignedPriority",
35
+ "currentPriority",
36
+ "attemptNr",
37
+ "maxAttempt",
38
+ "jobStatus",
39
+ "jobName",
40
+ "maxCpuCount",
41
+ "maxCpuUnit",
42
+ "maxDiskCount",
43
+ "maxDiskUnit",
44
+ "ipConnectivity",
45
+ "minRamCount",
46
+ "minRamUnit",
47
+ "startTime",
48
+ "endTime",
49
+ "cpuConsumptionTime",
50
+ "cpuConsumptionUnit",
51
+ "commandToPilot",
52
+ "transExitCode",
53
+ "pilotErrorCode",
54
+ "pilotErrorDiag",
55
+ "exeErrorCode",
56
+ "exeErrorDiag",
57
+ "supErrorCode",
58
+ "supErrorDiag",
59
+ "ddmErrorCode",
60
+ "ddmErrorDiag",
61
+ "brokerageErrorCode",
62
+ "brokerageErrorDiag",
63
+ "jobDispatcherErrorCode",
64
+ "jobDispatcherErrorDiag",
65
+ "taskBufferErrorCode",
66
+ "taskBufferErrorDiag",
67
+ "computingSite",
68
+ "computingElement",
69
+ "jobParameters",
70
+ "metadata",
71
+ "prodDBlock",
72
+ "dispatchDBlock",
73
+ "destinationDBlock",
74
+ "destinationSE",
75
+ "nEvents",
76
+ "grid",
77
+ "cloud",
78
+ "cpuConversion",
79
+ "sourceSite",
80
+ "destinationSite",
81
+ "transferType",
82
+ "taskID",
83
+ "cmtConfig",
84
+ "stateChangeTime",
85
+ "prodDBUpdateTime",
86
+ "lockedby",
87
+ "relocationFlag",
88
+ "jobExecutionID",
89
+ "VO",
90
+ "pilotTiming",
91
+ "workingGroup",
92
+ "processingType",
93
+ "prodUserName",
94
+ "nInputFiles",
95
+ "countryGroup",
96
+ "batchID",
97
+ "parentID",
98
+ "specialHandling",
99
+ "jobsetID",
100
+ "coreCount",
101
+ "nInputDataFiles",
102
+ "inputFileType",
103
+ "inputFileProject",
104
+ "inputFileBytes",
105
+ "nOutputDataFiles",
106
+ "outputFileBytes",
107
+ "jobMetrics",
108
+ "workQueue_ID",
109
+ "jediTaskID",
110
+ "jobSubStatus",
111
+ "actualCoreCount",
112
+ "reqID",
113
+ "maxRSS",
114
+ "maxVMEM",
115
+ "maxSWAP",
116
+ "maxPSS",
117
+ "avgRSS",
118
+ "avgVMEM",
119
+ "avgSWAP",
120
+ "avgPSS",
121
+ "maxWalltime",
122
+ "nucleus",
123
+ "eventService",
124
+ "failedAttempt",
125
+ "hs06sec",
126
+ "gshare",
127
+ "hs06",
128
+ "totRCHAR",
129
+ "totWCHAR",
130
+ "totRBYTES",
131
+ "totWBYTES",
132
+ "rateRCHAR",
133
+ "rateWCHAR",
134
+ "rateRBYTES",
135
+ "rateWBYTES",
136
+ "resource_type",
137
+ "diskIO",
138
+ "memory_leak",
139
+ "memory_leak_x2",
140
+ "container_name",
141
+ "job_label",
142
+ "gco2_regional",
143
+ "gco2_global",
144
+ "cpu_architecture_level",
145
+ "outputFileType",
146
+ )
147
+ # slots
148
+ __slots__ = _attributes + ("Files", "_changedAttrs", "_reserveChangedState")
149
+ # attributes which have 0 by default
150
+ _zeroAttrs = (
151
+ "assignedPriority",
152
+ "currentPriority",
153
+ "attemptNr",
154
+ "maxAttempt",
155
+ "maxCpuCount",
156
+ "maxDiskCount",
157
+ "minRamCount",
158
+ "cpuConsumptionTime",
159
+ "pilotErrorCode",
160
+ "exeErrorCode",
161
+ "supErrorCode",
162
+ "ddmErrorCode",
163
+ "brokerageErrorCode",
164
+ "jobDispatcherErrorCode",
165
+ "taskBufferErrorCode",
166
+ "nEvents",
167
+ "relocationFlag",
168
+ "jobExecutionID",
169
+ "nOutputDataFiles",
170
+ "outputFileBytes",
171
+ )
172
+ # attribute to be suppressed. They are in another table
173
+ _suppAttrs = ("jobParameters", "metadata")
174
+ # mapping between sequence and attr
175
+ _seqAttrMap = {"PandaID": "ATLAS_PANDA.JOBSDEFINED4_PANDAID_SEQ.nextval"}
176
+ # limit length
177
+ _limitLength = {
178
+ "ddmErrorDiag": 500,
179
+ "taskBufferErrorDiag": 300,
180
+ "jobDispatcherErrorDiag": 250,
181
+ "brokerageErrorDiag": 250,
182
+ "pilotErrorDiag": 500,
183
+ "exeErrorDiag": 500,
184
+ "jobSubStatus": 80,
185
+ "supErrorDiag": 250,
186
+ "commandToPilot": 250,
187
+ "inputFileType": 32,
188
+ "outputFileType": 32,
189
+ "grid": 50,
190
+ "sourceSite": 36,
191
+ "destinationSite": 36,
192
+ }
193
+ # tag for special handling
194
+ _tagForSH = {
195
+ "altStgOut": "ao",
196
+ "acceptPartial": "ap",
197
+ "allOkEvents": "at",
198
+ "notDiscardEvents": "de",
199
+ "decAttOnFailedES": "df",
200
+ "debugMode": "dm",
201
+ "dynamicNumEvents": "dy",
202
+ "encJobParams": "ej",
203
+ "fakeJobToIgnore": "fake",
204
+ "homeCloud": "hc",
205
+ "hpoWorkflow": "ho",
206
+ "inFilePosEvtNum": "if",
207
+ "inputPrestaging": "ip",
208
+ "lumiBlock": "lb",
209
+ "noLoopingCheck": "lc",
210
+ "mergeAtOs": "mo",
211
+ "noExecStrCnv": "nc",
212
+ "onSiteMerging": "om",
213
+ "pushStatusChanges": "pc",
214
+ "pushJob": "pj",
215
+ "putLogToOS": "po",
216
+ "registerEsFiles": "re",
217
+ "retryRam": "rr",
218
+ "resurrectConsumers": "rs",
219
+ "requestType": "rt",
220
+ "jobCloning": "sc",
221
+ "scoutJob": "sj",
222
+ "taskQueuedTime": "tq",
223
+ "usePrefetcher": "up",
224
+ "useSecrets": "us",
225
+ "useZipToPin": "uz",
226
+ "writeInputToFile": "wf",
227
+ }
228
+
229
+ # constructor
230
+ def __init__(self):
231
+ # install attributes
232
+ for attr in self._attributes:
233
+ object.__setattr__(self, attr, None)
234
+ # files list
235
+ object.__setattr__(self, "Files", [])
236
+ # map of changed attributes
237
+ object.__setattr__(self, "_changedAttrs", {})
238
+ # reserve changed state at instance level
239
+ object.__setattr__(self, "_reserveChangedState", False)
240
+
241
+ # override __getattribute__ for SQL
242
+ def __getattribute__(self, name):
243
+ ret = object.__getattribute__(self, name)
244
+ if ret is None:
245
+ return "NULL"
246
+ return ret
247
+
248
+ # override __setattr__ to collecte the changed attributes
249
+ def __setattr__(self, name, value):
250
+ oldVal = getattr(self, name)
251
+ object.__setattr__(self, name, value)
252
+ newVal = getattr(self, name)
253
+ if name == "jobStatus":
254
+ if oldVal != newVal:
255
+ self.stateChangeTime = naive_utcnow()
256
+ # collect changed attributes
257
+ if oldVal != newVal and name not in self._suppAttrs:
258
+ self._changedAttrs[name] = value
259
+
260
+ # reset changed attribute list
261
+ def resetChangedList(self):
262
+ object.__setattr__(self, "_changedAttrs", {})
263
+
264
+ # add File to files list
265
+ def addFile(self, file):
266
+ # set owner
267
+ file.setOwner(self)
268
+ # append
269
+ self.Files.append(file)
270
+
271
+ # pack tuple into JobSpec
272
+ def pack(self, values):
273
+ for i in range(len(self._attributes)):
274
+ attr = self._attributes[i]
275
+ val = values[i]
276
+ object.__setattr__(self, attr, val)
277
+
278
+ # return a tuple of values
279
+ def values(self):
280
+ ret = []
281
+ for attr in self._attributes:
282
+ val = getattr(self, attr)
283
+ ret.append(val)
284
+ return tuple(ret)
285
+
286
+ # return map of values
287
+ def valuesMap(self, useSeq=False, onlyChanged=False):
288
+ ret = {}
289
+ for attr in self._attributes:
290
+ if useSeq and attr in self._seqAttrMap:
291
+ continue
292
+ if onlyChanged:
293
+ if attr not in self._changedAttrs:
294
+ continue
295
+ val = getattr(self, attr)
296
+ if val == "NULL":
297
+ if attr in self._zeroAttrs:
298
+ val = 0
299
+ else:
300
+ val = None
301
+ # jobParameters/metadata go to another table
302
+ if attr in self._suppAttrs:
303
+ val = None
304
+ # truncate too long values
305
+ if attr in self._limitLength:
306
+ if val is not None:
307
+ val = val[: self._limitLength[attr]]
308
+ ret[f":{attr}"] = val
309
+ return ret
310
+
311
+ # return state values to be pickled
312
+ def __getstate__(self):
313
+ state = []
314
+ for attr in self._attributes:
315
+ val = getattr(self, attr)
316
+ state.append(val)
317
+ if reserveChangedState or self._reserveChangedState:
318
+ state.append(self._changedAttrs)
319
+ # append File info
320
+ state.append(self.Files)
321
+ return state
322
+
323
+ # restore state from the unpickled state values
324
+ def __setstate__(self, state):
325
+ for i in range(len(self._attributes)):
326
+ # schema evolution is supported only when adding attributes
327
+ if i + 1 < len(state):
328
+ object.__setattr__(self, self._attributes[i], state[i])
329
+ else:
330
+ object.__setattr__(self, self._attributes[i], "NULL")
331
+ object.__setattr__(self, "Files", state[-1])
332
+ if not hasattr(self, "_reserveChangedState"):
333
+ object.__setattr__(self, "_reserveChangedState", False)
334
+ if reserveChangedState or self._reserveChangedState:
335
+ object.__setattr__(self, "_changedAttrs", state[-2])
336
+ else:
337
+ object.__setattr__(self, "_changedAttrs", {})
338
+
339
+ # return column names for INSERT or full SELECT
340
+ def columnNames(cls):
341
+ ret = ""
342
+ for attr in cls._attributes:
343
+ if ret != "":
344
+ ret += ","
345
+ ret += attr
346
+ return ret
347
+
348
+ columnNames = classmethod(columnNames)
349
+
350
+ # return expression of values for INSERT
351
+ def valuesExpression(cls):
352
+ ret = "VALUES("
353
+ for attr in cls._attributes:
354
+ ret += "%s"
355
+ if attr != cls._attributes[len(cls._attributes) - 1]:
356
+ ret += ","
357
+ ret += ")"
358
+ return ret
359
+
360
+ valuesExpression = classmethod(valuesExpression)
361
+
362
+ # return expression of bind values for INSERT
363
+ def bindValuesExpression(cls, useSeq=False):
364
+ from pandaserver.config import panda_config
365
+
366
+ ret = "VALUES("
367
+ for attr in cls._attributes:
368
+ if useSeq and attr in cls._seqAttrMap:
369
+ if panda_config.backend == "mysql":
370
+ # mysql
371
+ ret += f"NULL,"
372
+ else:
373
+ # oracle
374
+ ret += f"{cls._seqAttrMap[attr]},"
375
+ else:
376
+ ret += f":{attr},"
377
+ ret = ret[:-1]
378
+ ret += ")"
379
+ return ret
380
+
381
+ bindValuesExpression = classmethod(bindValuesExpression)
382
+
383
+ # return an expression for UPDATE
384
+ def updateExpression(cls):
385
+ ret = ""
386
+ for attr in cls._attributes:
387
+ ret = ret + attr + "=%s"
388
+ if attr != cls._attributes[len(cls._attributes) - 1]:
389
+ ret += ","
390
+ return ret
391
+
392
+ updateExpression = classmethod(updateExpression)
393
+
394
+ # return an expression of bind variables for UPDATE
395
+ def bindUpdateExpression(cls):
396
+ ret = ""
397
+ for attr in cls._attributes:
398
+ ret += f"{attr}=:{attr},"
399
+ ret = ret[:-1]
400
+ ret += " "
401
+ return ret
402
+
403
+ bindUpdateExpression = classmethod(bindUpdateExpression)
404
+
405
+ # comparison function for sort
406
+ def compFunc(cls, a, b):
407
+ iPandaID = list(cls._attributes).index("PandaID")
408
+ iPriority = list(cls._attributes).index("currentPriority")
409
+ if a[iPriority] > b[iPriority]:
410
+ return -1
411
+ elif a[iPriority] < b[iPriority]:
412
+ return 1
413
+ else:
414
+ if a[iPandaID] > b[iPandaID]:
415
+ return 1
416
+ elif a[iPandaID] < b[iPandaID]:
417
+ return -1
418
+ else:
419
+ return 0
420
+
421
+ compFunc = classmethod(compFunc)
422
+
423
+ # return an expression of bind variables for UPDATE to update only changed attributes
424
+ def bindUpdateChangesExpression(self):
425
+ ret = ""
426
+ for attr in self._attributes:
427
+ if attr in self._changedAttrs:
428
+ ret += f"{attr}=:{attr},"
429
+ ret = ret[:-1]
430
+ ret += " "
431
+ return ret
432
+
433
+ # check if goint to merging
434
+ def produceUnMerge(self):
435
+ for tmpFile in self.Files:
436
+ if tmpFile.isUnMergedOutput():
437
+ return True
438
+ return False
439
+
440
+ # truncate string attribute
441
+ def truncateStringAttr(cls, attr, val):
442
+ if attr not in cls._limitLength:
443
+ return val
444
+ if val is None:
445
+ return val
446
+ return val[: cls._limitLength[attr]]
447
+
448
+ truncateStringAttr = classmethod(truncateStringAttr)
449
+
450
+ # set DDM backend
451
+ def setDdmBackEnd(self, backEnd):
452
+ if self.specialHandling in [None, ""]:
453
+ self.specialHandling = "ddm:" + backEnd
454
+ else:
455
+ if "ddm:" in self.specialHandling:
456
+ self.specialHandling = re.sub("ddm:[,]+", "ddm:" + backEnd, self.specialHandling)
457
+ else:
458
+ self.specialHandling = self.specialHandling + "," + "ddm:" + backEnd
459
+
460
+ # set LB number
461
+ def setLumiBlockNr(self, lumiBlockNr):
462
+ if self.specialHandling in ["", None, "NULL"]:
463
+ self.specialHandling = f"lb:{lumiBlockNr}"
464
+ else:
465
+ self.specialHandling += f",lb:{lumiBlockNr}"
466
+
467
+ # get LB number
468
+ def getLumiBlockNr(self):
469
+ if self.specialHandling is not None:
470
+ for tmpItem in self.specialHandling.split(","):
471
+ if tmpItem.startswith("lb:"):
472
+ return int(tmpItem.split(":")[-1])
473
+ return None
474
+
475
+ # get DDM backend
476
+ def getDdmBackEnd(self):
477
+ if self.specialHandling is None:
478
+ return None
479
+ for tmpItem in self.specialHandling.split(","):
480
+ if tmpItem.startswith("ddm:"):
481
+ return tmpItem.split(":")[-1]
482
+ return None
483
+
484
+ # set to accept partial finish
485
+ def setToAcceptPartialFinish(self):
486
+ self.set_special_handling("acceptPartial")
487
+
488
+ # accept partial finish
489
+ def acceptPartialFinish(self):
490
+ return self.check_special_handling("acceptPartial")
491
+
492
+ # set home cloud
493
+ def setHomeCloud(self, homeCloud):
494
+ if self.specialHandling in ["", None, "NULL"]:
495
+ self.specialHandling = f"hc:{homeCloud}"
496
+ else:
497
+ self.specialHandling += f",hc:{homeCloud}"
498
+
499
+ # get cloud
500
+ def getCloud(self):
501
+ if self.specialHandling is not None:
502
+ for tmpItem in self.specialHandling.split(","):
503
+ if tmpItem.startswith("hc:"): # hc: Home Cloud
504
+ return tmpItem.split(":")[-1]
505
+ return self.cloud
506
+
507
+ # check if cancelled or it's flavor
508
+ def isCancelled(self):
509
+ return self.jobStatus in ["cancelled", "closed"]
510
+
511
+ # get file names which were uploaded to alternative locations
512
+ def altStgOutFileList(self):
513
+ try:
514
+ if self.jobMetrics is not None:
515
+ for item in self.jobMetrics.split():
516
+ if item.startswith("altTransferred="):
517
+ return item.split("=")[-1].split(",")
518
+ except Exception:
519
+ pass
520
+ return []
521
+
522
+ # check special handling
523
+ def check_special_handling(self, key):
524
+ if self.specialHandling:
525
+ return self._tagForSH[key] in self.specialHandling.split(",")
526
+ return False
527
+
528
+ # set special handling
529
+ def set_special_handling(self, key):
530
+ if self.specialHandling:
531
+ items = self.specialHandling.split(",")
532
+ else:
533
+ items = []
534
+ if self._tagForSH[key] not in items:
535
+ items.append(self._tagForSH[key])
536
+ self.specialHandling = ",".join(items)
537
+
538
+ # get mode for alternative stage-out
539
+ def getAltStgOut(self):
540
+ if self.specialHandling is not None:
541
+ for tmpItem in self.specialHandling.split(","):
542
+ if tmpItem.startswith(f"{self._tagForSH['altStgOut']}:"):
543
+ return tmpItem.split(":")[-1]
544
+ return None
545
+
546
+ # set alternative stage-out
547
+ def setAltStgOut(self, mode):
548
+ if self.specialHandling is not None:
549
+ items = self.specialHandling.split(",")
550
+ else:
551
+ items = []
552
+ # remove old value
553
+ newItems = []
554
+ for tmpItem in items:
555
+ if tmpItem.startswith(f"{self._tagForSH['altStgOut']}:"):
556
+ continue
557
+ newItems.append(tmpItem)
558
+ newItems.append(f"{self._tagForSH['altStgOut']}:{mode}")
559
+ self.specialHandling = ",".join(newItems)
560
+
561
+ # put log files to OS
562
+ def putLogToOS(self):
563
+ return self.check_special_handling("putLogToOS")
564
+
565
+ # set to put log files to OS
566
+ def setToPutLogToOS(self):
567
+ self.set_special_handling("putLogToOS")
568
+
569
+ # write input to file
570
+ def writeInputToFile(self):
571
+ return self.check_special_handling("writeInputToFile")
572
+
573
+ # set to write input to file
574
+ def setToWriteInputToFile(self):
575
+ self.set_special_handling("writeInputToFile")
576
+
577
+ # set request type
578
+ def setRequestType(self, reqType):
579
+ if self.specialHandling is not None:
580
+ items = self.specialHandling.split(",")
581
+ else:
582
+ items = []
583
+ newItems = []
584
+ setFlag = False
585
+ for item in items:
586
+ if not item.startswith(self._tagForSH["requestType"]):
587
+ newItems.append(item)
588
+ newItems.append(f"{self._tagForSH['requestType']}={reqType}")
589
+ self.specialHandling = ",".join(newItems)
590
+
591
+ # sort files
592
+ def sortFiles(self):
593
+ try:
594
+ lfnMap = {}
595
+ for tmpFile in self.Files:
596
+ if tmpFile.lfn not in lfnMap:
597
+ lfnMap[tmpFile.lfn] = []
598
+ lfnMap[tmpFile.lfn].append(tmpFile)
599
+ lfns = sorted(lfnMap)
600
+ newFiles = []
601
+ for tmpLFN in lfns:
602
+ for tmpFile in lfnMap[tmpLFN]:
603
+ newFiles.append(tmpFile)
604
+ self.Files = newFiles
605
+ except Exception:
606
+ pass
607
+
608
+ # get zip file map
609
+ def getZipFileMap(self):
610
+ zipMap = dict()
611
+ try:
612
+ if self.jobParameters is not None:
613
+ zipStr = re.search("<ZIP_MAP>(.+)</ZIP_MAP>", self.jobParameters)
614
+ if zipStr is not None:
615
+ for item in zipStr.group(1).split():
616
+ zipFile, conFiles = item.split(":")
617
+ conFiles = conFiles.split(",")
618
+ zipMap[zipFile] = conFiles
619
+ except Exception:
620
+ pass
621
+ return zipMap
622
+
623
+ # add multi step exec
624
+ def addMultiStepExec(self, steps):
625
+ if not self.jobParameters:
626
+ self.jobParameters = ""
627
+ self.jobParameters += "<MULTI_STEP_EXEC>" + json.dumps(steps) + "</MULTI_STEP_EXEC>"
628
+
629
+ # extract multi step exec
630
+ def extractMultiStepExec(self):
631
+ try:
632
+ if "<MULTI_STEP_EXEC>" in self.jobParameters and "</MULTI_STEP_EXEC>" in self.jobParameters:
633
+ pp_1, pp_2 = self.jobParameters.split("<MULTI_STEP_EXEC>")
634
+ pp_2 = pp_2.split("</MULTI_STEP_EXEC>")[0]
635
+ return pp_1, json.loads(pp_2)
636
+ except Exception:
637
+ pass
638
+ return self.jobParameters, None
639
+
640
+ # suppress execute string conversion
641
+ def noExecStrCnv(self):
642
+ return self.check_special_handling("noExecStrCnv")
643
+
644
+ # set to suppress execute string conversion
645
+ def setNoExecStrCnv(self):
646
+ self.set_special_handling("noExecStrCnv")
647
+
648
+ # in-file positional event number
649
+ def inFilePosEvtNum(self):
650
+ return self.check_special_handling("inFilePosEvtNum")
651
+
652
+ # set to use in-file positional event number
653
+ def setInFilePosEvtNum(self):
654
+ self.set_special_handling("inFilePosEvtNum")
655
+
656
+ # register event service files
657
+ def registerEsFiles(self):
658
+ return self.check_special_handling("registerEsFiles")
659
+
660
+ # set to register event service files
661
+ def setRegisterEsFiles(self):
662
+ self.set_special_handling("registerEsFiles")
663
+
664
+ # set background-able flag
665
+ def setBackgroundableFlag(self):
666
+ self.jobExecutionID = 0
667
+ if self.prodSourceLabel not in ["managed", "test"]:
668
+ return
669
+ try:
670
+ if self.inputFileBytes / self.maxWalltime > 5000:
671
+ return
672
+ except Exception:
673
+ return
674
+ try:
675
+ if self.coreCount <= 1:
676
+ return
677
+ except Exception:
678
+ return
679
+ if self.currentPriority > 250:
680
+ return
681
+ self.jobExecutionID = 1
682
+
683
+ # use prefetcher
684
+ def usePrefetcher(self):
685
+ return self.check_special_handling("usePrefetcher")
686
+
687
+ # set to use prefetcher
688
+ def setUsePrefetcher(self):
689
+ self.set_special_handling("usePrefetcher")
690
+
691
+ # use zip to pin
692
+ def useZipToPin(self):
693
+ return self.check_special_handling("useZipToPin")
694
+
695
+ # set to use zip to pin
696
+ def setUseZipToPin(self):
697
+ self.set_special_handling("useZipToPin")
698
+
699
+ # use secrets
700
+ def use_secrets(self):
701
+ return self.check_special_handling("useSecrets")
702
+
703
+ # set to use secrets
704
+ def set_use_secrets(self):
705
+ self.set_special_handling("useSecrets")
706
+
707
+ # not discard events
708
+ def notDiscardEvents(self):
709
+ return self.check_special_handling("notDiscardEvents")
710
+
711
+ # set not to discard events
712
+ def setNotDiscardEvents(self):
713
+ self.set_special_handling("notDiscardEvents")
714
+
715
+ # all events are done
716
+ def allOkEvents(self):
717
+ return self.check_special_handling("allOkEvents")
718
+
719
+ # set all events are done
720
+ def setAllOkEvents(self):
721
+ self.set_special_handling("allOkEvents")
722
+
723
+ # set scout job flag
724
+ def setScoutJobFlag(self):
725
+ self.set_special_handling("scoutJob")
726
+
727
+ # check if scout job
728
+ def isScoutJob(self):
729
+ return self.check_special_handling("scoutJob")
730
+
731
+ # decrement attemptNr of events only when failed
732
+ def decAttOnFailedES(self):
733
+ return self.check_special_handling("decAttOnFailedES")
734
+
735
+ # set to decrement attemptNr of events only when failed
736
+ def setDecAttOnFailedES(self):
737
+ self.set_special_handling("decAttOnFailedES")
738
+
739
+ # set fake flag to ignore in monigoring
740
+ def setFakeJobToIgnore(self):
741
+ self.set_special_handling("fakeJobToIgnore")
742
+
743
+ # remove fake flag to ignore in monigoring
744
+ def removeFakeJobToIgnore(self):
745
+ if self.specialHandling is not None:
746
+ items = self.specialHandling.split(",")
747
+ else:
748
+ items = []
749
+ if self._tagForSH["fakeJobToIgnore"] in items:
750
+ items.remove(self._tagForSH["fakeJobToIgnore"])
751
+ self.specialHandling = ",".join(items)
752
+
753
+ # set task attribute
754
+ def set_task_attribute(self, key, value):
755
+ if not isinstance(self.metadata, list):
756
+ self.metadata = [None, None]
757
+ if len(self.metadata) != 3:
758
+ self.metadata.append({})
759
+ self.metadata[2][key] = value
760
+
761
+ # get task attribute
762
+ def get_task_attribute(self, key):
763
+ try:
764
+ return self.metadata[2][key]
765
+ except Exception:
766
+ return None
767
+
768
+ # set input prestaging
769
+ def setInputPrestaging(self):
770
+ self.set_special_handling("inputPrestaging")
771
+
772
+ # use input prestaging
773
+ def useInputPrestaging(self):
774
+ return self.check_special_handling("inputPrestaging")
775
+
776
+ # to a dictionary
777
+ def to_dict_advanced(self):
778
+ ret = {}
779
+
780
+ # Get the job attributes
781
+ for a in self._attributes:
782
+ v = getattr(self, a)
783
+ if v == "NULL":
784
+ v = None
785
+ ret[a] = v
786
+
787
+ # Add the files explicitly, as they are not included under _attributes
788
+ a = "Files"
789
+ files = getattr(self, a)
790
+ file_stats = []
791
+ for f in files:
792
+ file_stats.append(f.to_dict())
793
+ ret[a] = file_stats
794
+
795
+ return ret
796
+
797
+ def to_dict(self):
798
+ ret = {}
799
+ for a in self._attributes:
800
+ v = getattr(self, a)
801
+ if isinstance(v, datetime.datetime):
802
+ v = str(v)
803
+ elif v == "NULL":
804
+ v = None
805
+ ret[a] = v
806
+ return ret
807
+
808
+ # check if HPO workflow flag
809
+ def is_hpo_workflow(self):
810
+ return self.check_special_handling("hpoWorkflow")
811
+
812
+ # set HPO workflow flag
813
+ def set_hpo_workflow(self):
814
+ self.set_special_handling("hpoWorkflow")
815
+
816
+ # check if looping check is disabled
817
+ def is_no_looping_check(self):
818
+ return self.check_special_handling("noLoopingCheck")
819
+
820
+ # disable looping check
821
+ def disable_looping_check(self):
822
+ self.set_special_handling("noLoopingCheck")
823
+
824
+ # check if encode job parameters
825
+ def to_encode_job_params(self):
826
+ return self.check_special_handling("encJobParams")
827
+
828
+ # encode job parameters
829
+ def set_encode_job_params(self):
830
+ self.set_special_handling("encJobParams")
831
+
832
+ # check if debug mode
833
+ def is_debug_mode(self):
834
+ if self.specialHandling is not None:
835
+ items = self.specialHandling.split(",")
836
+ return self._tagForSH["debugMode"] in items or "debug" in items
837
+ return False
838
+
839
+ # set debug mode
840
+ def set_debug_mode(self):
841
+ self.set_special_handling("debugMode")
842
+
843
+ # set push status changes
844
+ def set_push_status_changes(self):
845
+ self.set_special_handling("pushStatusChanges")
846
+
847
+ # check if to push status changes
848
+ def push_status_changes(self):
849
+ return push_status_changes(self.specialHandling)
850
+
851
+ # set push job
852
+ def set_push_job(self):
853
+ self.set_special_handling("pushJob")
854
+
855
+ # check if to push job
856
+ def is_push_job(self):
857
+ return self.check_special_handling("pushJob")
858
+
859
+ # set on-site merging
860
+ def set_on_site_merging(self):
861
+ self.set_special_handling("onSiteMerging")
862
+
863
+ # check if on-site merging
864
+ def is_on_site_merging(self):
865
+ return self.check_special_handling("onSiteMerging")
866
+
867
+ # get RAM for retry
868
+ def get_ram_for_retry(self):
869
+ if self.specialHandling is not None:
870
+ for tmpItem in self.specialHandling.split(","):
871
+ if tmpItem.startswith(f"{self._tagForSH['retryRam']}:"):
872
+ return int(tmpItem.split(":")[-1])
873
+ return None
874
+
875
+ # set RAM for retry
876
+ def set_ram_for_retry(self, val):
877
+ if self.specialHandling:
878
+ items = self.specialHandling.split(",")
879
+ else:
880
+ items = []
881
+ # remove old value
882
+ newItems = []
883
+ for tmpItem in items:
884
+ if tmpItem.startswith(f"{self._tagForSH['retryRam']}:"):
885
+ continue
886
+ newItems.append(tmpItem)
887
+ newItems.append(f"{self._tagForSH['retryRam']}:{val}")
888
+ self.specialHandling = ",".join(newItems)
889
+
890
+ # dump to json-serializable
891
+ def dump_to_json_serializable(self):
892
+ job_state = self.__getstate__()
893
+ file_state_list = []
894
+ for file_spec in job_state[-1]:
895
+ file_stat = file_spec.dump_to_json_serializable()
896
+ file_state_list.append(file_stat)
897
+ job_state = job_state[:-1]
898
+ # append files
899
+ job_state.append(file_state_list)
900
+ return job_state
901
+
902
+ # load from json-serializable
903
+ def load_from_json_serializable(self, job_state):
904
+ # initialize with empty file list
905
+ self.__setstate__(job_state[:-1] + [[]])
906
+ # add files
907
+ for file_stat in job_state[-1]:
908
+ file_spec = FileSpec()
909
+ file_spec.__setstate__(file_stat)
910
+ self.addFile(file_spec)
911
+
912
+ def load_from_dict(self, job_dict):
913
+ # Extract job attributes (excluding files)
914
+ job_attrs = []
915
+ for slot in self.__slots__:
916
+ if slot == "Files": # skip files here
917
+ continue
918
+ job_attrs.append(job_dict.get(slot, None))
919
+
920
+ # Initialize with empty file list
921
+ self.__setstate__(job_attrs + [[]])
922
+
923
+ # Add files
924
+ for file_data in job_dict.get("Files", []):
925
+ file_spec = FileSpec()
926
+ file_spec.__setstate__([file_data.get(s, None) for s in file_spec.__slots__])
927
+ self.addFile(file_spec)
928
+
929
+ # set input and output file types
930
+ def set_input_output_file_types(self) -> None:
931
+ """
932
+ Set input and output file types based on the input and output file names
933
+ """
934
+ in_types = set()
935
+ out_types = set()
936
+ for tmp_file in self.Files:
937
+ # ignore DBRelease/lib.tgz files
938
+ if tmp_file.dataset.startswith("ddo") or tmp_file.lfn.endswith(".lib.tgz"):
939
+ continue
940
+ # extract type while ignoring user/group/groupXY
941
+ tmp_items = tmp_file.dataset.split(".")
942
+ if (
943
+ len(tmp_items) > 4
944
+ and (not tmp_items[0] in ["", "NULL", "user", "group", "hc_test"])
945
+ and (not tmp_items[0].startswith("group"))
946
+ and not tmp_file.dataset.startswith("panda.um.")
947
+ ):
948
+ tmp_type = tmp_items[4]
949
+ else:
950
+ continue
951
+ if tmp_file.type == "input":
952
+ in_types.add(tmp_type)
953
+ elif tmp_file.type == "output":
954
+ out_types.add(tmp_type)
955
+ # set types
956
+ if in_types:
957
+ in_types = sorted(list(in_types))
958
+ self.inputFileType = ",".join(in_types)[: self._limitLength["inputFileType"]]
959
+ if out_types:
960
+ out_types = sorted(list(out_types))
961
+ self.outputFileType = ",".join(out_types)[: self._limitLength["outputFileType"]]
962
+
963
+ # set task queued time
964
+ def set_task_queued_time(self, queued_time):
965
+ """
966
+ Set task queued time in job metrics. Skip if queued_time is None
967
+
968
+ :param queued_time: task queued time in seconds since epoch
969
+ """
970
+ if queued_time is None:
971
+ return
972
+ if self.specialHandling in [None, "", "NULL"]:
973
+ items = []
974
+ else:
975
+ items = self.specialHandling.split(",")
976
+ items.append(f"{self._tagForSH['taskQueuedTime']}={queued_time}")
977
+ self.specialHandling = ",".join(items)
978
+
979
+
980
+ # utils
981
+
982
+
983
+ # check if to push status changes without class instance
984
+ def push_status_changes(special_handling):
985
+ if special_handling is not None:
986
+ items = special_handling.split(",")
987
+ return JobSpec._tagForSH["pushStatusChanges"] in items
988
+ return False
989
+
990
+
991
+ # get task queued time
992
+ def get_task_queued_time(special_handling):
993
+ """
994
+ Get task queued time from job metrics
995
+
996
+ :param special_handling: special handling string
997
+ :return: task queued time. None if unset
998
+ """
999
+ try:
1000
+ if special_handling is not None:
1001
+ for item in special_handling.split(","):
1002
+ if item.startswith(f"{JobSpec._tagForSH['taskQueuedTime']}="):
1003
+ return datetime.datetime.fromtimestamp(float(item.split("=")[-1]), datetime.timezone.utc)
1004
+ except Exception:
1005
+ pass
1006
+ return None