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,552 @@
1
+ import re
2
+ import sys
3
+ import time
4
+ import traceback
5
+ from re import error as ReError
6
+
7
+ from pandacommon.pandalogger.LogWrapper import LogWrapper
8
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
9
+
10
+ _logger = PandaLogger().getLogger("RetrialModule")
11
+
12
+ NO_RETRY = "no_retry"
13
+ INCREASE_MEM = "increase_memory"
14
+ LIMIT_RETRY = "limit_retry"
15
+ INCREASE_CPU = "increase_cputime"
16
+ INCREASE_MEM_XTIMES = "increase_memory_xtimes"
17
+ REDUCE_INPUT_PER_JOB = "reduce_input_per_job"
18
+
19
+ SYSTEM_ERROR_CLASS = "system"
20
+ NO_ERROR_CLASS = "unknown"
21
+
22
+
23
+ def timeit(method):
24
+ """
25
+ Decorator function to time the execution time of any given method. Use as decorator.
26
+ """
27
+
28
+ def timed(*args, **kwargs):
29
+ tmp_log = LogWrapper(_logger, f"timed {method.__name__!r} ({args!r}, {kwargs!r})")
30
+ tmp_log.debug(f"Start")
31
+
32
+ ts = time.time()
33
+ result = method(*args, **kwargs)
34
+ te = time.time()
35
+
36
+ tmp_log.debug(f"Took {te - ts:.2f} sec")
37
+ return result
38
+
39
+ return timed
40
+
41
+
42
+ def safe_match(pattern, message):
43
+ """
44
+ Wrapper around re.search with simple exception handling
45
+ """
46
+ tmp_log = LogWrapper(_logger, f"safe_match")
47
+
48
+ matches = False
49
+ try:
50
+ matches = re.match(pattern, message, flags=re.DOTALL)
51
+ except ReError:
52
+ tmp_log.error(f"Regexp matching excepted. \nPattern: {pattern} \nString: {message}")
53
+ finally:
54
+ return matches
55
+
56
+
57
+ def conditions_apply(
58
+ errordiag_job,
59
+ architecture_job,
60
+ release_job,
61
+ wqid_job,
62
+ errordiag_rule,
63
+ architecture_rule,
64
+ release_rule,
65
+ wqid_rule,
66
+ ):
67
+ """
68
+ Checks that the error regexp, architecture, release and work queue of rule and job match,
69
+ only in case the attributes are defined for the rule
70
+ """
71
+ tmp_log = LogWrapper(_logger, f"conditions_apply")
72
+ tmp_log.debug(f"Start {locals()}")
73
+ if (
74
+ (errordiag_rule and not safe_match(errordiag_rule, errordiag_job))
75
+ or (architecture_rule and architecture_rule != architecture_job)
76
+ or (release_rule and release_rule != release_job)
77
+ or (wqid_rule and wqid_rule != wqid_job)
78
+ ):
79
+ tmp_log.debug("Leaving: False")
80
+ return False
81
+
82
+ _logger.debug("Leaving: True")
83
+ return True
84
+
85
+
86
+ def compare_strictness(rule1, rule2):
87
+ """
88
+ Return 1 if rule1 is stricter, 0 if equal, -1 if rule2 is stricter
89
+ """
90
+ tmp_log = LogWrapper(_logger, f"compare_strictness")
91
+ tmp_log.debug("Start")
92
+ rule1_weight = 0
93
+ if rule1["architecture"]:
94
+ rule1_weight += 1
95
+ if rule1["release"]:
96
+ rule1_weight += 1
97
+ if rule1["wqid"]:
98
+ rule1_weight += 1
99
+
100
+ rule2_weight = 0
101
+ if rule2["architecture"]:
102
+ rule2_weight += 1
103
+ if rule2["release"]:
104
+ rule2_weight += 1
105
+ if rule2["wqid"]:
106
+ rule2_weight += 1
107
+
108
+ if rule1_weight > rule2_weight:
109
+ return 1
110
+ elif rule1_weight < rule2_weight:
111
+ return -1
112
+ else:
113
+ return 0
114
+
115
+
116
+ def preprocess_rules(rules, error_diag_job, release_job, architecture_job, wqid_job):
117
+ """
118
+ Do some preliminary validation of the applicable rules.
119
+ - Duplicate rules, (action=limit_retry, maxAttempt=5) vs (action=limit_retry, maxAttempt=7, release=X):
120
+ resolve to the most specific rule, in our example (action=limit_retry, maxAttempt=7, release=X)
121
+ - Inconsistent rules, e.g. (action=limit_retry, maxAttempt=5) vs (action=limit_retry, maxAttempt=7):
122
+ resolve into the strictest rule, in our example (limit_retry = 5)
123
+ - Bad intended rules, e.g. (action=limit_retry, maxAttempt=5) vs (action=limit_retry, maxAttempt=7, release=X):
124
+ """
125
+ tmp_log = LogWrapper(_logger, f"preprocess_rules")
126
+ tmp_log.debug("Start")
127
+ filtered_rules = []
128
+ limit_retry_rule = {}
129
+ try:
130
+ # See if there is a INCREASE_MEM rule.
131
+ # The effect of INCREASE_MEM rules is the same, so take the first one that appears
132
+ for rule in rules:
133
+ if rule["action"] != INCREASE_MEM or not conditions_apply(
134
+ error_diag_job,
135
+ architecture_job,
136
+ release_job,
137
+ wqid_job,
138
+ rule["error_diag"],
139
+ rule["architecture"],
140
+ rule["release"],
141
+ rule["wqid"],
142
+ ):
143
+ continue
144
+ else:
145
+ filtered_rules.append(rule)
146
+ break
147
+
148
+ # See if there is a INCREASE_MEM_XTIMES rule.
149
+ # The effect of INCREASE_MEM_XTIMES rules is the same, so take the first one that appears
150
+ for rule in rules:
151
+ if rule["action"] != INCREASE_MEM_XTIMES or not conditions_apply(
152
+ error_diag_job,
153
+ architecture_job,
154
+ release_job,
155
+ wqid_job,
156
+ rule["error_diag"],
157
+ rule["architecture"],
158
+ rule["release"],
159
+ rule["wqid"],
160
+ ):
161
+ continue
162
+ else:
163
+ filtered_rules.append(rule)
164
+ break
165
+
166
+ # See if there is a INCREASE_CPU rule. The effect of INCREASE_CPU rules is the same, so take the first one that appears
167
+ for rule in rules:
168
+ if rule["action"] != INCREASE_CPU or not conditions_apply(
169
+ error_diag_job,
170
+ architecture_job,
171
+ release_job,
172
+ wqid_job,
173
+ rule["error_diag"],
174
+ rule["architecture"],
175
+ rule["release"],
176
+ rule["wqid"],
177
+ ):
178
+ continue
179
+ else:
180
+ filtered_rules.append(rule)
181
+ break
182
+
183
+ # See if there is a REDUCE_INPUT_PER_JOB rule.
184
+ for rule in rules:
185
+ if rule["action"] != REDUCE_INPUT_PER_JOB or not conditions_apply(
186
+ error_diag_job,
187
+ architecture_job,
188
+ release_job,
189
+ wqid_job,
190
+ rule["error_diag"],
191
+ rule["architecture"],
192
+ rule["release"],
193
+ rule["wqid"],
194
+ ):
195
+ continue
196
+ else:
197
+ filtered_rules.append(rule)
198
+ break
199
+
200
+ # See if there is a LIMIT_RETRY or NO_RETRY rule (they are handled together in this context). Take the narrowest rule, in case of draw take the strictest conditions
201
+ for rule in rules:
202
+ if rule["action"] not in (LIMIT_RETRY, NO_RETRY) or not conditions_apply(
203
+ error_diag_job,
204
+ architecture_job,
205
+ release_job,
206
+ wqid_job,
207
+ rule["error_diag"],
208
+ rule["architecture"],
209
+ rule["release"],
210
+ rule["wqid"],
211
+ ):
212
+ continue
213
+ elif not limit_retry_rule:
214
+ limit_retry_rule = rule
215
+ else:
216
+ comparison = compare_strictness(rule, limit_retry_rule)
217
+ if comparison == 1:
218
+ limit_retry_rule = rule
219
+ elif comparison == 0:
220
+ limit_retry_rule["params"]["maxAttempt"] = min(
221
+ limit_retry_rule["params"]["maxAttempt"],
222
+ rule["params"]["maxAttempt"],
223
+ )
224
+ elif comparison == -1:
225
+ pass
226
+ except KeyError:
227
+ tmp_log.error(f"Rules are not properly defined. Rules: {rules}")
228
+
229
+ if limit_retry_rule:
230
+ filtered_rules.append(limit_retry_rule)
231
+
232
+ tmp_log.debug("Done")
233
+ return filtered_rules
234
+
235
+
236
+ @timeit
237
+ def apply_retrial_rules(task_buffer, job, errors, attemptNr):
238
+ """
239
+ Get rules from DB and applies them to a failed job. Actions can be:
240
+ - flag the job so it is not retried again (error code is a final state and retrying will not help)
241
+ - limit the number of retries
242
+ - increase the memory of a job if it failed because of insufficient memory
243
+ """
244
+ job_id = job.PandaID
245
+
246
+ _logger.debug(f"Entered apply_retrial_rules for PandaID={job_id}, errors={errors}, attemptNr={attemptNr}")
247
+
248
+ retrial_rules = task_buffer.getRetrialRules()
249
+ _logger.debug("Back from getRetrialRules")
250
+ if not retrial_rules:
251
+ return
252
+
253
+ try:
254
+ acted_on_job = False
255
+ for error in errors:
256
+ # in case of multiple errors for a job (e.g. pilot error + exe error) we will only apply one action
257
+ if acted_on_job:
258
+ break
259
+
260
+ error_source = error["source"]
261
+ error_code = error["error_code"]
262
+ error_diag = error["error_diag"]
263
+
264
+ try:
265
+ error_code = int(error_code)
266
+ except ValueError:
267
+ if error_code != "NULL":
268
+ _logger.error(f"Error code ({error_code}) can not be casted to int")
269
+ continue
270
+ try:
271
+ rule = retrial_rules[error_source][error_code]
272
+ except KeyError as e:
273
+ _logger.debug(f"Retry rule does not apply for jobID {job_id}, attemptNr {attemptNr}, failed with {errors}. (Exception {e})")
274
+ continue
275
+
276
+ applicable_rules = preprocess_rules(rule, error_diag, job.AtlasRelease, job.cmtConfig, job.workQueue_ID)
277
+ _logger.debug(f"Applicable rules for PandaID={job_id}: {applicable_rules}")
278
+ for rule in applicable_rules:
279
+ try:
280
+ error_id = rule["error_id"]
281
+ error_diag_rule = rule["error_diag"]
282
+ action = rule["action"]
283
+ parameters = rule["params"]
284
+ architecture = rule["architecture"] # cmtconfig
285
+ release = rule["release"] # transHome
286
+ wqid = rule["wqid"] # work queue ID
287
+ active = rule["active"] # If False, don't apply rule, only log
288
+
289
+ _logger.debug(
290
+ "error_diag_rule {0}, action {1}, parameters {2}, architecture {3}, release {4}, wqid {5}, active {6}".format(
291
+ error_diag_rule,
292
+ action,
293
+ parameters,
294
+ architecture,
295
+ release,
296
+ wqid,
297
+ active,
298
+ )
299
+ )
300
+
301
+ _logger.debug(f"Processing rule {rule} for jobID {job_id}, error_source {error_source}, error_code {error_code}, attemptNr {attemptNr}")
302
+ if not conditions_apply(
303
+ error_diag,
304
+ job.cmtConfig,
305
+ job.AtlasRelease,
306
+ job.workQueue_ID,
307
+ error_diag_rule,
308
+ architecture,
309
+ release,
310
+ wqid,
311
+ ):
312
+ _logger.debug(
313
+ f"Skipped rule {rule}. cmtConfig ({architecture} : {job.cmtConfig}) or Release ({release} : {job.AtlasRelease}) did NOT match"
314
+ )
315
+ continue
316
+
317
+ if action == NO_RETRY:
318
+ if active:
319
+ task_buffer.setNoRetry(job_id, job.jediTaskID, job.Files)
320
+ message = (
321
+ f"action=setNoRetry for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
322
+ f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
323
+ f"Error/action active={active} error_id={error_id} )"
324
+ )
325
+ acted_on_job = True
326
+ _logger.info(message)
327
+
328
+ elif action == LIMIT_RETRY:
329
+ try:
330
+ if active:
331
+ task_buffer.setMaxAttempt(
332
+ job_id,
333
+ job.jediTaskID,
334
+ job.Files,
335
+ int(parameters["maxAttempt"]),
336
+ )
337
+ message = (
338
+ f"action=setMaxAttempt for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} maxAttempt={int(parameters['maxAttempt'])} "
339
+ f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
340
+ f"Error/action active={active} error_id={error_id} )"
341
+ )
342
+ acted_on_job = True
343
+ _logger.info(message)
344
+ except (KeyError, ValueError):
345
+ _logger.error(f"Inconsistent definition of limit_retry rule - maxAttempt not defined. parameters: {parameters}")
346
+
347
+ elif action == INCREASE_MEM:
348
+ try:
349
+ if active:
350
+ task_buffer.increaseRamLimitJobJEDI(job, job.minRamCount, job.jediTaskID)
351
+ message = (
352
+ f"action=increaseRAMLimit for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
353
+ f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
354
+ f"Error/action active={active} error_id={error_id} )"
355
+ )
356
+ acted_on_job = True
357
+ _logger.info(message)
358
+ except Exception:
359
+ error_type, error_value = sys.exc_info()[:2]
360
+ _logger.error(f"Failed to increase RAM limit : {error_type} {error_value}")
361
+
362
+ elif action == INCREASE_MEM_XTIMES:
363
+ try:
364
+ if active:
365
+ task_buffer.increaseRamLimitJobJEDI_xtimes(job, job.minRamCount, job.jediTaskID, attemptNr)
366
+ message = (
367
+ f"action=increaseRAMLimit_xtimes for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
368
+ f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
369
+ f"Error/action active={active} error_id={error_id} )"
370
+ )
371
+ acted_on_job = True
372
+ _logger.info(message)
373
+ except Exception:
374
+ error_type, error_value = sys.exc_info()[:2]
375
+ _logger.error(f"Failed to increase RAM xtimes limit : {error_type} {error_value}")
376
+
377
+ elif action == INCREASE_CPU:
378
+ new_cpu_time = None
379
+ try:
380
+ # update the task CPU time based on the failed job
381
+ if active:
382
+ new_cpu_time, new_cpu_time_unit = task_buffer.initialize_cpu_time_task(
383
+ job_id, job.jediTaskID, job.computingSite, job.Files, active
384
+ )
385
+
386
+ message = (
387
+ f"action=increaseCpuTime triggered CPU time increase based on failed job PandaID={job_id} "
388
+ f"jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} (active={active} ), new_cpu_time={new_cpu_time} new_cpu_time_unit={new_cpu_time_unit}. "
389
+ f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
390
+ f"Error/action active={active} error_id={error_id} )"
391
+ )
392
+ _logger.info(message)
393
+ except Exception:
394
+ error_type, error_value = sys.exc_info()[:2]
395
+ _logger.error(f"Failed to increase CPU-Time based on failed jobs: {error_type} {error_value}")
396
+
397
+ # if the CPU time was not increased based on one failed job, we request recalculation of task parameters
398
+ if not new_cpu_time:
399
+ try:
400
+ # request recalculation of task parameters and see if it applied
401
+ applied = False
402
+
403
+ if active:
404
+ rowcount = task_buffer.requestTaskParameterRecalculation(job.jediTaskID)
405
+ else:
406
+ rowcount = 0
407
+
408
+ if rowcount:
409
+ applied = True
410
+
411
+ message = (
412
+ f"action=increaseCpuTime requested recalculation of task parameters for PandaID={job_id} "
413
+ f"jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} (active={active} ), applied={applied}. "
414
+ f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
415
+ f"Error/action active={active} error_id={error_id} )"
416
+ )
417
+
418
+ acted_on_job = True
419
+ _logger.info(message)
420
+ except Exception:
421
+ error_type, error_value = sys.exc_info()[:2]
422
+ _logger.error(f"Failed to increase CPU-Time : {error_type} {error_value}")
423
+
424
+ elif action == REDUCE_INPUT_PER_JOB:
425
+ try:
426
+ applied = False
427
+ if active:
428
+ applied = task_buffer.reduce_input_per_job(
429
+ job.PandaID, job.jediTaskID, job.attemptNr, parameters.get("excluded_rules"), parameters.get("steps")
430
+ )
431
+ message = (
432
+ f"action=reduceInputPerJob for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} applied={applied} "
433
+ f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_code}. "
434
+ f"Error/action active={active} error_id={error_id} )"
435
+ )
436
+ acted_on_job = True
437
+ _logger.info(message)
438
+ except Exception as e:
439
+ _logger.error(f"Failed to reduce input per job : {e} {traceback.format_exc()}")
440
+ _logger.error(traceback.format_exc())
441
+
442
+ _logger.debug(f"Finished rule {rule} for PandaID={job_id} error_source={error_source} error_code={error_code} attemptNr={attemptNr}")
443
+
444
+ except KeyError:
445
+ _logger.error(f"Rule was missing some field(s). Rule: {rule}")
446
+
447
+ except KeyError as e:
448
+ _logger.debug(f"No retrial rules to apply for jobID {job_id}, attemptNr {attemptNr}, failed with {errors}. (Exception {e})")
449
+
450
+
451
+ def get_job_error_details(job_spec):
452
+ # Possible error types
453
+ error_sources = ["pilotError", "exeError", "supError", "ddmError", "brokerageError", "jobDispatcherError", "taskBufferError"]
454
+ job_id = job_spec.PandaID
455
+ job_errors = []
456
+
457
+ tmp_log = LogWrapper(_logger, f"get_job_error_details PandaID={job_id}")
458
+
459
+ tmp_log.debug(f"Starting for status {job_spec.jobStatus}")
460
+
461
+ # Get the error codes and messages that are set for the job
462
+ for source in error_sources:
463
+ error_code = getattr(job_spec, source + "Code", None) # 1099
464
+ error_diag = getattr(job_spec, source + "Diag", None) # "Test error message"
465
+ error_source = source + "Code" # pilotErrorCode
466
+ if error_code:
467
+ job_errors.append((error_code, error_diag, error_source))
468
+
469
+ if job_errors:
470
+ tmp_log.debug(f"Job has following error codes: {job_errors}")
471
+ else:
472
+ tmp_log.debug("Job has no error codes")
473
+
474
+ return job_errors
475
+
476
+
477
+ def classify_error(task_buffer, job_id, job_errors):
478
+ # Get the confirmed error classification rules
479
+ tmp_log = LogWrapper(_logger, f"classify_error PandaID={job_id}")
480
+
481
+ # Query the error classification rules from the database
482
+ sql = "SELECT id, error_source, error_code, error_diag, error_class, active FROM ATLAS_PANDA.ERROR_CLASSIFICATION"
483
+ var_map = []
484
+ status, rules = task_buffer.querySQLS(sql, var_map)
485
+ if not rules:
486
+ tmp_log.debug(f"No error classification rules defined in the database")
487
+ return None
488
+
489
+ # Iterate job errors and rules to find a match
490
+ for job_error in job_errors:
491
+ err_code, err_diag, err_source = job_error
492
+ for rule in rules:
493
+ rule_id, rule_source, rule_code, rule_diag, rule_class, rule_active = rule
494
+
495
+ if (
496
+ (rule_source and err_source is not None and rule_source == err_source)
497
+ and (rule_code and err_code is not None and rule_code == err_code)
498
+ and (rule_diag and err_diag is not None and safe_match(rule_diag, err_diag))
499
+ ):
500
+ active = rule_active == "Y"
501
+ tmp_log.debug(f"Job classified with rule {rule_id}: ({err_source}, {err_code}, {err_diag}) as {rule_class} (active: {active})")
502
+ return rule_id, rule_source, rule_code, rule_diag, rule_class, active
503
+
504
+ tmp_log.debug(f"No matching rule found")
505
+ return None
506
+
507
+
508
+ @timeit
509
+ def apply_error_classification_logic(task_buffer, job):
510
+ tmp_log = LogWrapper(_logger, f"apply_error_classification_logic")
511
+
512
+ # Find the error source and getting the code, diag, and source
513
+ job_errors = get_job_error_details(job)
514
+
515
+ # Classify the error
516
+ ret = classify_error(task_buffer, job.PandaID, job_errors)
517
+ if not ret:
518
+ return
519
+
520
+ # Unpack the classification
521
+ rule_id, rule_source, rule_code, rule_diag, rule_class, active = ret
522
+
523
+ # System errors should not count towards the user's max attempt. We increase the max attempt, since we can't repeat attempt numbers
524
+ if rule_class == SYSTEM_ERROR_CLASS:
525
+ # Structure the message for logstash parsing and monitoring.
526
+ # We are using a large offset in the rule IDs in the database to avoid overlapping IDs with the retry module
527
+ message = (
528
+ f"action=increase_max_failure for PandaID={job.PandaID} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
529
+ f"( ErrorSource={rule_source} ErrorCode={rule_code} ErrorDiag: {rule_diag}. "
530
+ f"Error/action active={active} error_id={rule_id} )"
531
+ )
532
+ tmp_log.info(message)
533
+
534
+ # Apply the rule only for active errors
535
+ if active:
536
+ task_buffer.increase_max_failure(job.PandaID, job.jediTaskID, job.Files)
537
+
538
+
539
+ def job_failure_postprocessing(task_buffer, job_id, errors, attempt_number):
540
+ """
541
+ Entry point for job failure post-processing. This includes applying the retry rules and error classification logic.
542
+ """
543
+ # get the job spec from the ID
544
+ job = task_buffer.peekJobs([job_id], fromDefined=False, fromArchived=True, fromWaiting=False)[0]
545
+ if not job:
546
+ return
547
+
548
+ # Run the retry module on the job
549
+ apply_retrial_rules(task_buffer, job, errors, attempt_number)
550
+
551
+ # Apply any logic related to error classification
552
+ apply_error_classification_logic(task_buffer, job)