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,79 @@
1
+ import datetime
2
+ import os
3
+ import socket
4
+ import sys
5
+ import time
6
+
7
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
8
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
9
+
10
+ from pandajedi.jediconfig import jedi_config
11
+ from pandajedi.jedicore import Interaction
12
+ from pandajedi.jedicore.FactoryBase import FactoryBase
13
+ from pandajedi.jedicore.MsgWrapper import MsgWrapper
14
+
15
+ from .JediKnight import JediKnight
16
+
17
+ logger = PandaLogger().getLogger(__name__.split(".")[-1])
18
+
19
+
20
+ # worker class for watchdog
21
+ class WatchDog(JediKnight, FactoryBase):
22
+ # constructor
23
+ def __init__(self, commuChannel, taskBufferIF, ddmIF, vos, prodSourceLabels, subStr, period):
24
+ self.vos = self.parseInit(vos)
25
+ self.prodSourceLabels = self.parseInit(prodSourceLabels)
26
+ self.subStr = subStr
27
+ self.period = period
28
+ self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}-dog"
29
+ JediKnight.__init__(self, commuChannel, taskBufferIF, ddmIF, logger)
30
+ FactoryBase.__init__(self, self.vos, self.prodSourceLabels, logger, jedi_config.watchdog.modConfig)
31
+
32
+ # main
33
+ def start(self):
34
+ # start base classes
35
+ JediKnight.start(self)
36
+ FactoryBase.initializeMods(self, self.taskBufferIF, self.ddmIF)
37
+ # go into main loop
38
+ while True:
39
+ startTime = naive_utcnow()
40
+ try:
41
+ # get logger
42
+ tmpLog = MsgWrapper(logger)
43
+ tmpLog.info("start")
44
+ # loop over all vos
45
+ for vo in self.vos:
46
+ # loop over all sourceLabels
47
+ for prodSourceLabel in self.prodSourceLabels:
48
+ # vo/prodSourceLabel specific action
49
+ impl = self.getImpl(vo, prodSourceLabel, subType=self.subStr)
50
+ if impl is not None:
51
+ plugin_name = impl.__class__.__name__
52
+ tmpLog.info(f"pre-action for vo={vo} label={prodSourceLabel} cls={plugin_name}")
53
+ impl.pre_action(tmpLog, vo, prodSourceLabel, self.pid)
54
+ tmpLog.info(f"do action for vo={vo} label={prodSourceLabel} cls={plugin_name}")
55
+ tmpStat = impl.doAction()
56
+ if tmpStat != Interaction.SC_SUCCEEDED:
57
+ tmpLog.error(f"failed to run special action for vo={vo} label={prodSourceLabel} cls={plugin_name}")
58
+ else:
59
+ tmpLog.info(f"done for vo={vo} label={prodSourceLabel} cls={plugin_name}")
60
+ tmpLog.info("done")
61
+ except Exception:
62
+ errtype, errvalue = sys.exc_info()[:2]
63
+ tmpLog.error(f"failed in {self.__class__.__name__}.start() with {errtype.__name__} {errvalue}")
64
+ # sleep if needed
65
+ loopCycle = jedi_config.watchdog.loopCycle if self.period is None else self.period
66
+ timeDelta = naive_utcnow() - startTime
67
+ sleepPeriod = loopCycle - timeDelta.seconds
68
+ if sleepPeriod > 0:
69
+ time.sleep(sleepPeriod)
70
+ # randomize cycle
71
+ self.randomSleep(max_val=loopCycle)
72
+
73
+
74
+ # launch
75
+
76
+
77
+ def launcher(commuChannel, taskBufferIF, ddmIF, vos=None, prodSourceLabels=None, subStr=None, period=None):
78
+ p = WatchDog(commuChannel, taskBufferIF, ddmIF, vos, prodSourceLabels, subStr, period)
79
+ p.start()
File without changes
@@ -0,0 +1,404 @@
1
+ import datetime
2
+ import random
3
+ import re
4
+ import sys
5
+ import time
6
+ import traceback
7
+ from email.mime.multipart import MIMEMultipart
8
+ from email.mime.text import MIMEText
9
+
10
+ from pandacommon.pandautils.PandaUtils import naive_utcnow
11
+
12
+ from pandajedi.jedirefine import RefinerUtils
13
+ from pandaserver.taskbuffer import EventServiceUtils
14
+
15
+ from .MailTemplates import html_head, jedi_task_html_body, jedi_task_plain
16
+ from .PostProcessorBase import PostProcessorBase
17
+
18
+
19
+ def format_weight(weight):
20
+ power = 1000
21
+ n = 0
22
+ power_labels = {0: "gCO2", 1: "kgCO2", 2: "tCO2", 3: "MtCO2", 4: "GtCO2"}
23
+ while weight > power:
24
+ weight /= power
25
+ n += 1
26
+
27
+ weight_str = f"{weight:.2f} {power_labels[n]}"
28
+ return weight_str
29
+
30
+
31
+ # post processor for ATLAS production
32
+ class AtlasAnalPostProcessor(PostProcessorBase):
33
+ # constructor
34
+ def __init__(self, taskBufferIF, ddmIF):
35
+ PostProcessorBase.__init__(self, taskBufferIF, ddmIF)
36
+ self.taskParamMap = None
37
+ self.user_container_lifetime = taskBufferIF.getConfigValue("user_output", "OUTPUT_CONTAINER_LIFETIME", "jedi")
38
+ if not self.user_container_lifetime:
39
+ self.user_container_lifetime = 14
40
+ self.user_container_lifetime *= 24 * 60 * 60
41
+
42
+ # main
43
+ def doPostProcess(self, taskSpec, tmp_logger):
44
+ # freeze datasets
45
+ try:
46
+ # get DDM I/F
47
+ ddmIF = self.ddmIF.getInterface(taskSpec.vo)
48
+ # shuffle datasets
49
+ random.shuffle(taskSpec.datasetSpecList)
50
+ # loop over all datasets
51
+ use_lib = False
52
+ n_ok_lib = 0
53
+ lock_update_time = naive_utcnow()
54
+ done_containers = set()
55
+ for datasetSpec in taskSpec.datasetSpecList:
56
+ # ignore template
57
+ if datasetSpec.type.startswith("tmpl_"):
58
+ continue
59
+ # only output, log or lib datasets
60
+ if not datasetSpec.type.endswith("log") and not datasetSpec.type.endswith("output") and not datasetSpec.type == "lib":
61
+ continue
62
+ # only user group, or panda dataset
63
+ if (
64
+ not datasetSpec.datasetName.startswith("user")
65
+ and not datasetSpec.datasetName.startswith("panda")
66
+ and not datasetSpec.datasetName.startswith("group")
67
+ ):
68
+ continue
69
+ # check if already closed
70
+ dataset_attrs = self.taskBufferIF.getDatasetAttributes_JEDI(datasetSpec.jediTaskID, datasetSpec.datasetID, ["state"])
71
+ if "state" in dataset_attrs and dataset_attrs["state"] == "closed":
72
+ tmp_logger.info(f"skip freezing closed datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
73
+ closed_flag = True
74
+ else:
75
+ closed_flag = False
76
+ # remove wrong files
77
+ if not closed_flag and datasetSpec.type in ["output"]:
78
+ # get successful files
79
+ ok_files = self.taskBufferIF.getSuccessfulFiles_JEDI(datasetSpec.jediTaskID, datasetSpec.datasetID)
80
+ if ok_files is None:
81
+ tmp_logger.warning(f"failed to get successful files for {datasetSpec.datasetName}")
82
+ return self.SC_FAILED
83
+ # get files in dataset
84
+ ddm_files = ddmIF.getFilesInDataset(datasetSpec.datasetName, skipDuplicate=False)
85
+ tmp_logger.debug(
86
+ f"datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName} has {len(ok_files)} files in DB, {len(ddm_files)} files in DDM"
87
+ )
88
+ # check all files
89
+ to_delete = []
90
+ for tmpGUID, attMap in ddm_files.items():
91
+ if attMap["lfn"] not in ok_files:
92
+ did = {"scope": attMap["scope"], "name": attMap["lfn"]}
93
+ to_delete.append(did)
94
+ tmp_logger.debug(f"delete {attMap['lfn']} from {datasetSpec.datasetName}")
95
+ # delete
96
+ if to_delete:
97
+ ddmIF.deleteFilesFromDataset(datasetSpec.datasetName, to_delete)
98
+
99
+ # freeze datasets
100
+ if not closed_flag and not (datasetSpec.type.startswith("trn_") and datasetSpec.type not in ["trn_log"]):
101
+ tmp_logger.debug(f"freeze datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
102
+ ddmIF.freezeDataset(datasetSpec.datasetName, ignoreUnknown=True)
103
+ else:
104
+ if datasetSpec.type.startswith("trn_") and datasetSpec.type not in ["trn_log"]:
105
+ tmp_logger.debug(f"skip freezing transient datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
106
+ # update dataset
107
+ datasetSpec.state = "closed"
108
+ datasetSpec.stateCheckTime = naive_utcnow()
109
+
110
+ # check if build step was succeeded
111
+ if datasetSpec.type == "lib":
112
+ use_lib = True
113
+ else:
114
+ n_ok_lib += 1
115
+ # delete transient or empty datasets
116
+ if not closed_flag:
117
+ empty_only = True
118
+ if datasetSpec.type.startswith("trn_") and datasetSpec.type not in ["trn_log"]:
119
+ empty_only = False
120
+ retStr = ddmIF.deleteDataset(datasetSpec.datasetName, empty_only, ignoreUnknown=True)
121
+ tmp_logger.debug(retStr)
122
+ # extend lifetime
123
+ if datasetSpec.type in ["output"] and datasetSpec.datasetName.startswith("user"):
124
+ tmp_logger.debug(f"extend lifetime datasetID={datasetSpec.datasetID}:Name={datasetSpec.datasetName}")
125
+ ddmIF.updateReplicationRules(
126
+ datasetSpec.datasetName, {"type=.+": {"lifetime": 14 * 24 * 60 * 60}, "(SCRATCH|USER)DISK": {"lifetime": 14 * 24 * 60 * 60}}
127
+ )
128
+ # extend container rules
129
+ if datasetSpec.containerName and datasetSpec.containerName not in done_containers:
130
+ tmp_logger.debug(f"extend lifetime container:Name={datasetSpec.containerName}")
131
+ ddmIF.updateReplicationRules(
132
+ datasetSpec.containerName,
133
+ {"type=.+": {"lifetime": self.user_container_lifetime}, "(SCRATCH|USER)DISK": {"lifetime": self.user_container_lifetime}},
134
+ )
135
+ done_containers.add(datasetSpec.containerName)
136
+ # update dataset in DB
137
+ self.taskBufferIF.updateDatasetAttributes_JEDI(
138
+ datasetSpec.jediTaskID, datasetSpec.datasetID, {"state": datasetSpec.state, "stateCheckTime": datasetSpec.stateCheckTime}
139
+ )
140
+ # update task lock
141
+ if naive_utcnow() - lock_update_time > datetime.timedelta(minutes=5):
142
+ lock_update_time = naive_utcnow()
143
+ # update lock
144
+ self.taskBufferIF.updateTaskLock_JEDI(taskSpec.jediTaskID)
145
+ # dialog
146
+ if use_lib and n_ok_lib == 0:
147
+ taskSpec.setErrDiag("No build jobs succeeded", True)
148
+ except Exception:
149
+ err_type, err_value = sys.exc_info()[:2]
150
+ tmp_logger.warning(f"failed to freeze datasets with {err_type.__name__}:{err_value}")
151
+ ret_val = self.SC_SUCCEEDED
152
+ try:
153
+ self.doBasicPostProcess(taskSpec, tmp_logger)
154
+ except Exception:
155
+ err_type, err_value = sys.exc_info()[:2]
156
+ tmp_logger.error(f"doBasicPostProcess failed with {err_type.__name__}:{err_value}")
157
+ ret_val = self.SC_FATAL
158
+ return ret_val
159
+
160
+ # final procedure
161
+ def doFinalProcedure(self, taskSpec, tmp_logger):
162
+ # check email address
163
+ to_add = self.getEmail(taskSpec.userName, taskSpec.vo, tmp_logger)
164
+
165
+ # calculate carbon footprint for the task
166
+ try:
167
+ carbon_footprint = self.taskBufferIF.get_task_carbon_footprint(taskSpec.jediTaskID, level="global")
168
+ carbon_footprint_redacted = {}
169
+ zero = "0 gCO2"
170
+
171
+ for job_status in ["finished", "failed", "cancelled", "total"]:
172
+ if carbon_footprint and job_status in carbon_footprint:
173
+ carbon_footprint_redacted[job_status] = format_weight(carbon_footprint[job_status])
174
+ else:
175
+ carbon_footprint_redacted[job_status] = zero
176
+ except Exception:
177
+ carbon_footprint_redacted = {}
178
+ err_type, err_value = sys.exc_info()[:2]
179
+ tmp_logger.error(f"failed to calculate task carbon footprint {err_type.__name__}:{err_value}")
180
+
181
+ # read task parameters
182
+ try:
183
+ task_parameters = self.taskBufferIF.getTaskParamsWithID_JEDI(taskSpec.jediTaskID)
184
+ self.taskParamMap = RefinerUtils.decodeJSON(task_parameters)
185
+ except Exception:
186
+ err_type, err_value = sys.exc_info()[:2]
187
+ tmp_logger.error(f"task param conversion from json failed with {err_type.__name__}:{err_value}")
188
+ if to_add is None or (self.taskParamMap is not None and "noEmail" in self.taskParamMap and self.taskParamMap["noEmail"] is True):
189
+ tmp_logger.debug("email notification is suppressed")
190
+ else:
191
+ try:
192
+ # send email notification
193
+ from_add = self.senderAddress()
194
+ html_text, plain_text, subject = self.compose_message(taskSpec, carbon_footprint_redacted)
195
+ msg = MIMEMultipart("alternative")
196
+
197
+ msg["Subject"] = subject
198
+ msg["From"] = from_add
199
+ msg["To"] = to_add
200
+
201
+ # Record the MIME types of both parts - text/plain and text/html.
202
+ part1 = MIMEText(plain_text, "plain")
203
+ part2 = MIMEText(html_text, "html")
204
+
205
+ # Attach parts into message container.
206
+ # According to RFC 2046, the last part of a multipart message, in this case
207
+ # the HTML message, is best and preferred.
208
+ msg.attach(part1)
209
+ msg.attach(part2)
210
+ self.sendMail(taskSpec.jediTaskID, from_add, to_add, msg.as_string(), 3, False, tmp_logger)
211
+ except Exception:
212
+ tmp_logger.error(traceback.format_exc())
213
+ return self.SC_SUCCEEDED
214
+
215
+ # compose mail message
216
+ def compose_message(self, taskSpec, carbon_footprint):
217
+ # summary
218
+ input_datasets = []
219
+ output_datasets = []
220
+ log_datasets = []
221
+ n_total_jobs = 0
222
+ n_succeeded_jobs = 0
223
+ n_failed_jobs = 0
224
+ n_cancelled_jobs = 0
225
+
226
+ if not taskSpec.is_hpo_workflow():
227
+ input_str = "Inputs"
228
+ cancelled_str = "Cancelled "
229
+
230
+ for datasetSpec in taskSpec.datasetSpecList:
231
+ # dataset summary
232
+ if datasetSpec.type == "log":
233
+ if datasetSpec.containerName not in log_datasets:
234
+ log_datasets.append(datasetSpec.containerName)
235
+ elif datasetSpec.type == "input":
236
+ if datasetSpec.containerName not in input_datasets:
237
+ input_datasets.append(datasetSpec.containerName)
238
+ elif datasetSpec.type == "output":
239
+ if datasetSpec.containerName not in output_datasets:
240
+ output_datasets.append(datasetSpec.containerName)
241
+ # process summary
242
+ if datasetSpec.isMasterInput():
243
+ if datasetSpec.status == "removed":
244
+ continue
245
+ try:
246
+ n_total_jobs += datasetSpec.nFiles
247
+ n_succeeded_jobs += datasetSpec.nFilesFinished
248
+ n_failed_jobs += datasetSpec.nFilesFailed
249
+ except Exception:
250
+ pass
251
+ else:
252
+ input_str = "Points"
253
+ cancelled_str = "Unprocessed"
254
+ n_total_jobs = taskSpec.get_total_num_jobs()
255
+ event_stat = self.taskBufferIF.get_event_statistics(taskSpec.jediTaskID)
256
+ if event_stat is not None:
257
+ n_succeeded_jobs = event_stat.get(EventServiceUtils.ST_finished, 0)
258
+ n_failed_jobs = event_stat.get(EventServiceUtils.ST_failed, 0)
259
+ try:
260
+ n_cancelled_jobs = n_total_jobs - n_succeeded_jobs - n_failed_jobs
261
+ except Exception:
262
+ pass
263
+ if n_succeeded_jobs == n_total_jobs:
264
+ msg_succeeded = "All Succeeded"
265
+ else:
266
+ msg_succeeded = "Succeeded"
267
+ input_datasets.sort()
268
+ output_datasets.sort()
269
+ log_datasets.sort()
270
+ dataset_summary = ""
271
+ for tmpDS in input_datasets:
272
+ dataset_summary += f"In : {tmpDS}\n"
273
+ for tmpDS in output_datasets:
274
+ dataset_summary += f"Out : {tmpDS}\n"
275
+ for tmpDS in log_datasets:
276
+ dataset_summary += f"Log : {tmpDS}\n"
277
+ dataset_summary = dataset_summary[:-1]
278
+
279
+ # CLI param
280
+ if "cliParams" in self.taskParamMap:
281
+ cli_parameters = self.taskParamMap["cliParams"]
282
+ else:
283
+ cli_parameters = None
284
+
285
+ # make message
286
+ head = html_head.format(title="Task summary notification")
287
+ body = jedi_task_html_body.format(
288
+ jedi_task_id=taskSpec.jediTaskID,
289
+ creation_time=taskSpec.creationDate,
290
+ end_time=taskSpec.endTime,
291
+ task_status=taskSpec.status,
292
+ error_dialog=self.removeTags(taskSpec.errorDialog),
293
+ command=cli_parameters,
294
+ n_total=n_total_jobs,
295
+ n_succeeded=n_succeeded_jobs,
296
+ n_failed=n_failed_jobs,
297
+ n_cancelled=n_cancelled_jobs,
298
+ carbon_succeeded=carbon_footprint["finished"],
299
+ carbon_failed=carbon_footprint["failed"],
300
+ carbon_cancelled=carbon_footprint["cancelled"],
301
+ carbon_total=carbon_footprint["total"],
302
+ datasets_in=input_datasets,
303
+ datasets_out=output_datasets,
304
+ datasets_log=log_datasets,
305
+ msg_succeeded=msg_succeeded,
306
+ input_str=input_str,
307
+ cancelled_str=cancelled_str,
308
+ )
309
+ message_html = head + body
310
+
311
+ message_plain = jedi_task_plain.format(
312
+ jedi_task_id=taskSpec.jediTaskID,
313
+ creation_time=taskSpec.creationDate,
314
+ end_time=taskSpec.endTime,
315
+ task_status=taskSpec.status,
316
+ error_dialog=self.removeTags(taskSpec.errorDialog),
317
+ command=cli_parameters,
318
+ n_total=n_total_jobs,
319
+ n_succeeded=n_succeeded_jobs,
320
+ n_failed=n_failed_jobs,
321
+ n_cancelled=n_cancelled_jobs,
322
+ carbon_succeeded=carbon_footprint["finished"],
323
+ carbon_failed=carbon_footprint["failed"],
324
+ carbon_cancelled=carbon_footprint["cancelled"],
325
+ carbon_total=carbon_footprint["total"],
326
+ dataset_summary=dataset_summary,
327
+ msg_succeeded=msg_succeeded,
328
+ input_str=input_str,
329
+ cancelled_str=cancelled_str,
330
+ )
331
+
332
+ subject = f"JEDI notification for TaskID:{taskSpec.jediTaskID} ({n_succeeded_jobs}/{n_total_jobs} {msg_succeeded})"
333
+
334
+ # return
335
+ return message_html, message_plain, subject
336
+
337
+ # get email
338
+ def getEmail(self, user_name, vo, tmp_logger):
339
+ # return to suppress mail
340
+ ret_suppressed = None
341
+ # get DN
342
+ tmp_logger.debug(f"getting email for {user_name}")
343
+
344
+ # get email from PANDAMETA DB
345
+ mail_address_db, dn, db_uptime = self.taskBufferIF.getEmailAddr(user_name, withDN=True)
346
+ tmp_logger.debug(f"email from MetaDB : {mail_address_db}")
347
+ # email notification is suppressed
348
+ not_send_mail = False
349
+ if mail_address_db is not None and mail_address_db.startswith("notsend"):
350
+ not_send_mail = True
351
+ # DN is unavailable
352
+ if dn in ["", None]:
353
+ # there will be no email
354
+ tmp_logger.debug("DN is empty")
355
+ else:
356
+ # avoid too frequent lookup
357
+ if db_uptime is not None and naive_utcnow() - db_uptime < datetime.timedelta(hours=1):
358
+ tmp_logger.debug("no lookup")
359
+ if not_send_mail or mail_address_db in [None, ""]:
360
+ return ret_suppressed
361
+ else:
362
+ return mail_address_db.split(":")[-1]
363
+ else:
364
+ # get email from DQ2
365
+ tmp_logger.debug(f"getting email using dq2Info.finger({dn})")
366
+ n_tries = 3
367
+ for iDDMTry in range(n_tries):
368
+ try:
369
+ user_info = self.ddmIF.getInterface(vo).finger(dn)
370
+ mail_address = user_info["email"]
371
+ tmp_logger.debug(f"email from DQ2 : {mail_address}")
372
+ if mail_address is None:
373
+ mail_address = ""
374
+ # make email field to update DB
375
+ mail_addr_to_db = ""
376
+ if not_send_mail:
377
+ mail_addr_to_db += "notsend:"
378
+ mail_addr_to_db += mail_address
379
+ # update database
380
+ tmp_logger.debug(f"update email to {mail_addr_to_db}")
381
+ self.taskBufferIF.setEmailAddr(user_name, mail_addr_to_db)
382
+
383
+ if not_send_mail or mail_address == "":
384
+ return ret_suppressed
385
+ return mail_address
386
+ except Exception:
387
+ if iDDMTry + 1 < n_tries:
388
+ tmp_logger.debug(f"sleep for retry {iDDMTry}/{n_tries}")
389
+ time.sleep(10)
390
+ else:
391
+ err_type, err_value = sys.exc_info()[:2]
392
+ tmp_logger.error(f"{err_type}:{err_value}")
393
+ # not send email
394
+ return ret_suppressed
395
+
396
+ # remove tags
397
+ def removeTags(self, tmp_str):
398
+ try:
399
+ if tmp_str is not None:
400
+ tmp_str = re.sub(">[^<]+<", "><", tmp_str)
401
+ tmp_str = re.sub("<[^<]+>", "", tmp_str)
402
+ except Exception:
403
+ pass
404
+ return tmp_str
@@ -0,0 +1,50 @@
1
+ import copy
2
+
3
+ from pandajedi.jedicore import JediException
4
+ from pandajedi.jedirefine import RefinerUtils
5
+
6
+ try:
7
+ import idds.common.constants
8
+ import idds.common.utils
9
+ from idds.client.client import Client as iDDS_Client
10
+ except ImportError:
11
+ pass
12
+
13
+
14
+ # send notification to external system for additional post-processing
15
+ def send_notification(taskBufferIF, ddmIF, taskSpec, tmpLog):
16
+ # send notification to external system
17
+ try:
18
+ taskParam = taskBufferIF.getTaskParamsWithID_JEDI(taskSpec.jediTaskID)
19
+ taskParamMap = RefinerUtils.decodeJSON(taskParam)
20
+ except Exception as e:
21
+ errStr = f"task param conversion from json failed with {str(e)}"
22
+ raise JediException.ExternalTempError(errStr)
23
+ if "outputPostProcessing" in taskParamMap and "system" in taskParamMap["outputPostProcessing"]:
24
+ if taskParamMap["outputPostProcessing"]["system"] == "idds":
25
+ try:
26
+ c = iDDS_Client(idds.common.utils.get_rest_host())
27
+ if taskParamMap["outputPostProcessing"]["type"] == "active_learning":
28
+ for datasetSpec in taskSpec.datasetSpecList:
29
+ if datasetSpec.type != "output":
30
+ continue
31
+ data = copy.copy(taskParamMap["outputPostProcessing"]["data"])
32
+ tmp_scope, tmp_name = ddmIF.extract_scope(datasetSpec.datasetName)
33
+ data["workload_id"] = taskSpec.jediTaskID
34
+ req = {
35
+ "scope": tmp_scope,
36
+ "name": tmp_name,
37
+ "requester": "panda",
38
+ "request_type": idds.common.constants.RequestType.ActiveLearning,
39
+ "transform_tag": idds.common.constants.RequestType.ActiveLearning.value,
40
+ "status": idds.common.constants.RequestStatus.New,
41
+ "priority": 0,
42
+ "lifetime": 30,
43
+ "request_metadata": data,
44
+ }
45
+ tmpLog.debug(f"req {str(req)}")
46
+ ret = c.add_request(**req)
47
+ tmpLog.debug(f"got requestID={str(ret)}")
48
+ except Exception as e:
49
+ errStr = f"iDDS failed with {str(e)}"
50
+ raise JediException.ExternalTempError(errStr)