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,178 @@
1
+ import sys
2
+
3
+ from pandaserver.dataservice import DataServiceUtils
4
+ from pandaserver.taskbuffer import EventServiceUtils
5
+
6
+ from . import AtlasPostProcessorUtils
7
+ from .PostProcessorBase import PostProcessorBase
8
+
9
+
10
+ # post processor for ATLAS production
11
+ class AtlasProdPostProcessor(PostProcessorBase):
12
+ # constructor
13
+ def __init__(self, taskBufferIF, ddmIF):
14
+ PostProcessorBase.__init__(self, taskBufferIF, ddmIF)
15
+
16
+ # main
17
+ def doPostProcess(self, taskSpec, tmpLog):
18
+ # pre-check
19
+ try:
20
+ tmpStat = self.doPreCheck(taskSpec, tmpLog)
21
+ if tmpStat:
22
+ return self.SC_SUCCEEDED
23
+ except Exception:
24
+ errtype, errvalue = sys.exc_info()[:2]
25
+ tmpLog.error(f"doPreCheck failed with {errtype.__name__}:{errvalue}")
26
+ return self.SC_FATAL
27
+ # get DDM I/F
28
+ ddmIF = self.ddmIF.getInterface(taskSpec.vo)
29
+ # loop over all datasets
30
+ for datasetSpec in taskSpec.datasetSpecList:
31
+ # skip pseudo output datasets
32
+ if datasetSpec.type in ["output"] and datasetSpec.isPseudo():
33
+ continue
34
+ try:
35
+ # remove wrong files
36
+ if datasetSpec.type in ["output"]:
37
+ # get successful files
38
+ okFiles = self.taskBufferIF.getSuccessfulFiles_JEDI(datasetSpec.jediTaskID, datasetSpec.datasetID)
39
+ if okFiles is None:
40
+ tmpLog.warning(f"failed to get successful files for {datasetSpec.datasetName}")
41
+ return self.SC_FAILED
42
+ # get files in dataset
43
+ ddmFiles = ddmIF.getFilesInDataset(datasetSpec.datasetName, skipDuplicate=False, ignoreUnknown=True)
44
+ tmpLog.debug(
45
+ f"datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName} has {len(okFiles)} files in DB, {len(ddmFiles)} files in DDM"
46
+ )
47
+ # check all files
48
+ toDelete = []
49
+ for tmpGUID, attMap in ddmFiles.items():
50
+ if attMap["lfn"] not in okFiles:
51
+ did = {"scope": attMap["scope"], "name": attMap["lfn"]}
52
+ toDelete.append(did)
53
+ tmpLog.debug(f"delete {attMap['lfn']} from {datasetSpec.datasetName}")
54
+ # delete
55
+ if toDelete != []:
56
+ ddmIF.deleteFilesFromDataset(datasetSpec.datasetName, toDelete)
57
+ except Exception:
58
+ errtype, errvalue = sys.exc_info()[:2]
59
+ tmpLog.warning(f"failed to remove wrong files with {errtype.__name__}:{errvalue}")
60
+ return self.SC_FAILED
61
+ try:
62
+ # freeze output and log datasets
63
+ if datasetSpec.type in ["output", "log", "trn_log"]:
64
+ tmpLog.info(f"freezing datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
65
+ ddmIF.freezeDataset(datasetSpec.datasetName, ignoreUnknown=True)
66
+ except Exception:
67
+ errtype, errvalue = sys.exc_info()[:2]
68
+ tmpLog.warning(f"failed to freeze datasets with {errtype.__name__}:{errvalue}")
69
+ return self.SC_FAILED
70
+ try:
71
+ # delete transient datasets
72
+ if datasetSpec.type in ["trn_output"]:
73
+ tmpLog.debug(f"deleting datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
74
+ retStr = ddmIF.deleteDataset(datasetSpec.datasetName, False, ignoreUnknown=True)
75
+ tmpLog.info(retStr)
76
+ except Exception:
77
+ errtype, errvalue = sys.exc_info()[:2]
78
+ tmpLog.warning(f"failed to delete datasets with {errtype.__name__}:{errvalue}")
79
+ # check duplication
80
+ if self.getFinalTaskStatus(taskSpec) in ["finished", "done"] and taskSpec.gshare != "Test":
81
+ nDup = self.taskBufferIF.checkDuplication_JEDI(taskSpec.jediTaskID)
82
+ tmpLog.debug(f"checked duplication with {nDup}")
83
+ if nDup is not None and nDup > 0:
84
+ errStr = f"paused since {nDup} duplication found"
85
+ taskSpec.oldStatus = self.getFinalTaskStatus(taskSpec)
86
+ taskSpec.status = "paused"
87
+ taskSpec.setErrDiag(errStr)
88
+ tmpLog.debug(errStr)
89
+ # delete ES datasets
90
+ if taskSpec.registerEsFiles():
91
+ try:
92
+ targetName = EventServiceUtils.getEsDatasetName(taskSpec.jediTaskID)
93
+ tmpLog.debug(f"deleting ES dataset name={targetName}")
94
+ retStr = ddmIF.deleteDataset(targetName, False, ignoreUnknown=True)
95
+ tmpLog.debug(retStr)
96
+ except Exception:
97
+ errtype, errvalue = sys.exc_info()[:2]
98
+ tmpLog.warning(f"failed to delete ES dataset with {errtype.__name__}:{errvalue}")
99
+ try:
100
+ AtlasPostProcessorUtils.send_notification(self.taskBufferIF, ddmIF, taskSpec, tmpLog)
101
+ except Exception as e:
102
+ tmpLog.error(f"failed to talk to external system with {str(e)}")
103
+ return self.SC_FAILED
104
+ try:
105
+ self.doBasicPostProcess(taskSpec, tmpLog)
106
+ except Exception:
107
+ errtype, errvalue = sys.exc_info()[:2]
108
+ tmpLog.error(f"doBasicPostProcess failed with {errtype.__name__}:{errvalue}")
109
+ return self.SC_FATAL
110
+ return self.SC_SUCCEEDED
111
+
112
+ # final procedure
113
+ def doFinalProcedure(self, taskSpec, tmpLog):
114
+ tmpLog.info(f"final procedure for status={taskSpec.status} processingType={taskSpec.processingType}")
115
+ if taskSpec.status in ["done", "finished"] or (taskSpec.status == "paused" and taskSpec.oldStatus in ["done", "finished"]):
116
+ trnLifeTime = 14 * 24 * 60 * 60
117
+ trnLifeTimeMerge = 40 * 24 * 60 * 60
118
+ ddmIF = self.ddmIF.getInterface(taskSpec.vo)
119
+ # set lifetime to transient datasets
120
+ metaData = {"lifetime": trnLifeTime}
121
+ datasetTypeListI = set()
122
+ datasetTypeListO = set()
123
+ for datasetSpec in taskSpec.datasetSpecList:
124
+ if datasetSpec.type in ["log", "output"]:
125
+ if datasetSpec.getTransient() is True:
126
+ tmpLog.debug(f"set metadata={str(metaData)} to datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
127
+ for metadataName, metadaValue in metaData.items():
128
+ ddmIF.setDatasetMetadata(datasetSpec.datasetName, metadataName, metadaValue)
129
+ # collect dataset types
130
+ datasetType = DataServiceUtils.getDatasetType(datasetSpec.datasetName)
131
+ if datasetType not in ["", None]:
132
+ if datasetSpec.type == "input":
133
+ datasetTypeListI.add(datasetType)
134
+ elif datasetSpec.type == "output":
135
+ datasetTypeListO.add(datasetType)
136
+ # set lifetime to parent transient datasets
137
+ if taskSpec.processingType in ["merge"] and (
138
+ taskSpec.status == "done"
139
+ or (taskSpec.status == "finished" and self.getFinalTaskStatus(taskSpec, checkParent=False) == "done")
140
+ or (
141
+ taskSpec.status == "paused"
142
+ and (taskSpec.oldStatus == "done" or (taskSpec.oldStatus == "finished" and self.getFinalTaskStatus(taskSpec, checkParent=False) == "done"))
143
+ )
144
+ ):
145
+ # get parent task
146
+ if taskSpec.parent_tid not in [None, taskSpec.jediTaskID]:
147
+ # get parent
148
+ tmpStat, parentTaskSpec = self.taskBufferIF.getTaskDatasetsWithID_JEDI(taskSpec.parent_tid, None, False)
149
+ if tmpStat and parentTaskSpec is not None:
150
+ # set lifetime to parent datasets if they are transient
151
+ for datasetSpec in parentTaskSpec.datasetSpecList:
152
+ if datasetSpec.type in ["output"]:
153
+ # check dataset type
154
+ datasetType = DataServiceUtils.getDatasetType(datasetSpec.datasetName)
155
+ if datasetType not in datasetTypeListI or datasetType not in datasetTypeListO:
156
+ continue
157
+ metaData = {"lifetime": trnLifeTimeMerge}
158
+ tmpMetadata = ddmIF.getDatasetMetaData(datasetSpec.datasetName)
159
+ if tmpMetadata["transient"] is True:
160
+ tmpLog.debug(
161
+ "set metadata={0} to parent jediTaskID={1}:datasetID={2}:Name={3}".format(
162
+ str(metaData), taskSpec.parent_tid, datasetSpec.datasetID, datasetSpec.datasetName
163
+ )
164
+ )
165
+ for metadataName, metadaValue in metaData.items():
166
+ ddmIF.setDatasetMetadata(datasetSpec.datasetName, metadataName, metadaValue)
167
+ # set lifetime to failed datasets
168
+ if taskSpec.status in ["failed", "broken", "aborted"]:
169
+ trnLifeTime = 30 * 24 * 60 * 60
170
+ ddmIF = self.ddmIF.getInterface(taskSpec.vo)
171
+ # only log datasets
172
+ metaData = {"lifetime": trnLifeTime}
173
+ for datasetSpec in taskSpec.datasetSpecList:
174
+ if datasetSpec.type in ["log"]:
175
+ tmpLog.debug(f"set metadata={str(metaData)} to failed datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
176
+ for metadataName, metadaValue in metaData.items():
177
+ ddmIF.setDatasetMetadata(datasetSpec.datasetName, metadataName, metadaValue)
178
+ return self.SC_SUCCEEDED
@@ -0,0 +1,35 @@
1
+ from .PostProcessorBase import PostProcessorBase
2
+
3
+
4
+ # post processor for general purpose
5
+ class GenPostProcessor(PostProcessorBase):
6
+ # constructor
7
+ def __init__(self, taskBufferIF, ddmIF):
8
+ PostProcessorBase.__init__(self, taskBufferIF, ddmIF)
9
+ self.failOnZeroOkFile = True
10
+
11
+ # main
12
+ def doPostProcess(self, taskSpec, tmpLog):
13
+ try:
14
+ # get DDM I/F
15
+ ddmIF = self.ddmIF.getInterface(taskSpec.vo, taskSpec.cloud)
16
+ # skip if DDM I/F is inactive
17
+ if not ddmIF:
18
+ tmpLog.info("skip due to inactive DDM I/F")
19
+ else:
20
+ # loop over all datasets
21
+ for datasetSpec in taskSpec.datasetSpecList:
22
+ # only output and log datasets
23
+ if datasetSpec.type not in ["log", "output"]:
24
+ continue
25
+ tmpLog.info(f"freezing datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
26
+ ddmIF.freezeDataset(datasetSpec.datasetName, ignoreUnknown=True)
27
+ except Exception as e:
28
+ tmpLog.warning(f"failed to freeze datasets with {str(e)}")
29
+ return self.SC_FAILED
30
+ try:
31
+ self.doBasicPostProcess(taskSpec, tmpLog)
32
+ except Exception as e:
33
+ tmpLog.error(f"doBasicPostProcess failed with {str(e)}")
34
+ return self.SC_FATAL
35
+ return self.SC_SUCCEEDED
@@ -0,0 +1,185 @@
1
+ jedi_task_plain = """
2
+ Subject: JEDI notification for TaskID:{jedi_task_id} ({n_succeeded}/{n_total} {msg_succeeded})
3
+
4
+ Summary of TaskID:{jedi_task_id}
5
+
6
+ Created : {creation_time} (UTC)
7
+ Ended : {end_time} (UTC)
8
+
9
+ Final Status : {task_status}
10
+
11
+ Total Number of {input_str} : {n_total}
12
+ Succeeded : {n_succeeded}
13
+ Failed : {n_failed}
14
+ {cancelled_str} : {n_cancelled}
15
+
16
+
17
+ Error Dialog : {error_dialog}
18
+
19
+ {dataset_summary}
20
+
21
+ Parameters : {command}
22
+
23
+
24
+ PandaMonURL : https://bigpanda.cern.ch/task/{jedi_task_id}/
25
+
26
+ Please use the smiley interface on this link to provide feedback on the task execution.
27
+
28
+ Estimated carbon footprint for the task
29
+ Succeeded : {carbon_succeeded}
30
+ Failed : {carbon_failed}
31
+ Cancelled : {carbon_cancelled}
32
+ _________________
33
+ Total : {carbon_total}
34
+ (More details on estimation: https://panda-wms.readthedocs.io/en/latest/advanced/carbon_footprint.html)
35
+
36
+
37
+ Report Panda problems of any sort to
38
+
39
+ the eGroup for help request
40
+ hn-atlas-dist-analysis-help@cern.ch
41
+
42
+ the Discourse forum for distributed computing help
43
+ https://atlas-talk.web.cern.ch/c/distributed-computing-help
44
+
45
+ the JIRA portal for software bug
46
+ https://its.cern.ch/jira/browse/ATLASPANDA
47
+ """
48
+
49
+ html_head = """
50
+ <!DOCTYPE html>
51
+ <html>
52
+ <head>
53
+ <style>
54
+ body {{
55
+ font-family: Arial, sans-serif;
56
+ }}
57
+ table {{
58
+ width: 100%;
59
+ border-collapse: collapse;
60
+ margin-top: 10px;
61
+ }}
62
+ th, td {{
63
+ border: 1px solid #dddddd;
64
+ text-align: left;
65
+ padding: 8px;
66
+ }}
67
+ th {{
68
+ background-color: #f2f2f2;
69
+ }}
70
+ .logo {{
71
+ width: 100px; /* Adjust the size as needed */
72
+ }}
73
+ </style>
74
+ <title> {title} </title>
75
+ </head>
76
+ """
77
+
78
+ jedi_task_html_body = """
79
+ <body>
80
+ <img class="logo" src="https://panda-wms.readthedocs.io/en/latest/_static/PanDA-rev-logo-small-200px.jpg" alt="PanDA Logo">
81
+ <h2>Summary of TaskID: <a href="https://bigpanda.cern.ch/task/{jedi_task_id}">{jedi_task_id}</a> </h2>
82
+ <p> 💬 Please use the smiley interface on this link to provide feedback on the task execution.</p>
83
+ <table>
84
+ <tr>
85
+ <th>Detail</th>
86
+ <th>Value</th>
87
+ </tr>
88
+ <tr>
89
+ <td>Created</td>
90
+ <td>{creation_time}</td>
91
+ </tr>
92
+ <tr>
93
+ <td>Ended</td>
94
+ <td>{end_time}</td>
95
+ </tr>
96
+ <tr>
97
+ <td>Final Status</td>
98
+ <td>{task_status}</td>
99
+ </tr>
100
+ </table>
101
+
102
+ <h3>Total Number of {input_str}</h3>
103
+ <table>
104
+ <tr>
105
+ <th>Category</th>
106
+ <th>Count</th>
107
+ </tr>
108
+ <tr>
109
+ <td>Succeeded</td>
110
+ <td>{n_succeeded}</td>
111
+ </tr>
112
+ <tr>
113
+ <td>Failed</td>
114
+ <td>{n_failed}</td>
115
+ </tr>
116
+ <tr>
117
+ <td>{cancelled_str}</td>
118
+ <td>{n_cancelled}</td>
119
+ </tr>
120
+ <tr>
121
+ <td>Total</td>
122
+ <td>{n_total}</td>
123
+ </tr>
124
+ </table>
125
+
126
+ <p><strong>Error Dialog:</strong> {error_dialog}</p>
127
+
128
+ <h3>Datasets</h3>
129
+ <table>
130
+ <tr>
131
+ <td><strong>In</strong></td>
132
+ <td>{datasets_in}</td>
133
+ </tr>
134
+ <tr>
135
+ <td><strong>Out</strong></td>
136
+ <td>{datasets_out}</td>
137
+ </tr>
138
+ <tr>
139
+ <td><strong>Log</strong></td>
140
+ <td>{datasets_log}</td>
141
+ </tr>
142
+ </table>
143
+
144
+ <h3>Parameters</h3>
145
+ <table>
146
+ <tr>
147
+ <td><strong>Command</strong></td>
148
+ <td>{command}</td>
149
+ </tr>
150
+ </table>
151
+
152
+ <h3>Estimated Carbon Footprint for the Task</h3>
153
+ <table>
154
+ <tr>
155
+ <th>Category</th>
156
+ <th>gCO2</th>
157
+ </tr>
158
+ <tr>
159
+ <td>Succeeded</td>
160
+ <td>{carbon_succeeded}</td>
161
+ </tr>
162
+ <tr>
163
+ <td>Failed</td>
164
+ <td>{carbon_failed}</td>
165
+ </tr>
166
+ <tr>
167
+ <td>Cancelled</td>
168
+ <td>{carbon_cancelled}</td>
169
+ </tr>
170
+ <tr>
171
+ <td><strong>Total</strong></td>
172
+ <td><strong>{carbon_total}</strong></td>
173
+ </tr>
174
+ </table>
175
+ <p>More details on estimation: <a href="https://panda-wms.readthedocs.io/en/latest/advanced/carbon_footprint.html">https://panda-wms.readthedocs.io/en/latest/advanced/carbon_footprint.html</a></p>
176
+
177
+ <h3>Panda Problem Reporting</h3>
178
+ <ul>
179
+ <li>The eGroup for help request: <a href="mailto:hn-atlas-dist-analysis-help@cern.ch">hn-atlas-dist-analysis-help@cern.ch</a></li>
180
+ <li>The Discourse forum for distributed computing help: <a href="https://atlas-talk.web.cern.ch/c/distributed-computing-help">https://atlas-talk.web.cern.ch/c/distributed-computing-help</a></li>
181
+ <li>The JIRA portal for software bug: <a href="https://its.cern.ch/jira/browse/ATLASPANDA">https://its.cern.ch/jira/browse/ATLASPANDA</a></li>
182
+ </ul>
183
+ </body>
184
+ </html>
185
+ """
@@ -0,0 +1,269 @@
1
+ import datetime
2
+ import smtplib
3
+ import time
4
+ import uuid
5
+
6
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
7
+
8
+ from pandajedi.jedicore import Interaction
9
+ from pandaserver.config import panda_config
10
+ from pandaserver.taskbuffer import EventServiceUtils
11
+
12
+ # port for SMTP server
13
+ smtpPortList = [25, 587]
14
+
15
+
16
+ # wrapper to patch smtplib.stderr to send debug info to logger
17
+ class StderrLogger(object):
18
+ def __init__(self, tmpLog):
19
+ self.tmpLog = tmpLog
20
+
21
+ def write(self, message):
22
+ message = message.strip()
23
+ if message != "":
24
+ self.tmpLog.debug(message)
25
+
26
+
27
+ # wrapper of SMTP to redirect messages
28
+ class MySMTP(smtplib.SMTP):
29
+ def set_log(self, tmp_log):
30
+ self.tmpLog = tmp_log
31
+ try:
32
+ self.org_stderr = getattr(smtplib, "stderr")
33
+ setattr(smtplib, "stderr", tmp_log)
34
+ except Exception:
35
+ self.org_stderr = None
36
+
37
+ def _print_debug(self, *args):
38
+ self.tmpLog.write(" ".join(map(str, args)))
39
+
40
+ def reset_log(self):
41
+ if self.org_stderr is not None:
42
+ setattr(smtplib, "stderr", self.org_stderr)
43
+
44
+
45
+ # base class for post process
46
+ class PostProcessorBase(object):
47
+ # constructor
48
+ def __init__(self, taskBufferIF, ddmIF):
49
+ self.ddmIF = ddmIF
50
+ self.taskBufferIF = taskBufferIF
51
+ self.msgType = "postprocessor"
52
+ self.failOnZeroOkFile = False
53
+ self.refresh()
54
+
55
+ # refresh
56
+ def refresh(self):
57
+ self.siteMapper = self.taskBufferIF.get_site_mapper()
58
+
59
+ # basic post procedure
60
+ def doBasicPostProcess(self, taskSpec, tmpLog):
61
+ # update task status
62
+ taskSpec.lockedBy = None
63
+ taskSpec.status = self.getFinalTaskStatus(taskSpec)
64
+ if taskSpec.status == "failed":
65
+ # set dialog for preprocess
66
+ if taskSpec.usePrePro() and not taskSpec.checkPreProcessed():
67
+ taskSpec.setErrDiag("Preprocessing step failed", True)
68
+ tmpMsg = f"set task_status={taskSpec.status}"
69
+ tmpLog.info(tmpMsg)
70
+ tmpLog.sendMsg(f"set task_status={taskSpec.status}", self.msgType)
71
+ # update dataset
72
+ for datasetSpec in taskSpec.datasetSpecList:
73
+ if taskSpec.status in ["failed", "broken", "aborted"]:
74
+ datasetSpec.status = "failed"
75
+ else:
76
+ # set dataset status
77
+ if datasetSpec.type in ["output", "log", "lib"]:
78
+ # normal output datasets
79
+ if datasetSpec.nFiles and datasetSpec.nFilesFinished and datasetSpec.nFiles > datasetSpec.nFilesFinished:
80
+ datasetSpec.status = "finished"
81
+ else:
82
+ datasetSpec.status = "done"
83
+ elif datasetSpec.type.startswith("trn_") or datasetSpec.type.startswith("tmpl_"):
84
+ # set done for template or transient datasets
85
+ datasetSpec.status = "done"
86
+ else:
87
+ # not for input
88
+ continue
89
+ # set nFiles
90
+ if datasetSpec.type in ["output", "log", "lib"]:
91
+ datasetSpec.nFiles = datasetSpec.nFilesFinished
92
+ self.taskBufferIF.updateDataset_JEDI(datasetSpec, {"datasetID": datasetSpec.datasetID, "jediTaskID": datasetSpec.jediTaskID})
93
+ # trigger internal dataset cleanup
94
+ self.taskBufferIF.trigger_cleanup_internal_datasets(taskSpec.jediTaskID)
95
+ # end time
96
+ taskSpec.endTime = naive_utcnow()
97
+ # update task
98
+ self.taskBufferIF.updateTask_JEDI(taskSpec, {"jediTaskID": taskSpec.jediTaskID}, updateDEFT=True)
99
+ # kill or kick child tasks
100
+ if taskSpec.status in ["failed", "broken", "aborted"]:
101
+ self.taskBufferIF.killChildTasks_JEDI(taskSpec.jediTaskID, taskSpec.status)
102
+ else:
103
+ self.taskBufferIF.kickChildTasks_JEDI(taskSpec.jediTaskID)
104
+ tmpLog.debug(f"doBasicPostProcess done with taskStatus={taskSpec.status}")
105
+ return
106
+
107
+ # final procedure
108
+ def doFinalProcedure(self, taskSpec, tmpLog):
109
+ return self.SC_SUCCEEDED
110
+
111
+ # send mail
112
+ def sendMail(self, jediTaskID, fromAdd, toAdd, msgBody, nTry, fileBackUp, tmpLog):
113
+ tmpLog.debug(f"sending notification to {toAdd}\n{msgBody}")
114
+ for iTry in range(nTry):
115
+ try:
116
+ stderrLog = StderrLogger(tmpLog)
117
+ smtpPort = smtpPortList[iTry % len(smtpPortList)]
118
+ server = MySMTP(panda_config.emailSMTPsrv, smtpPort)
119
+ server.set_debuglevel(1)
120
+ server.set_log(stderrLog)
121
+ server.ehlo()
122
+ server.starttls()
123
+ out = server.sendmail(fromAdd, toAdd, msgBody)
124
+ tmpLog.debug(str(out))
125
+ server.quit()
126
+ break
127
+ except Exception as e:
128
+ if iTry + 1 < nTry:
129
+ # sleep for retry
130
+ tmpLog.debug(f"sleep {iTry} due to {str(e)}")
131
+ time.sleep(30)
132
+ else:
133
+ tmpLog.error(f"failed to send notification with {str(e)}")
134
+ if fileBackUp:
135
+ # write to file which is processed in add.py
136
+ mailFile = "{0}/jmail_{1}_{2}" % (panda_config.logdir, jediTaskID, uuid.uuid4())
137
+ oMail = open(mailFile, "w")
138
+ oMail.write(str(jediTaskID) + "\n" + toAdd + "\n" + msgBody)
139
+ oMail.close()
140
+ break
141
+ try:
142
+ server.reset_log()
143
+ except Exception:
144
+ pass
145
+
146
+ # return email sender
147
+ def senderAddress(self):
148
+ return panda_config.emailSender
149
+
150
+ # get task completeness
151
+ def getTaskCompleteness(self, taskSpec):
152
+ nFiles = 0
153
+ nFilesFinished = 0
154
+ totalInputEvents = 0
155
+ totalOkEvents = 0
156
+ for datasetSpec in taskSpec.datasetSpecList:
157
+ if datasetSpec.isMasterInput():
158
+ if datasetSpec.status == "removed":
159
+ continue
160
+ nFiles += datasetSpec.nFiles
161
+ nFilesFinished += datasetSpec.nFilesFinished
162
+ try:
163
+ totalInputEvents += datasetSpec.nEvents
164
+ except Exception:
165
+ pass
166
+ try:
167
+ totalOkEvents += datasetSpec.nEventsUsed
168
+ except Exception:
169
+ pass
170
+ # completeness
171
+ if totalInputEvents != 0:
172
+ taskCompleteness = float(totalOkEvents) / float(totalInputEvents) * 1000.0
173
+ elif nFiles != 0:
174
+ taskCompleteness = float(nFilesFinished) / float(nFiles) * 1000.0
175
+ else:
176
+ taskCompleteness = 0
177
+ return nFiles, nFilesFinished, totalInputEvents, totalOkEvents, taskCompleteness
178
+
179
+ # get final task status
180
+ def getFinalTaskStatus(self, taskSpec, checkParent=True, checkGoal=False):
181
+ # count nFiles and nEvents
182
+ nFiles, nFilesFinished, totalInputEvents, totalOkEvents, taskCompleteness = self.getTaskCompleteness(taskSpec)
183
+ # set new task status
184
+ if taskSpec.status == "tobroken":
185
+ status = "broken"
186
+ elif taskSpec.status == "toabort":
187
+ status = "aborted"
188
+ elif taskSpec.status == "paused":
189
+ status = "paused"
190
+ elif self.failOnZeroOkFile and nFiles == nFilesFinished == 0:
191
+ status = "failed"
192
+ elif nFiles == nFilesFinished:
193
+ # check parent status
194
+ if checkParent and taskSpec.parent_tid not in [None, taskSpec.jediTaskID]:
195
+ parent_status = self.taskBufferIF.getTaskStatus_JEDI(taskSpec.parent_tid)
196
+ if parent_status in ["failed", "broken", "aborted"]:
197
+ status = "failed"
198
+ elif parent_status != "done":
199
+ status = "finished"
200
+ else:
201
+ # check if input is mutable
202
+ inputMutable = False
203
+ for datasetSpec in taskSpec.datasetSpecList:
204
+ if datasetSpec.isMasterInput() and datasetSpec.state == "mutable":
205
+ inputMutable = True
206
+ break
207
+ if inputMutable:
208
+ status = "finished"
209
+ else:
210
+ status = "done"
211
+ else:
212
+ status = "done"
213
+ elif nFilesFinished == 0:
214
+ status = "failed"
215
+ else:
216
+ status = "finished"
217
+ # task goal
218
+ if taskSpec.goal is None:
219
+ taskGoal = 1000
220
+ else:
221
+ taskGoal = taskSpec.goal
222
+ # fail if goal is not reached
223
+ if (
224
+ taskSpec.failGoalUnreached()
225
+ and status == "finished"
226
+ and (not taskSpec.useExhausted() or (taskSpec.useExhausted() and taskSpec.status in ["passed"]))
227
+ ):
228
+ if taskCompleteness < taskGoal:
229
+ status = "failed"
230
+ # HPO tasks always go to finished
231
+ if taskSpec.is_hpo_workflow():
232
+ event_stat = self.taskBufferIF.get_event_statistics(taskSpec.jediTaskID)
233
+ if event_stat is not None and event_stat.get(EventServiceUtils.ST_finished):
234
+ status = "finished"
235
+ # check goal only
236
+ if checkGoal:
237
+ # no goal
238
+ if taskSpec.goal is not None and taskCompleteness >= taskGoal:
239
+ return True
240
+ return False
241
+ # return status
242
+ return status
243
+
244
+ # pre-check
245
+ def doPreCheck(self, taskSpec, tmpLog):
246
+ # send task to exhausted
247
+ if (
248
+ taskSpec.useExhausted()
249
+ and taskSpec.status not in ["passed"]
250
+ and self.getFinalTaskStatus(taskSpec) in ["finished"]
251
+ and not self.getFinalTaskStatus(taskSpec, checkGoal=True)
252
+ ):
253
+ taskSpec.status = "exhausted"
254
+ if self.getFinalTaskStatus(taskSpec, checkParent=False) == "done":
255
+ taskSpec.errorDialog = "exhausted since the parent task was incomplete and didn't reach the goal"
256
+ else:
257
+ taskSpec.errorDialog = "exhausted since the task was incomplete and didn't reach the goal"
258
+ taskSpec.lockedBy = None
259
+ taskSpec.lockedTime = None
260
+ # update task
261
+ tmpLog.info(f"set task_status={taskSpec.status}")
262
+ self.taskBufferIF.updateTask_JEDI(taskSpec, {"jediTaskID": taskSpec.jediTaskID}, updateDEFT=True)
263
+ # kick child tasks
264
+ self.taskBufferIF.kickChildTasks_JEDI(taskSpec.jediTaskID)
265
+ return True
266
+ return False
267
+
268
+
269
+ Interaction.installSC(PostProcessorBase)
File without changes