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,1080 @@
1
+ import copy
2
+ import math
3
+ import random
4
+
5
+ from pandacommon.pandalogger.PandaLogger import PandaLogger
6
+
7
+ from pandaserver.srvcore import CoreUtils
8
+
9
+ logger = PandaLogger().getLogger(__name__.split(".")[-1])
10
+
11
+
12
+ # class for input
13
+ class InputChunk:
14
+ # default output size 1G + 500MB (safety merging)
15
+ defaultOutputSize = 1500 * 1024 * 1024
16
+ # max input size for scouts in MB
17
+ maxInputSizeScouts = 50000
18
+ # max input size for jobs after avalanche in MB
19
+ maxInputSizeAvalanche = 500000
20
+ # max number of input files
21
+ maxTotalNumFiles = 1000
22
+
23
+ def __str__(self):
24
+ sb = []
25
+ for key in self.__dict__:
26
+ sb.append(f"{key}='{self.__dict__[key]}'")
27
+
28
+ return ", ".join(sb)
29
+
30
+ def __repr__(self):
31
+ return self.__str__()
32
+
33
+ # constructor
34
+ def __init__(self, taskSpec, masterDataset=None, secondaryDatasetList=[], ramCount=0):
35
+ # task spec
36
+ self.taskSpec = taskSpec
37
+ # the list of secondary datasets
38
+ if secondaryDatasetList is None:
39
+ self.secondaryDatasetList = []
40
+ else:
41
+ self.secondaryDatasetList = secondaryDatasetList
42
+ # the list of site candidates
43
+ self.siteCandidates = {}
44
+ # the list of site candidates for jumbo jobs
45
+ self.siteCandidatesJumbo = {}
46
+ # the name of master index
47
+ self.masterIndexName = None
48
+ # dataset mapping including indexes of files/events
49
+ self.datasetMap = {}
50
+ # the master dataset
51
+ self.masterDataset = None
52
+ self.addMasterDS(masterDataset)
53
+ # the list of secondary datasets
54
+ self.secondaryDatasetList = []
55
+ for secondaryDS in secondaryDatasetList:
56
+ self.addSecondaryDS(secondaryDS)
57
+ # read in a block
58
+ self.readBlock = None
59
+ # merging
60
+ self.isMerging = False
61
+ # use scout
62
+ self.useScoutFlag = None
63
+ # memory requirements for the inputChunk
64
+ self.ramCount = ramCount
65
+ # flag to set if inputchunk is empty
66
+ self.isEmpty = False
67
+ # flag to use jumbo jobs
68
+ self.useJumbo = None
69
+ # checkpoint of used counters
70
+ self.file_checkpoints = {}
71
+ self.intermediate_file_checkpoints = {}
72
+ # list of bootstrapped sites
73
+ self.bootstrapped = set()
74
+
75
+ # add master dataset
76
+ def addMasterDS(self, masterDataset):
77
+ if masterDataset is not None:
78
+ self.masterDataset = masterDataset
79
+ self.masterIndexName = self.masterDataset.datasetID
80
+ self.datasetMap[self.masterDataset.datasetID] = {"used": 0, "datasetSpec": masterDataset}
81
+
82
+ # add secondary dataset
83
+ def addSecondaryDS(self, secondaryDataset):
84
+ if secondaryDataset not in self.secondaryDatasetList:
85
+ self.secondaryDatasetList.append(secondaryDataset)
86
+ self.datasetMap[secondaryDataset.datasetID] = {"used": 0, "datasetSpec": secondaryDataset}
87
+
88
+ # return list of datasets
89
+ def getDatasets(self, includePseudo=False):
90
+ dataList = []
91
+ if self.masterDataset is not None:
92
+ dataList.append(self.masterDataset)
93
+ dataList += self.secondaryDatasetList
94
+ # ignore pseudo datasets
95
+ if not includePseudo:
96
+ newDataList = []
97
+ for datasetSpec in dataList:
98
+ if not datasetSpec.isPseudo():
99
+ newDataList.append(datasetSpec)
100
+ dataList = newDataList
101
+ return dataList
102
+
103
+ # return dataset with datasetID
104
+ def getDatasetWithID(self, datasetID):
105
+ if datasetID in self.datasetMap:
106
+ return self.datasetMap[datasetID]["datasetSpec"]
107
+ return None
108
+
109
+ # return dataset with datasetName
110
+ def getDatasetWithName(self, datasetName):
111
+ for tmpDatasetID, tmpDatasetVal in self.datasetMap.items():
112
+ if tmpDatasetVal["datasetSpec"].datasetName == datasetName:
113
+ return tmpDatasetVal["datasetSpec"]
114
+ return None
115
+
116
+ # reset used counters
117
+ def resetUsedCounters(self):
118
+ for tmpKey, tmpVal in self.datasetMap.items():
119
+ tmpVal["used"] = 0
120
+
121
+ # checkpoint file usage
122
+ def checkpoint_file_usage(self, intermediate=False):
123
+ """
124
+ checkpoint (intermediate) file usage
125
+ :param intermediate: True to checkpoint intermediate
126
+ :return: None
127
+ """
128
+ checkpoints = self.intermediate_file_checkpoints if intermediate else self.file_checkpoints
129
+ for tmpKey, tmpVal in self.datasetMap.items():
130
+ checkpoints[tmpKey] = tmpVal["used"]
131
+
132
+ # rollback file usage
133
+ def rollback_file_usage(self, intermediate=False):
134
+ """
135
+ rollback file usage to the last (intermediate) checkpoint
136
+ :param intermediate: True to roll back to the intermediate checkpoint
137
+ :return: None
138
+ """
139
+ checkpoints = self.intermediate_file_checkpoints if intermediate else self.file_checkpoints
140
+ for tmpKey, tmpVal in self.datasetMap.items():
141
+ tmpVal["used"] = checkpoints.get(tmpKey, 0)
142
+
143
+ # add site candidates
144
+ def addSiteCandidate(self, siteCandidateSpec):
145
+ self.siteCandidates[siteCandidateSpec.siteName] = siteCandidateSpec
146
+ return
147
+
148
+ # add site candidates
149
+ def addSiteCandidateForJumbo(self, siteCandidateSpec):
150
+ self.siteCandidatesJumbo[siteCandidateSpec.siteName] = siteCandidateSpec
151
+ return
152
+
153
+ # has candidate for jumbo jobs
154
+ def hasCandidatesForJumbo(self):
155
+ return len(self.siteCandidatesJumbo) > 0
156
+
157
+ # get one site candidate randomly
158
+ def getOneSiteCandidate(self, nSubChunks=0, ngSites=None, get_msg=False):
159
+ retSiteCandidate = None
160
+ if ngSites is None:
161
+ ngSites = []
162
+ ngSites = copy.copy(ngSites)
163
+
164
+ # skip sites for distributed master dataset
165
+ dist_str = ""
166
+ if self.masterDataset.isDistributed():
167
+ dist_str = "(distributed"
168
+ datasetUsage = self.datasetMap[self.masterDataset.datasetID]
169
+ ng_for_dist = []
170
+ ok_for_dist = False
171
+ if len(self.masterDataset.Files) > datasetUsage["used"]:
172
+ tmpFileSpec = self.masterDataset.Files[datasetUsage["used"]]
173
+ for siteCandidate in self.siteCandidates.values():
174
+ # skip if the first file is unavailable at the site
175
+ if not siteCandidate.isAvailableFile(tmpFileSpec):
176
+ ng_for_dist.append(siteCandidate.siteName)
177
+ else:
178
+ ok_for_dist = True
179
+ if ok_for_dist:
180
+ ngSites += ng_for_dist
181
+ else:
182
+ dist_str += ": disabled locality constraint since the first file is unnavigable anywhere"
183
+ dist_str += ")"
184
+
185
+ # check if to be bootstrapped
186
+ siteCandidateList = list(self.siteCandidates.values())
187
+ newSiteCandidateList = []
188
+ for siteCandidate in siteCandidateList:
189
+ if siteCandidate.weight == 0 and siteCandidate.siteName not in self.bootstrapped and siteCandidate.siteName not in ngSites:
190
+ newSiteCandidateList.append(siteCandidate)
191
+ if newSiteCandidateList:
192
+ retSiteCandidate = random.choice(newSiteCandidateList)
193
+ self.bootstrapped.add(retSiteCandidate.siteName)
194
+ retMsg = f"to bootstrap: {retSiteCandidate.siteName}"
195
+ else:
196
+ # get total weight
197
+ totalWeight = 0
198
+ nNG = 0
199
+ nOK = 0
200
+ nBoosted = 0
201
+ nFull = 0
202
+ fullStr = ""
203
+ for siteCandidate in siteCandidateList:
204
+ # remove NG sites
205
+ if siteCandidate.siteName in ngSites:
206
+ nNG += 1
207
+ continue
208
+ # already bootstrapped
209
+ if siteCandidate.weight == 0 and siteCandidate.siteName in self.bootstrapped:
210
+ nBoosted += 1
211
+ continue
212
+ # skip incapable
213
+ if not siteCandidate.can_accept_jobs():
214
+ nFull += 1
215
+ fullStr += f"{siteCandidate.siteName}:{siteCandidate.nQueuedJobs}/{siteCandidate.nRunningJobsCap} "
216
+ continue
217
+ totalWeight += siteCandidate.weight
218
+ newSiteCandidateList.append(siteCandidate)
219
+ nOK += 1
220
+ siteCandidateList = newSiteCandidateList
221
+ if fullStr:
222
+ fullStr = f" (skipped {fullStr[:-1]})"
223
+ retMsg = f"OK={nOK} NG={','.join(ngSites) if ngSites else None} {dist_str} n_bootstrapped={len(self.bootstrapped)} n_occupied={nFull}{fullStr}"
224
+ # empty
225
+ if not siteCandidateList:
226
+ if get_msg:
227
+ return None, retMsg
228
+ return None
229
+ # get random number
230
+ rNumber = random.random() * totalWeight
231
+ for siteCandidate in siteCandidateList:
232
+ rNumber -= siteCandidate.weight
233
+ if rNumber <= 0:
234
+ retSiteCandidate = siteCandidate
235
+ break
236
+ # return something as a protection against precision of float
237
+ if retSiteCandidate is None:
238
+ retSiteCandidate = random.choice(siteCandidateList)
239
+ # modify weight
240
+ try:
241
+ if retSiteCandidate.nQueuedJobs is not None and retSiteCandidate.nAssignedJobs is not None:
242
+ oldNumQueued = retSiteCandidate.nQueuedJobs
243
+ retSiteCandidate.nQueuedJobs += nSubChunks
244
+ newNumQueued = retSiteCandidate.nQueuedJobs
245
+ retSiteCandidate.nAssignedJobs += nSubChunks
246
+ siteCandidate.weight = siteCandidate.weight * float(oldNumQueued + 1) / float(newNumQueued + 1)
247
+ except Exception:
248
+ pass
249
+ if get_msg:
250
+ return retSiteCandidate, retMsg
251
+ return retSiteCandidate
252
+
253
+ # get sites for parallel execution
254
+ def getParallelSites(self, nSites, nSubChunks, usedSites):
255
+ newSiteCandidate = self.getOneSiteCandidate(nSubChunks, usedSites)
256
+ if newSiteCandidate is not None:
257
+ usedSites.append(newSiteCandidate.siteName)
258
+ if nSites > len(usedSites):
259
+ return self.getParallelSites(nSites, nSubChunks, usedSites)
260
+ return ",".join(usedSites)
261
+
262
+ # get one site for jumbo jobs
263
+ def getOneSiteCandidateForJumbo(self, ngSites):
264
+ # get total weight
265
+ totalWeight = 0
266
+ weightList = []
267
+ siteCandidateList = list(self.siteCandidatesJumbo.values())
268
+ newSiteCandidateList = []
269
+ for siteCandidate in siteCandidateList:
270
+ # remove NG sites
271
+ if siteCandidate.siteName in ngSites:
272
+ continue
273
+ totalWeight += siteCandidate.weight
274
+ newSiteCandidateList.append(siteCandidate)
275
+ siteCandidateList = newSiteCandidateList
276
+ # empty
277
+ if siteCandidateList == []:
278
+ return None
279
+ # get random number
280
+ rNumber = random.random() * totalWeight
281
+ for siteCandidate in siteCandidateList:
282
+ rNumber -= siteCandidate.weight
283
+ if rNumber <= 0:
284
+ retSiteCandidate = siteCandidate
285
+ break
286
+ # return something as a protection against precision of float
287
+ if retSiteCandidate is None:
288
+ retSiteCandidate = random.choice(siteCandidateList)
289
+ return retSiteCandidate
290
+
291
+ # check if unused files/events remain
292
+ def checkUnused(self):
293
+ # master is undefined
294
+ if self.masterIndexName is None:
295
+ return False
296
+ indexVal = self.datasetMap[self.masterIndexName]
297
+ return indexVal["used"] < len(indexVal["datasetSpec"].Files)
298
+
299
+ # get master used index
300
+ def getMasterUsedIndex(self):
301
+ # master is undefined
302
+ if self.masterIndexName is None:
303
+ return 0
304
+ indexVal = self.datasetMap[self.masterIndexName]
305
+ return indexVal["used"]
306
+
307
+ # get num of files in master
308
+ def getNumFilesInMaster(self):
309
+ # master is undefined
310
+ if self.masterIndexName is None:
311
+ return 0
312
+ indexVal = self.datasetMap[self.masterIndexName]
313
+ return len(indexVal["datasetSpec"].Files)
314
+
315
+ # check if secondary datasets use event ratios
316
+ def useEventRatioForSec(self):
317
+ for datasetSpec in self.secondaryDatasetList:
318
+ if datasetSpec.getEventRatio() is not None:
319
+ return True
320
+ return False
321
+
322
+ # get maximum size of atomic subchunk
323
+ def getMaxAtomSize(self, effectiveSize=False, getNumEvents=False):
324
+ # number of files per job if defined
325
+ if not self.isMerging:
326
+ nFilesPerJob = self.taskSpec.getNumFilesPerJob()
327
+ else:
328
+ nFilesPerJob = self.taskSpec.getNumFilesPerMergeJob()
329
+ nEventsPerJob = None
330
+ if nFilesPerJob is None:
331
+ # number of events per job
332
+ if not self.isMerging:
333
+ nEventsPerJob = self.taskSpec.getNumEventsPerJob()
334
+ else:
335
+ nEventsPerJob = self.taskSpec.getNumEventsPerMergeJob()
336
+ if nEventsPerJob is None:
337
+ nFilesPerJob = 1
338
+ # grouping with boundaryID
339
+ useBoundary = self.taskSpec.useGroupWithBoundaryID()
340
+ # LB
341
+ respectLB = self.taskSpec.respectLumiblock()
342
+ maxAtomSize = 0
343
+ while True:
344
+ if not self.isMerging:
345
+ maxNumFiles = self.taskSpec.getMaxNumFilesPerJob()
346
+ else:
347
+ maxNumFiles = self.taskSpec.getMaxNumFilesPerMergeJob()
348
+ # get one subchunk
349
+ subChunk, _ = self.getSubChunk(
350
+ None, nFilesPerJob=nFilesPerJob, nEventsPerJob=nEventsPerJob, useBoundary=useBoundary, respectLB=respectLB, maxNumFiles=maxNumFiles
351
+ )
352
+ if subChunk is None:
353
+ break
354
+ # get size
355
+ tmpAtomSize = 0
356
+ lfn_set = set()
357
+ for tmpDatasetSpec, tmpFileSpecList in subChunk:
358
+ if (effectiveSize or getNumEvents) and not tmpDatasetSpec.isMaster():
359
+ continue
360
+ for tmpFileSpec in tmpFileSpecList:
361
+ if effectiveSize:
362
+ tmpAtomSize += CoreUtils.getEffectiveFileSize(tmpFileSpec.fsize, tmpFileSpec.startEvent, tmpFileSpec.endEvent, tmpFileSpec.nEvents)
363
+ elif getNumEvents:
364
+ tmpAtomSize += tmpFileSpec.getEffectiveNumEvents()
365
+ else:
366
+ if tmpFileSpec.lfn in lfn_set:
367
+ continue
368
+ lfn_set.add(tmpFileSpec.lfn)
369
+ tmpAtomSize += tmpFileSpec.fsize
370
+ if maxAtomSize < tmpAtomSize:
371
+ maxAtomSize = tmpAtomSize
372
+ # reset counters
373
+ self.resetUsedCounters()
374
+ # return
375
+ return maxAtomSize
376
+
377
+ # use scout
378
+ def useScout(self):
379
+ if self.masterDataset is not None and self.useScoutFlag is not None:
380
+ return self.useScoutFlag
381
+ if self.masterDataset is not None and self.masterDataset.nFiles > self.masterDataset.nFilesToBeUsed:
382
+ return True
383
+ return False
384
+
385
+ # set use scout
386
+ def setUseScout(self, useScoutFlag):
387
+ self.useScoutFlag = useScoutFlag
388
+
389
+ # get preassigned site
390
+ def getPreassignedSite(self):
391
+ if self.masterDataset is not None:
392
+ return self.masterDataset.site
393
+ return None
394
+
395
+ # get max output size
396
+ def getOutSize(self, outSizeMap):
397
+ values = sorted(outSizeMap.values())
398
+ try:
399
+ return values[-1]
400
+ except Exception:
401
+ return 0
402
+
403
+ # get value with unit
404
+ def get_value_str(self, value):
405
+ try:
406
+ return f"{int(math.ceil(value / 1024 / 1024))}MB"
407
+ except Exception:
408
+ return None
409
+
410
+ # get subchunk with a selection criteria
411
+ def getSubChunk(
412
+ self,
413
+ siteName,
414
+ maxNumFiles=None,
415
+ maxSize=None,
416
+ sizeGradients=0,
417
+ sizeIntercepts=0,
418
+ nFilesPerJob=None,
419
+ multiplicand=1,
420
+ walltimeGradient=0,
421
+ maxWalltime=0,
422
+ nEventsPerJob=None,
423
+ useBoundary=None,
424
+ sizeGradientsPerInSize=None,
425
+ maxOutSize=None,
426
+ coreCount=1,
427
+ respectLB=False,
428
+ corePower=None,
429
+ dynNumEvents=False,
430
+ multiplicity=None,
431
+ splitByFields=None,
432
+ tmpLog=None,
433
+ useDirectIO=False,
434
+ maxDiskSize=None,
435
+ enableLog=False,
436
+ no_split=False,
437
+ min_walltime=None,
438
+ max_events=None,
439
+ skip_short_output=False,
440
+ ):
441
+ is_short = False
442
+ # check if there are unused files/events
443
+ if not self.checkUnused():
444
+ return None, is_short
445
+ # protection against unreasonable values
446
+ if nFilesPerJob == 0 or dynNumEvents:
447
+ nFilesPerJob = None
448
+ if nEventsPerJob == 0 or dynNumEvents:
449
+ nEventsPerJob = None
450
+ # set default max number of files
451
+ if maxNumFiles is None:
452
+ maxNumFiles = 200
453
+ # set default max size
454
+ if maxSize is None and nFilesPerJob is None and nEventsPerJob is None:
455
+ # 20 GB at most by default
456
+ maxSize = 20 * 1024 * 1024 * 1024
457
+ # set default output size
458
+ minOutSize = self.defaultOutputSize
459
+ # set default max number of events
460
+ maxNumEvents = None
461
+ # ignore negative walltime gradient
462
+ if walltimeGradient is None or walltimeGradient < 0:
463
+ walltimeGradient = 0
464
+ # overwrite parameters when nFiles/EventsPerJob is used
465
+ if nFilesPerJob is not None and not dynNumEvents:
466
+ maxNumFiles = nFilesPerJob
467
+ if not respectLB:
468
+ multiplicand = nFilesPerJob
469
+ if nEventsPerJob is not None and not dynNumEvents:
470
+ maxNumEvents = nEventsPerJob
471
+ elif max_events and dynNumEvents:
472
+ maxNumEvents = max_events
473
+ # split with boundayID
474
+ splitWithBoundaryID = False
475
+ if useBoundary is not None:
476
+ splitWithBoundaryID = True
477
+ if useBoundary["inSplit"] == 2:
478
+ # unset max values to split only with boundaryID
479
+ maxNumFiles = None
480
+ maxSize = None
481
+ maxWalltime = 0
482
+ maxNumEvents = None
483
+ multiplicand = 1
484
+ # get site when splitting per site
485
+ if siteName is not None:
486
+ siteCandidate = self.siteCandidates[siteName]
487
+ # use event ratios
488
+ useEventRatio = self.useEventRatioForSec()
489
+ # start splitting
490
+ inputNumFiles = 0
491
+ inputNumEvents = 0
492
+ fileSize = 0
493
+ firstLoop = True
494
+ firstMaster = True
495
+ inputFileMap = {}
496
+ expWalltime = 0
497
+ nextStartEvent = None
498
+ boundaryID = None
499
+ newBoundaryID = False
500
+ eventJump = False
501
+ nSecFilesMap = {}
502
+ nSecEventsMap = {}
503
+ numMaster = 0
504
+ masterSize = 0
505
+ outSizeMap = {}
506
+ lumiBlockNr = None
507
+ newLumiBlockNr = False
508
+ siteAvailable = True
509
+ inputFileSet = set()
510
+ fieldStr = None
511
+ diskSize = 0
512
+ totalNumFiles = 0
513
+ dumpStr = ""
514
+ currentLFN = None
515
+ while no_split or (
516
+ (maxNumFiles is None or (not dynNumEvents and inputNumFiles <= maxNumFiles) or (dynNumEvents and len(inputFileSet) <= maxNumFiles))
517
+ and (maxSize is None or (maxSize is not None and fileSize <= maxSize))
518
+ and (maxWalltime is None or maxWalltime <= 0 or expWalltime <= maxWalltime)
519
+ and (maxNumEvents is None or (maxNumEvents is not None and inputNumEvents <= maxNumEvents))
520
+ and (maxOutSize is None or self.getOutSize(outSizeMap) <= maxOutSize)
521
+ and (maxDiskSize is None or diskSize <= maxDiskSize)
522
+ and totalNumFiles <= self.maxTotalNumFiles
523
+ ):
524
+ # get one file (or one file group for MP) from master
525
+ datasetUsage = self.datasetMap[self.masterDataset.datasetID]
526
+ if self.masterDataset.datasetID not in outSizeMap:
527
+ outSizeMap[self.masterDataset.datasetID] = 0
528
+ boundaryIDs = set()
529
+ primaryHasEvents = False
530
+ for tmpFileSpec in self.masterDataset.Files[datasetUsage["used"] : datasetUsage["used"] + multiplicand]:
531
+ # check start event to keep continuity
532
+ if (maxNumEvents is not None or dynNumEvents) and tmpFileSpec.startEvent is not None:
533
+ if nextStartEvent is not None and nextStartEvent != tmpFileSpec.startEvent:
534
+ eventJump = True
535
+ break
536
+ elif nextStartEvent and nextStartEvent == tmpFileSpec.startEvent and currentLFN and currentLFN != tmpFileSpec.lfn:
537
+ eventJump = True
538
+ break
539
+ # check boundaryID
540
+ if splitWithBoundaryID and boundaryID is not None and boundaryID != tmpFileSpec.boundaryID and useBoundary["inSplit"] != 3:
541
+ newBoundaryID = True
542
+ break
543
+ # check LB
544
+ if respectLB and lumiBlockNr is not None and lumiBlockNr != tmpFileSpec.lumiBlockNr:
545
+ newLumiBlockNr = True
546
+ break
547
+ # check field
548
+ if splitByFields is not None:
549
+ tmpFieldStr = tmpFileSpec.extractFieldsStr(splitByFields)
550
+ if fieldStr is None:
551
+ fieldStr = tmpFieldStr
552
+ elif tmpFieldStr != fieldStr:
553
+ newBoundaryID = True
554
+ break
555
+ # check for distributed datasets
556
+ # if self.masterDataset.isDistributed() and siteName is not None and \
557
+ # not siteCandidate.isAvailableFile(tmpFileSpec):
558
+ # siteAvailable = False
559
+ # break
560
+ if self.masterDataset.datasetID not in inputFileMap:
561
+ inputFileMap[self.masterDataset.datasetID] = []
562
+ inputFileMap[self.masterDataset.datasetID].append(tmpFileSpec)
563
+ inputFileSet.add(tmpFileSpec.lfn)
564
+ datasetUsage["used"] += 1
565
+ numMaster += 1
566
+ # get effective file size
567
+ effectiveFsize = CoreUtils.getEffectiveFileSize(tmpFileSpec.fsize, tmpFileSpec.startEvent, tmpFileSpec.endEvent, tmpFileSpec.nEvents)
568
+ # get num of events
569
+ effectiveNumEvents = tmpFileSpec.getEffectiveNumEvents()
570
+ # sum
571
+ inputNumFiles += 1
572
+ totalNumFiles += 1
573
+ if self.taskSpec.outputScaleWithEvents():
574
+ tmpOutSize = int(sizeGradients * effectiveNumEvents)
575
+ fileSize += tmpOutSize
576
+ diskSize += tmpOutSize
577
+ if not dynNumEvents or tmpFileSpec.lfn not in inputFileSet:
578
+ fileSize += int(tmpFileSpec.fsize)
579
+ masterSize += int(tmpFileSpec.fsize)
580
+ if not useDirectIO:
581
+ diskSize += int(tmpFileSpec.fsize)
582
+ outSizeMap[self.masterDataset.datasetID] += int(sizeGradients * effectiveNumEvents)
583
+ else:
584
+ tmpOutSize = int(sizeGradients * effectiveFsize)
585
+ fileSize += tmpOutSize
586
+ diskSize += tmpOutSize
587
+ if not dynNumEvents or tmpFileSpec.lfn not in inputFileSet:
588
+ fileSize += int(tmpFileSpec.fsize)
589
+ masterSize += int(tmpFileSpec.fsize)
590
+ if not useDirectIO:
591
+ diskSize += int(tmpFileSpec.fsize)
592
+ outSizeMap[self.masterDataset.datasetID] += int(sizeGradients * effectiveFsize)
593
+ if sizeGradientsPerInSize is not None:
594
+ tmpOutSize = int(effectiveFsize * sizeGradientsPerInSize)
595
+ fileSize += tmpOutSize
596
+ diskSize += tmpOutSize
597
+ outSizeMap[self.masterDataset.datasetID] += int(effectiveFsize * sizeGradientsPerInSize)
598
+ # sum offset only for the first master
599
+ if firstMaster:
600
+ fileSize += sizeIntercepts
601
+ # walltime
602
+ if self.taskSpec.useHS06():
603
+ if firstMaster:
604
+ expWalltime += self.taskSpec.baseWalltime
605
+ tmpExpWalltime = walltimeGradient * effectiveNumEvents / float(coreCount)
606
+ if corePower not in [None, 0]:
607
+ tmpExpWalltime /= corePower
608
+ if self.taskSpec.cpuEfficiency == 0:
609
+ tmpExpWalltime = 0
610
+ else:
611
+ tmpExpWalltime /= float(self.taskSpec.cpuEfficiency) / 100.0
612
+ if multiplicity is not None:
613
+ tmpExpWalltime /= float(multiplicity)
614
+ expWalltime += int(tmpExpWalltime)
615
+ else:
616
+ tmpExpWalltime = walltimeGradient * effectiveFsize / float(coreCount)
617
+ if multiplicity is not None:
618
+ tmpExpWalltime /= float(multiplicity)
619
+ expWalltime += int(tmpExpWalltime)
620
+ # the number of events
621
+ if (maxNumEvents is not None or useEventRatio or dynNumEvents) and tmpFileSpec.startEvent is not None and tmpFileSpec.endEvent is not None:
622
+ primaryHasEvents = True
623
+ inputNumEvents += tmpFileSpec.endEvent - tmpFileSpec.startEvent + 1
624
+ # set next start event
625
+ nextStartEvent = tmpFileSpec.endEvent + 1
626
+ if nextStartEvent == tmpFileSpec.nEvents:
627
+ nextStartEvent = 0
628
+ currentLFN = tmpFileSpec.lfn
629
+ # boundaryID
630
+ if splitWithBoundaryID:
631
+ boundaryID = tmpFileSpec.boundaryID
632
+ if boundaryID not in boundaryIDs:
633
+ boundaryIDs.add(boundaryID)
634
+ # LB
635
+ if respectLB:
636
+ lumiBlockNr = tmpFileSpec.lumiBlockNr
637
+ firstMaster = False
638
+ # get files from secondaries
639
+ firstSecondary = True
640
+ for datasetSpec in self.secondaryDatasetList:
641
+ if datasetSpec.datasetID not in outSizeMap:
642
+ outSizeMap[datasetSpec.datasetID] = 0
643
+ if datasetSpec.isNoSplit():
644
+ # every job uses dataset without splitting
645
+ if firstLoop:
646
+ datasetUsage = self.datasetMap[datasetSpec.datasetID]
647
+ for tmpFileSpec in datasetSpec.Files:
648
+ if datasetSpec.datasetID not in inputFileMap:
649
+ inputFileMap[datasetSpec.datasetID] = []
650
+ inputFileMap[datasetSpec.datasetID].append(tmpFileSpec)
651
+ # sum
652
+ fileSize += tmpFileSpec.fsize
653
+ if not useDirectIO:
654
+ diskSize += tmpFileSpec.fsize
655
+ if sizeGradientsPerInSize is not None:
656
+ tmpOutSize = tmpFileSpec.fsize * sizeGradientsPerInSize
657
+ fileSize += tmpOutSize
658
+ diskSize += tmpOutSize
659
+ outSizeMap[datasetSpec.datasetID] += tmpFileSpec.fsize * sizeGradientsPerInSize
660
+ datasetUsage["used"] += 1
661
+ totalNumFiles += 1
662
+ else:
663
+ if datasetSpec.datasetID not in nSecFilesMap:
664
+ nSecFilesMap[datasetSpec.datasetID] = 0
665
+ # get number of files to be used for the secondary
666
+ nSecondary = datasetSpec.getNumFilesPerJob()
667
+ if nSecondary is not None and firstLoop is False:
668
+ # read files only in the first bunch when number of files per job is specified
669
+ continue
670
+ if nSecondary is None:
671
+ nSecondary = datasetSpec.getNumMultByRatio(numMaster) - nSecFilesMap[datasetSpec.datasetID]
672
+ if (datasetSpec.getEventRatio() is not None and inputNumEvents > 0) or (splitWithBoundaryID and useBoundary["inSplit"] != 3):
673
+ # set large number to get all associated secondary files
674
+ nSecondary = 10000
675
+ datasetUsage = self.datasetMap[datasetSpec.datasetID]
676
+ # reset nUsed
677
+ if datasetSpec.isReusable() and datasetUsage["used"] + nSecondary > len(datasetSpec.Files):
678
+ datasetUsage["used"] = 0
679
+ for tmpFileSpec in datasetSpec.Files[datasetUsage["used"] : datasetUsage["used"] + nSecondary]:
680
+ # check boundaryID
681
+ if (
682
+ (splitWithBoundaryID or (useBoundary is not None and useBoundary["inSplit"] == 3 and datasetSpec.getRatioToMaster() > 1))
683
+ and boundaryID is not None
684
+ and not (boundaryID == tmpFileSpec.boundaryID or tmpFileSpec.boundaryID in boundaryIDs)
685
+ ):
686
+ break
687
+ # check for distributed datasets
688
+ # if datasetSpec.isDistributed() and siteName is not None and \
689
+ # not siteCandidate.isAvailableFile(tmpFileSpec):
690
+ # break
691
+ # check ratio
692
+ if datasetSpec.datasetID not in nSecEventsMap:
693
+ nSecEventsMap[datasetSpec.datasetID] = 0
694
+ if datasetSpec.getEventRatio() is not None and inputNumEvents > 0:
695
+ if float(nSecEventsMap[datasetSpec.datasetID]) / float(inputNumEvents) >= datasetSpec.getEventRatio():
696
+ break
697
+ if datasetSpec.datasetID not in inputFileMap:
698
+ inputFileMap[datasetSpec.datasetID] = []
699
+ inputFileMap[datasetSpec.datasetID].append(tmpFileSpec)
700
+ # sum
701
+ fileSize += tmpFileSpec.fsize
702
+ if not useDirectIO:
703
+ diskSize += tmpFileSpec.fsize
704
+ if sizeGradientsPerInSize is not None:
705
+ tmpOutSize = tmpFileSpec.fsize * sizeGradientsPerInSize
706
+ fileSize += tmpOutSize
707
+ diskSize += tmpOutSize
708
+ outSizeMap[datasetSpec.datasetID] += tmpFileSpec.fsize * sizeGradientsPerInSize
709
+ datasetUsage["used"] += 1
710
+ nSecFilesMap[datasetSpec.datasetID] += 1
711
+ totalNumFiles += 1
712
+ # the number of events
713
+ if firstSecondary and maxNumEvents is not None and not primaryHasEvents:
714
+ if tmpFileSpec.startEvent is not None and tmpFileSpec.endEvent is not None:
715
+ inputNumEvents += tmpFileSpec.endEvent - tmpFileSpec.startEvent + 1
716
+ elif tmpFileSpec.nEvents is not None:
717
+ inputNumEvents += tmpFileSpec.nEvents
718
+ if tmpFileSpec.nEvents is not None:
719
+ nSecEventsMap[datasetSpec.datasetID] += tmpFileSpec.nEvents
720
+ # use only the first secondary
721
+ firstSecondary = False
722
+ # unset first loop flag
723
+ firstLoop = False
724
+ # check if there are unused files/evets
725
+ if not self.checkUnused():
726
+ dumpStr = "no more files"
727
+ break
728
+ # break if nFilesPerJob is used as multiplicand
729
+ if nFilesPerJob is not None and not respectLB:
730
+ dumpStr = "nFilesPerJob specified"
731
+ break
732
+ # boundaryID is changed
733
+ if newBoundaryID:
734
+ dumpStr = "new BoundaryID"
735
+ break
736
+ # LB is changed
737
+ if newLumiBlockNr:
738
+ dumpStr = "new LB"
739
+ break
740
+ # event jump
741
+ if eventJump:
742
+ dumpStr = "event jump"
743
+ break
744
+ # distributed files are unavailable
745
+ if not siteAvailable:
746
+ dumpStr = "distributed files are unavailable"
747
+ break
748
+ primaryHasEvents = False
749
+ # check master in the next loop
750
+ datasetUsage = self.datasetMap[self.masterDataset.datasetID]
751
+ newInputNumFiles = inputNumFiles
752
+ newInputNumEvents = inputNumEvents
753
+ newFileSize = fileSize
754
+ newExpWalltime = expWalltime
755
+ newNextStartEvent = nextStartEvent
756
+ newNumMaster = numMaster
757
+ terminateFlag = False
758
+ newOutSizeMap = copy.copy(outSizeMap)
759
+ newBoundaryIDs = set()
760
+ newInputFileSet = copy.copy(inputFileSet)
761
+ newDiskSize = diskSize
762
+ new_nSecEventsMap = copy.copy(nSecEventsMap)
763
+ newTotalNumFiles = totalNumFiles
764
+ if self.masterDataset.datasetID not in newOutSizeMap:
765
+ newOutSizeMap[self.masterDataset.datasetID] = 0
766
+ for tmpFileSpec in self.masterDataset.Files[datasetUsage["used"] : datasetUsage["used"] + multiplicand]:
767
+ # check continuity of event
768
+ if maxNumEvents is not None and tmpFileSpec.startEvent is not None and tmpFileSpec.endEvent is not None:
769
+ primaryHasEvents = True
770
+ newInputNumEvents += tmpFileSpec.endEvent - tmpFileSpec.startEvent + 1
771
+ # continuity of event is broken
772
+ if newNextStartEvent is not None and newNextStartEvent != tmpFileSpec.startEvent:
773
+ # no files in the next loop
774
+ if newInputNumFiles == 0:
775
+ dumpStr = "no files with continuous events in the next loop"
776
+ terminateFlag = True
777
+ break
778
+ newNextStartEvent = tmpFileSpec.endEvent + 1
779
+ # check boundary
780
+ if splitWithBoundaryID and boundaryID is not None and boundaryID != tmpFileSpec.boundaryID and useBoundary["inSplit"] != 3:
781
+ # no files in the next loop
782
+ if newInputNumFiles == 0:
783
+ dumpStr = "no files with the same BoundaryID in the next loop"
784
+ terminateFlag = True
785
+ break
786
+ # check LB
787
+ if respectLB and lumiBlockNr is not None and lumiBlockNr != tmpFileSpec.lumiBlockNr:
788
+ # no files in the next loop
789
+ if newInputNumFiles == 0:
790
+ dumpStr = "no files with the same LB in the next loop"
791
+ terminateFlag = True
792
+ break
793
+ # check field
794
+ if splitByFields is not None:
795
+ tmpFieldStr = tmpFileSpec.extractFieldsStr(splitByFields)
796
+ if tmpFieldStr != fieldStr:
797
+ # no files in the next loop
798
+ if newInputNumFiles == 0:
799
+ dumpStr = "no files with the same LFN field in the next loop"
800
+ terminateFlag = True
801
+ break
802
+ # check for distributed datasets
803
+ # if self.masterDataset.isDistributed() and siteName is not None and \
804
+ # not siteCandidate.isAvailableFile(tmpFileSpec):
805
+ # # no files in the next loop
806
+ # if newInputNumFiles == 0:
807
+ # terminateFlag = True
808
+ # break
809
+ # get effective file size
810
+ effectiveFsize = CoreUtils.getEffectiveFileSize(tmpFileSpec.fsize, tmpFileSpec.startEvent, tmpFileSpec.endEvent, tmpFileSpec.nEvents)
811
+ # get num of events
812
+ effectiveNumEvents = tmpFileSpec.getEffectiveNumEvents()
813
+ newInputNumFiles += 1
814
+ newNumMaster += 1
815
+ newTotalNumFiles += 1
816
+ newInputFileSet.add(tmpFileSpec.lfn)
817
+ if self.taskSpec.outputScaleWithEvents():
818
+ tmpOutSize = int(sizeGradients * effectiveNumEvents)
819
+ newFileSize += tmpOutSize
820
+ newDiskSize += tmpOutSize
821
+ if not dynNumEvents or tmpFileSpec.lfn not in inputFileSet:
822
+ newFileSize += int(tmpFileSpec.fsize)
823
+ if not useDirectIO:
824
+ newDiskSize += int(tmpFileSpec.fsize)
825
+ newOutSizeMap[self.masterDataset.datasetID] += int(sizeGradients * effectiveNumEvents)
826
+ else:
827
+ tmpOutSize = int(sizeGradients * effectiveFsize)
828
+ newFileSize += tmpOutSize
829
+ newDiskSize += tmpOutSize
830
+ if not dynNumEvents or tmpFileSpec.lfn not in inputFileSet:
831
+ newFileSize += int(tmpFileSpec.fsize)
832
+ if not useDirectIO:
833
+ newDiskSize += int(tmpFileSpec.fsize)
834
+ newOutSizeMap[self.masterDataset.datasetID] += int(sizeGradients * effectiveFsize)
835
+ if sizeGradientsPerInSize is not None:
836
+ tmpOutSize = int(effectiveFsize * sizeGradientsPerInSize)
837
+ newFileSize += tmpOutSize
838
+ newDiskSize += tmpOutSize
839
+ newOutSizeMap[self.masterDataset.datasetID] += int(effectiveFsize * sizeGradientsPerInSize)
840
+ if self.taskSpec.useHS06():
841
+ tmpExpWalltime = walltimeGradient * effectiveNumEvents / float(coreCount)
842
+ if corePower not in [None, 0]:
843
+ tmpExpWalltime /= corePower
844
+ if self.taskSpec.cpuEfficiency == 0:
845
+ tmpExpWalltime = 0
846
+ else:
847
+ tmpExpWalltime /= float(self.taskSpec.cpuEfficiency) / 100.0
848
+ if multiplicity is not None:
849
+ tmpExpWalltime /= float(multiplicity)
850
+ newExpWalltime += int(tmpExpWalltime)
851
+ else:
852
+ tmpExpWalltime = walltimeGradient * effectiveFsize / float(coreCount)
853
+ if multiplicity is not None:
854
+ tmpExpWalltime /= float(multiplicity)
855
+ newExpWalltime += int(tmpExpWalltime)
856
+ # boundaryID
857
+ if splitWithBoundaryID:
858
+ newBoundaryIDs.add(tmpFileSpec.boundaryID)
859
+ # check secondaries
860
+ firstSecondary = True
861
+ newSecMap = {}
862
+ for datasetSpec in self.secondaryDatasetList:
863
+ if datasetSpec.datasetID not in newOutSizeMap:
864
+ newOutSizeMap[datasetSpec.datasetID] = 0
865
+ if not datasetSpec.isNoSplit() and datasetSpec.getNumFilesPerJob() is None:
866
+ # check boundaryID
867
+ if splitWithBoundaryID and boundaryID is not None and boundaryID != tmpFileSpec.boundaryID and useBoundary["inSplit"] != 3:
868
+ break
869
+ newSecMap.setdefault(datasetSpec.datasetID, {"in_size": 0, "out_size": 0})
870
+ newNumSecondary = datasetSpec.getNumMultByRatio(newNumMaster) - nSecFilesMap[datasetSpec.datasetID]
871
+ if newNumSecondary < 0:
872
+ newNumSecondary = 0
873
+ newSecMap[datasetSpec.datasetID]["nSec"] = newNumSecondary
874
+ newSecMap[datasetSpec.datasetID]["nSecReal"] = 0
875
+ datasetUsage = self.datasetMap[datasetSpec.datasetID]
876
+ for tmpFileSpec in datasetSpec.Files[datasetUsage["used"] : datasetUsage["used"] + newNumSecondary]:
877
+ # check boundaryID
878
+ if (
879
+ splitWithBoundaryID
880
+ and boundaryID is not None
881
+ and boundaryID != tmpFileSpec.boundaryID
882
+ and tmpFileSpec.boundaryID not in boundaryIDs
883
+ and tmpFileSpec.boundaryID not in newBoundaryIDs
884
+ ):
885
+ break
886
+ # check ratio
887
+ if datasetSpec.getEventRatio() is not None and newInputNumEvents > 0:
888
+ if float(new_nSecEventsMap[datasetSpec.datasetID]) / float(newInputNumEvents) >= datasetSpec.getEventRatio():
889
+ break
890
+ newFileSize += tmpFileSpec.fsize
891
+ newSecMap[datasetSpec.datasetID]["in_size"] += tmpFileSpec.fsize
892
+ newSecMap[datasetSpec.datasetID]["nSecReal"] += 1
893
+ newTotalNumFiles += 1
894
+ if not useDirectIO:
895
+ newDiskSize += tmpFileSpec.fsize
896
+ if sizeGradientsPerInSize is not None:
897
+ tmpOutSize = tmpFileSpec.fsize * sizeGradientsPerInSize
898
+ newFileSize += tmpOutSize
899
+ newDiskSize += tmpOutSize
900
+ newOutSizeMap[datasetSpec.datasetID] += tmpOutSize
901
+ newSecMap[datasetSpec.datasetID]["out_size"] += tmpOutSize
902
+ # the number of events
903
+ if firstSecondary and maxNumEvents is not None and not primaryHasEvents:
904
+ if tmpFileSpec.startEvent is not None and tmpFileSpec.endEvent is not None:
905
+ newInputNumEvents += tmpFileSpec.endEvent - tmpFileSpec.startEvent + 1
906
+ elif tmpFileSpec.nEvents is not None:
907
+ newInputNumEvents += tmpFileSpec.nEvents
908
+ if tmpFileSpec.nEvents is not None:
909
+ new_nSecEventsMap[datasetSpec.datasetID] += tmpFileSpec.nEvents
910
+ firstSecondary = False
911
+ # no split:
912
+ if no_split:
913
+ continue
914
+ # termination
915
+ if terminateFlag:
916
+ break
917
+ # check
918
+ newOutSize = self.getOutSize(newOutSizeMap)
919
+ if (
920
+ (maxNumFiles is not None and ((not dynNumEvents and newInputNumFiles > maxNumFiles) or (dynNumEvents and (len(newInputFileSet) > maxNumFiles))))
921
+ or (maxSize is not None and newFileSize > maxSize)
922
+ or (maxSize is not None and newOutSize < minOutSize and maxSize - minOutSize < newFileSize - newOutSize)
923
+ or (maxWalltime is not None and 0 < maxWalltime < newExpWalltime)
924
+ or (maxNumEvents is not None and newInputNumEvents > maxNumEvents)
925
+ or (maxOutSize is not None and self.getOutSize(newOutSizeMap) > maxOutSize)
926
+ or (maxDiskSize is not None and newDiskSize > maxDiskSize)
927
+ or newTotalNumFiles > self.maxTotalNumFiles
928
+ ):
929
+ dumpStr = "check for the next loop. "
930
+ if maxNumFiles is not None and (not dynNumEvents and newInputNumFiles > maxNumFiles):
931
+ dumpStr += f"maxNumFiles exceeds maxNumFiles={maxNumFiles} inputNumFiles={inputNumFiles} newInputNumFiles={newInputNumFiles}. "
932
+ if maxSize is not None and newFileSize > maxSize:
933
+ dumpStr += "maxSize exceeds maxSize={} fileSize={} masterSize={} newFileSize={} newSecMap={}. ".format(
934
+ self.get_value_str(maxSize),
935
+ self.get_value_str(fileSize),
936
+ self.get_value_str(masterSize),
937
+ self.get_value_str(newFileSize),
938
+ str(newSecMap),
939
+ )
940
+ if maxSize is not None and newOutSize < minOutSize and maxSize - minOutSize < newFileSize - newOutSize:
941
+ dumpStr += "maxSize exceeds with outSize maxSize={} minOutSize={} fileSize={} newFileSize={} newOutSize={}. ".format(
942
+ self.get_value_str(maxSize),
943
+ self.get_value_str(minOutSize),
944
+ self.get_value_str(fileSize),
945
+ self.get_value_str(newFileSize),
946
+ self.get_value_str(newOutSize),
947
+ )
948
+ if maxWalltime is not None and 0 < maxWalltime < newExpWalltime:
949
+ dumpStr += f"maxWalltime exceeds maxWalltime={maxWalltime} expWalltime={expWalltime} newExpWalltime={newExpWalltime}. "
950
+ if maxNumEvents is not None and newInputNumEvents > maxNumEvents:
951
+ dumpStr += f"maxNumEvents exceeds maxNumEvents={maxNumEvents} inputNumEvents={inputNumEvents} newInputNumEvents={newInputNumEvents}. "
952
+ if maxOutSize is not None and self.getOutSize(newOutSizeMap) > maxOutSize:
953
+ dumpStr += "maxOutSize exceeds maxOutSize={} getOutSize(newOutSizeMap)={}. ".format(
954
+ self.get_value_str(maxOutSize), self.get_value_str(self.getOutSize(newOutSizeMap))
955
+ )
956
+ if maxDiskSize is not None and newDiskSize > maxDiskSize:
957
+ dumpStr += "maxDiskSize exceeds maxDiskSize={} diskSize={} newDiskSize={}. ".format(
958
+ self.get_value_str(maxDiskSize), self.get_value_str(diskSize), self.get_value_str(newDiskSize)
959
+ )
960
+ if newTotalNumFiles > self.maxTotalNumFiles:
961
+ dumpStr += f"total num of input files exceeds {self.maxTotalNumFiles}. "
962
+ break
963
+ # reset nUsed for repeated datasets
964
+ for tmpDatasetID, datasetUsage in self.datasetMap.items():
965
+ tmpDatasetSpec = datasetUsage["datasetSpec"]
966
+ if tmpDatasetSpec.isRepeated():
967
+ if len(tmpDatasetSpec.Files) > 0:
968
+ datasetUsage["used"] %= len(tmpDatasetSpec.Files)
969
+ # check min walltime
970
+ if dynNumEvents and min_walltime and min_walltime > expWalltime:
971
+ if enableLog and tmpLog:
972
+ tmpLog.debug(f"expected walltime {expWalltime} less than min walltime {min_walltime} at {siteName}")
973
+ return [], is_short
974
+ # make copy to return
975
+ returnList = []
976
+ for tmpDatasetID, inputFileList in inputFileMap.items():
977
+ tmpRetList = []
978
+ for tmpFileSpec in inputFileList:
979
+ # split par site or get atomic subchunk
980
+ if siteName is not None:
981
+ # make copy to individually set locality
982
+ newFileSpec = copy.copy(tmpFileSpec)
983
+ # set locality
984
+ newFileSpec.locality = siteCandidate.getFileLocality(tmpFileSpec)
985
+ if newFileSpec.locality == "remote":
986
+ newFileSpec.sourceName = siteCandidate.remoteSource
987
+ # append
988
+ tmpRetList.append(newFileSpec)
989
+ else:
990
+ # getting atomic subchunk
991
+ tmpRetList.append(tmpFileSpec)
992
+ # add to return map
993
+ tmpDatasetSpec = self.getDatasetWithID(tmpDatasetID)
994
+ returnList.append((tmpDatasetSpec, tmpRetList))
995
+ # dump only problematic splitting
996
+ err_msg = ""
997
+ if returnList:
998
+ if maxNumEvents and inputNumEvents < maxNumEvents:
999
+ is_short = True
1000
+ err_msg = f"not enough events {maxNumEvents}>{inputNumEvents} at {siteName}: numMaster={numMaster}. {dumpStr}"
1001
+ elif nFilesPerJob and inputNumFiles < nFilesPerJob:
1002
+ is_short = True
1003
+ err_msg = f"not enough files {nFilesPerJob}>{inputNumFiles} at {siteName}. {dumpStr}"
1004
+ # return empty to skip input chunks producing output files with insufficient events
1005
+ if is_short:
1006
+ if enableLog and tmpLog:
1007
+ tmpLog.debug(err_msg)
1008
+ if skip_short_output:
1009
+ return None, is_short
1010
+ # return
1011
+ return returnList, is_short
1012
+
1013
+ # check if master is mutable
1014
+ def isMutableMaster(self):
1015
+ if self.masterDataset is not None and self.masterDataset.state == "mutable":
1016
+ return True
1017
+ return False
1018
+
1019
+ # figure out if output will go through express stream
1020
+ def isExpress(self):
1021
+ if self.taskSpec.processingType == "urgent" or self.taskSpec.currentPriority > 1000:
1022
+ return True
1023
+
1024
+ return False
1025
+
1026
+ # get max ramCount
1027
+ def getMaxRamCount(self):
1028
+ if self.isMerging:
1029
+ return max(self.taskSpec.mergeRamCount, self.ramCount, 2000) if self.taskSpec.mergeRamCount else self.ramCount
1030
+ else:
1031
+ return max(self.taskSpec.ramCount, self.ramCount) if self.taskSpec.ramCount else self.ramCount
1032
+
1033
+ # get site candidate
1034
+ def getSiteCandidate(self, name):
1035
+ if name in self.siteCandidates:
1036
+ return self.siteCandidates[name]
1037
+ return None
1038
+
1039
+ # get list of candidate names
1040
+ def get_candidate_names(self):
1041
+ return list(self.siteCandidates.keys())
1042
+
1043
+ # update number of queued jobs
1044
+ def update_n_queue(self, live_counter):
1045
+ sites = []
1046
+ for siteCandidate in self.siteCandidates.values():
1047
+ if live_counter is not None:
1048
+ n = live_counter.get(siteCandidate.siteName)
1049
+ if n > 0:
1050
+ siteCandidate.nQueuedJobs += n
1051
+ sites.append(siteCandidate.siteName)
1052
+ return ",".join(sites)
1053
+
1054
+ # check event continuity
1055
+ def check_event_jump_and_sum(self):
1056
+ nextStartEvent = None
1057
+ eventJump = False
1058
+ totalEvents = 0
1059
+ currentLFN = None
1060
+ maxChunk = 0
1061
+ datasetUsage = self.datasetMap[self.masterDataset.datasetID]
1062
+ for tmpFileSpec in self.masterDataset.Files[datasetUsage["used"] :]:
1063
+ if tmpFileSpec.startEvent is not None:
1064
+ if nextStartEvent is not None and nextStartEvent != tmpFileSpec.startEvent:
1065
+ maxChunk = max(maxChunk, totalEvents)
1066
+ eventJump = True
1067
+ totalEvents = 0
1068
+ elif nextStartEvent and currentLFN != tmpFileSpec.lfn:
1069
+ maxChunk = max(maxChunk, totalEvents)
1070
+ eventJump = True
1071
+ totalEvents = 0
1072
+ nextStartEvent = tmpFileSpec.endEvent + 1
1073
+ if nextStartEvent == tmpFileSpec.nEvents:
1074
+ nextStartEvent = 0
1075
+ totalEvents += tmpFileSpec.endEvent - tmpFileSpec.startEvent + 1
1076
+ elif tmpFileSpec.endEvent:
1077
+ totalEvents += tmpFileSpec.endEvent
1078
+ currentLFN = tmpFileSpec.lfn
1079
+ maxChunk = max(maxChunk, totalEvents)
1080
+ return eventJump, maxChunk