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.
- panda_server_source-0.8.0.dist-info/METADATA +55 -0
- panda_server_source-0.8.0.dist-info/RECORD +375 -0
- panda_server_source-0.8.0.dist-info/WHEEL +4 -0
- panda_server_source-0.8.0.dist-info/licenses/LICENSE.txt +202 -0
- pandajedi/__init__.py +0 -0
- pandajedi/jedibrokerage/AtlasAnalJobBroker.py +2343 -0
- pandajedi/jedibrokerage/AtlasBrokerUtils.py +1292 -0
- pandajedi/jedibrokerage/AtlasProdJobBroker.py +2064 -0
- pandajedi/jedibrokerage/AtlasProdTaskBroker.py +629 -0
- pandajedi/jedibrokerage/GenJobBroker.py +381 -0
- pandajedi/jedibrokerage/JobBrokerBase.py +133 -0
- pandajedi/jedibrokerage/TaskBrokerBase.py +15 -0
- pandajedi/jedibrokerage/__init__.py +0 -0
- pandajedi/jediconfig/__init__.py +0 -0
- pandajedi/jediconfig/jedi_config.py +40 -0
- pandajedi/jedicore/FactoryBase.py +182 -0
- pandajedi/jedicore/Interaction.py +433 -0
- pandajedi/jedicore/JediDBProxy.py +56 -0
- pandajedi/jedicore/JediDBProxyPool.py +37 -0
- pandajedi/jedicore/JediDBSchemaInfo.py +17 -0
- pandajedi/jedicore/JediException.py +13 -0
- pandajedi/jedicore/JediTaskBuffer.py +984 -0
- pandajedi/jedicore/JediTaskBufferInterface.py +50 -0
- pandajedi/jedicore/MsgWrapper.py +124 -0
- pandajedi/jedicore/ProcessUtils.py +34 -0
- pandajedi/jedicore/SiteCandidate.py +90 -0
- pandajedi/jedicore/ThreadUtils.py +205 -0
- pandajedi/jedicore/__init__.py +0 -0
- pandajedi/jediddm/AtlasDDMClient.py +1708 -0
- pandajedi/jediddm/DDMClientBase.py +16 -0
- pandajedi/jediddm/DDMInterface.py +83 -0
- pandajedi/jediddm/GenDDMClient.py +17 -0
- pandajedi/jediddm/__init__.py +0 -0
- pandajedi/jedidog/AtlasAnalWatchDog.py +648 -0
- pandajedi/jedidog/AtlasDataCarouselWatchDog.py +189 -0
- pandajedi/jedidog/AtlasDataLocalityUpdaterWatchDog.py +215 -0
- pandajedi/jedidog/AtlasProdWatchDog.py +443 -0
- pandajedi/jedidog/AtlasQueueFillerWatchDog.py +800 -0
- pandajedi/jedidog/AtlasTaskWithholderWatchDog.py +245 -0
- pandajedi/jedidog/AtlasWorkflowManagerWatchDog.py +63 -0
- pandajedi/jedidog/GenWatchDog.py +21 -0
- pandajedi/jedidog/JumboWatchDog.py +147 -0
- pandajedi/jedidog/TypicalWatchDogBase.py +122 -0
- pandajedi/jedidog/WatchDogBase.py +53 -0
- pandajedi/jedidog/__init__.py +0 -0
- pandajedi/jediexec/reniceJEDI.py +25 -0
- pandajedi/jedigen/AtlasTaskGenerator.py +110 -0
- pandajedi/jedigen/TaskGeneratorBase.py +15 -0
- pandajedi/jedigen/__init__.py +0 -0
- pandajedi/jedimsgprocessor/__init__.py +0 -0
- pandajedi/jedimsgprocessor/ai_parameter_adjuster_msg_processor +71 -0
- pandajedi/jedimsgprocessor/atlas_idds_msg_processor.py +66 -0
- pandajedi/jedimsgprocessor/base_msg_processor.py +33 -0
- pandajedi/jedimsgprocessor/forwarding_msg_processor.py +29 -0
- pandajedi/jedimsgprocessor/hpo_msg_processor.py +93 -0
- pandajedi/jedimsgprocessor/jedi_contents_feeder_msg_processor.py +63 -0
- pandajedi/jedimsgprocessor/jedi_job_generator_msg_processor.py +185 -0
- pandajedi/jedimsgprocessor/jedi_post_processor_msg_processor.py +82 -0
- pandajedi/jedimsgprocessor/kafka_msg_processor.py +44 -0
- pandajedi/jedimsgprocessor/panda_callback_msg_processor.py +182 -0
- pandajedi/jedimsgprocessor/panda_to_jedi_msg_processor.py +74 -0
- pandajedi/jedimsgprocessor/processing_msg_processor.py +134 -0
- pandajedi/jedimsgprocessor/status_report_msg_processor.py +129 -0
- pandajedi/jedimsgprocessor/tape_carousel_msg_processor.py +118 -0
- pandajedi/jedimsgprocessor/workflow_manager_msg_processor.py +89 -0
- pandajedi/jediorder/ContentsFeeder.py +785 -0
- pandajedi/jediorder/JediDaemon.py +27 -0
- pandajedi/jediorder/JediKnight.py +55 -0
- pandajedi/jediorder/JediMaster.py +238 -0
- pandajedi/jediorder/JediMsgProcessor.py +32 -0
- pandajedi/jediorder/JobBroker.py +33 -0
- pandajedi/jediorder/JobGenerator.py +2763 -0
- pandajedi/jediorder/JobSplitter.py +308 -0
- pandajedi/jediorder/JobThrottler.py +28 -0
- pandajedi/jediorder/PostProcessor.py +166 -0
- pandajedi/jediorder/TaskBroker.py +268 -0
- pandajedi/jediorder/TaskCommando.py +334 -0
- pandajedi/jediorder/TaskGenerator.py +17 -0
- pandajedi/jediorder/TaskRefiner.py +611 -0
- pandajedi/jediorder/TaskSetupper.py +17 -0
- pandajedi/jediorder/WatchDog.py +79 -0
- pandajedi/jediorder/__init__.py +0 -0
- pandajedi/jedipprocess/AtlasAnalPostProcessor.py +404 -0
- pandajedi/jedipprocess/AtlasPostProcessorUtils.py +50 -0
- pandajedi/jedipprocess/AtlasProdPostProcessor.py +178 -0
- pandajedi/jedipprocess/GenPostProcessor.py +35 -0
- pandajedi/jedipprocess/MailTemplates.py +185 -0
- pandajedi/jedipprocess/PostProcessorBase.py +269 -0
- pandajedi/jedipprocess/__init__.py +0 -0
- pandajedi/jedirefine/AtlasAnalTaskRefiner.py +259 -0
- pandajedi/jedirefine/AtlasProdTaskRefiner.py +162 -0
- pandajedi/jedirefine/GenTaskRefiner.py +61 -0
- pandajedi/jedirefine/RefinerUtils.py +154 -0
- pandajedi/jedirefine/TaskRefinerBase.py +996 -0
- pandajedi/jedirefine/__init__.py +0 -0
- pandajedi/jedisetup/AtlasTaskSetupper.py +309 -0
- pandajedi/jedisetup/GenTaskSetupper.py +16 -0
- pandajedi/jedisetup/SimpleTaskSetupper.py +152 -0
- pandajedi/jedisetup/TaskSetupperBase.py +15 -0
- pandajedi/jedisetup/__init__.py +0 -0
- pandajedi/jeditest/SchemaChecker.py +41 -0
- pandajedi/jeditest/addContWoSoTask.py +54 -0
- pandajedi/jeditest/addDataCarouselRequest.py +47 -0
- pandajedi/jeditest/addEventServiceTask.py +101 -0
- pandajedi/jeditest/addHpoTask.py +81 -0
- pandajedi/jeditest/addIDDSStageInRequest.py +56 -0
- pandajedi/jeditest/addNonAtlasTask.py +71 -0
- pandajedi/jeditest/addNonAtlasTaskEnc.py +64 -0
- pandajedi/jeditest/addTestTask.py +124 -0
- pandajedi/jeditest/addTestTaskParamToDEFT.py +70 -0
- pandajedi/jeditest/addTestTaskWithMerger.py +97 -0
- pandajedi/jeditest/brokerTest.py +78 -0
- pandajedi/jeditest/cancelDataCarouselRequest.py +35 -0
- pandajedi/jeditest/contentsFeederTest.py +28 -0
- pandajedi/jeditest/contentsFeederTestWithTask.py +18 -0
- pandajedi/jeditest/ddmTest.py +4 -0
- pandajedi/jeditest/jobGenForOneTask.py +38 -0
- pandajedi/jeditest/jobGeneratorTest.py +43 -0
- pandajedi/jeditest/jobSplitterTest.py +104 -0
- pandajedi/jeditest/jobThrottlerTest.py +22 -0
- pandajedi/jeditest/msgProcessorTest.py +9 -0
- pandajedi/jeditest/postProcessorTest.py +18 -0
- pandajedi/jeditest/resubmitDataCarouselRequest.py +35 -0
- pandajedi/jeditest/resubmitTaskParam.py +20 -0
- pandajedi/jeditest/taskBrokerTest.py +22 -0
- pandajedi/jeditest/taskBrokerTestWithTask.py +38 -0
- pandajedi/jeditest/taskBufferTest.py +5 -0
- pandajedi/jeditest/taskCommandoTest.py +28 -0
- pandajedi/jeditest/taskRefinerTest.py +30 -0
- pandajedi/jeditest/testOneTaskToRefine.py +49 -0
- pandajedi/jeditest/testOneTaskToTaskBroker.py +42 -0
- pandajedi/jeditest/testPandaDDMMap.py +62 -0
- pandajedi/jeditest/updateDataCarouselRequestParams.py +50 -0
- pandajedi/jeditest/watchDogTest.py +25 -0
- pandajedi/jeditest/workQueueTest.py +22 -0
- pandajedi/jedithrottle/AtlasAnalJobThrottler.py +19 -0
- pandajedi/jedithrottle/AtlasProdJobThrottler.py +18 -0
- pandajedi/jedithrottle/GenJobThrottler.py +26 -0
- pandajedi/jedithrottle/JobThrottlerBase.py +496 -0
- pandajedi/jedithrottle/__init__.py +0 -0
- pandaserver/__init__.py +0 -0
- pandaserver/api/__init__.py +0 -0
- pandaserver/api/v1/__init__.py +0 -0
- pandaserver/api/v1/common.py +339 -0
- pandaserver/api/v1/credential_management_api.py +372 -0
- pandaserver/api/v1/data_carousel_api.py +264 -0
- pandaserver/api/v1/event_api.py +269 -0
- pandaserver/api/v1/file_server_api.py +942 -0
- pandaserver/api/v1/harvester_api.py +445 -0
- pandaserver/api/v1/http_client.py +166 -0
- pandaserver/api/v1/idds_api.py +180 -0
- pandaserver/api/v1/job_api.py +463 -0
- pandaserver/api/v1/metaconfig_api.py +122 -0
- pandaserver/api/v1/pilot_api.py +895 -0
- pandaserver/api/v1/statistics_api.py +207 -0
- pandaserver/api/v1/system_api.py +186 -0
- pandaserver/api/v1/task_api.py +1520 -0
- pandaserver/api/v1/tests/__init__.py +0 -0
- pandaserver/api/v1/tests/credential_management_api_tests.py +116 -0
- pandaserver/api/v1/tests/data_carousel_api_tests.py +82 -0
- pandaserver/api/v1/tests/harvester_api_tests.py +188 -0
- pandaserver/api/v1/tests/idds_api_tests.py +45 -0
- pandaserver/api/v1/tests/job_api_tests.py +112 -0
- pandaserver/api/v1/tests/pilot_api_tests.py +154 -0
- pandaserver/api/v1/tests/statistics_api_tests.py +104 -0
- pandaserver/api/v1/tests/system_api_tests.py +58 -0
- pandaserver/api/v1/tests/task_api_tests.py +590 -0
- pandaserver/api/v1/version.py +2 -0
- pandaserver/api/v1/workflow_api.py +136 -0
- pandaserver/brokerage/SiteMapper.py +406 -0
- pandaserver/brokerage/__init__.py +0 -0
- pandaserver/brokerage/broker.py +272 -0
- pandaserver/config/__init__.py +0 -0
- pandaserver/config/config_utils.py +15 -0
- pandaserver/config/daemon_config.py +29 -0
- pandaserver/config/mcp_config.py +38 -0
- pandaserver/config/panda_config.py +233 -0
- pandaserver/configurator/Carbon.py +65 -0
- pandaserver/configurator/Configurator.py +908 -0
- pandaserver/configurator/__init__.py +0 -0
- pandaserver/configurator/aux.py +73 -0
- pandaserver/daemons/__init__.py +0 -0
- pandaserver/daemons/master.py +110 -0
- pandaserver/daemons/master_systemd.py +79 -0
- pandaserver/daemons/scripts/__init__.py +0 -0
- pandaserver/daemons/scripts/add_main.py +264 -0
- pandaserver/daemons/scripts/add_sub.py +221 -0
- pandaserver/daemons/scripts/cache_pilots.py +47 -0
- pandaserver/daemons/scripts/cache_schedconfig.py +286 -0
- pandaserver/daemons/scripts/carbon.py +50 -0
- pandaserver/daemons/scripts/configurator.py +94 -0
- pandaserver/daemons/scripts/copyArchive.py +1387 -0
- pandaserver/daemons/scripts/datasetManager.py +1154 -0
- pandaserver/daemons/scripts/dummy_test.py +25 -0
- pandaserver/daemons/scripts/evpPD2P.py +144 -0
- pandaserver/daemons/scripts/hs_scrapers.py +264 -0
- pandaserver/daemons/scripts/metric_collector.py +927 -0
- pandaserver/daemons/scripts/panda_activeusers_query.py +80 -0
- pandaserver/daemons/scripts/pilotStreaming.py +109 -0
- pandaserver/daemons/scripts/process_workflow_files_daemon.py +166 -0
- pandaserver/daemons/scripts/recover_lost_files_daemon.py +177 -0
- pandaserver/daemons/scripts/task_evaluator.py +405 -0
- pandaserver/daemons/scripts/tmpwatch.py +70 -0
- pandaserver/daemons/scripts/worker_synchronization.py +97 -0
- pandaserver/daemons/utils.py +753 -0
- pandaserver/dataservice/DataServiceUtils.py +323 -0
- pandaserver/dataservice/ErrorCode.py +25 -0
- pandaserver/dataservice/RecoverLostFilesCore.py +322 -0
- pandaserver/dataservice/__init__.py +0 -0
- pandaserver/dataservice/activator.py +79 -0
- pandaserver/dataservice/adder_atlas_plugin.py +1106 -0
- pandaserver/dataservice/adder_dummy_plugin.py +31 -0
- pandaserver/dataservice/adder_gen.py +954 -0
- pandaserver/dataservice/adder_plugin_base.py +27 -0
- pandaserver/dataservice/adder_result.py +75 -0
- pandaserver/dataservice/adder_simple_plugin.py +160 -0
- pandaserver/dataservice/closer.py +247 -0
- pandaserver/dataservice/closer_atlas_plugin.py +90 -0
- pandaserver/dataservice/ddm.py +1381 -0
- pandaserver/dataservice/ddm_handler.py +95 -0
- pandaserver/dataservice/dyn_data_distributer.py +824 -0
- pandaserver/dataservice/event_lookup_client_ei.py +77 -0
- pandaserver/dataservice/event_picker.py +268 -0
- pandaserver/dataservice/finisher.py +194 -0
- pandaserver/dataservice/setupper.py +225 -0
- pandaserver/dataservice/setupper_atlas_plugin.py +1851 -0
- pandaserver/dataservice/setupper_dummy_plugin.py +47 -0
- pandaserver/dataservice/setupper_plugin_base.py +67 -0
- pandaserver/jobdispatcher/DispatcherUtils.py +35 -0
- pandaserver/jobdispatcher/ErrorCode.py +19 -0
- pandaserver/jobdispatcher/JobDispatcher.py +1625 -0
- pandaserver/jobdispatcher/Protocol.py +443 -0
- pandaserver/jobdispatcher/Watcher.py +186 -0
- pandaserver/jobdispatcher/__init__.py +0 -0
- pandaserver/pandamcp/__init__.py +0 -0
- pandaserver/pandamcp/mcp_main.py +56 -0
- pandaserver/pandamcp/mcp_test_client.py +95 -0
- pandaserver/pandamcp/mcp_utils.py +74 -0
- pandaserver/proxycache/__init__.py +0 -0
- pandaserver/proxycache/panda_proxy_cache.py +240 -0
- pandaserver/proxycache/token_cache.py +141 -0
- pandaserver/server/.gacl +6 -0
- pandaserver/server/panda.py +539 -0
- pandaserver/srvcore/CoreUtils.py +572 -0
- pandaserver/srvcore/MailUtils.py +106 -0
- pandaserver/srvcore/__init__.py +0 -0
- pandaserver/srvcore/allowed_methods.py +100 -0
- pandaserver/srvcore/oidc_utils.py +152 -0
- pandaserver/srvcore/panda_request.py +196 -0
- pandaserver/srvcore/srv_msg_utils.py +42 -0
- pandaserver/taskbuffer/ConBridge.py +513 -0
- pandaserver/taskbuffer/DBProxyPool.py +95 -0
- pandaserver/taskbuffer/DataCarousel.py +3053 -0
- pandaserver/taskbuffer/DatasetSpec.py +137 -0
- pandaserver/taskbuffer/DdmSpec.py +201 -0
- pandaserver/taskbuffer/ErrorCode.py +87 -0
- pandaserver/taskbuffer/EventServiceUtils.py +396 -0
- pandaserver/taskbuffer/FileSpec.py +290 -0
- pandaserver/taskbuffer/GlobalShares.py +244 -0
- pandaserver/taskbuffer/HarvesterMetricsSpec.py +92 -0
- pandaserver/taskbuffer/Initializer.py +82 -0
- pandaserver/taskbuffer/InputChunk.py +1080 -0
- pandaserver/taskbuffer/JediCacheSpec.py +57 -0
- pandaserver/taskbuffer/JediDatasetSpec.py +588 -0
- pandaserver/taskbuffer/JediFileSpec.py +242 -0
- pandaserver/taskbuffer/JediTaskSpec.py +1687 -0
- pandaserver/taskbuffer/JobSpec.py +1006 -0
- pandaserver/taskbuffer/JobUtils.py +232 -0
- pandaserver/taskbuffer/NucleusSpec.py +116 -0
- pandaserver/taskbuffer/OraDBProxy.py +174 -0
- pandaserver/taskbuffer/PanDAMsgProcessor.py +8 -0
- pandaserver/taskbuffer/PandaDBSchemaInfo.py +18 -0
- pandaserver/taskbuffer/ParseJobXML.py +350 -0
- pandaserver/taskbuffer/PickleFileSpec.py +18 -0
- pandaserver/taskbuffer/PickleJobSpec.py +25 -0
- pandaserver/taskbuffer/PrioUtil.py +40 -0
- pandaserver/taskbuffer/ProcessGroups.py +111 -0
- pandaserver/taskbuffer/ResourceSpec.py +175 -0
- pandaserver/taskbuffer/SQLDumper.py +37 -0
- pandaserver/taskbuffer/SQLManager.py +60 -0
- pandaserver/taskbuffer/SiteSpec.py +304 -0
- pandaserver/taskbuffer/SupErrors.py +10 -0
- pandaserver/taskbuffer/TaskBuffer.py +2882 -0
- pandaserver/taskbuffer/TaskBufferInterface.py +163 -0
- pandaserver/taskbuffer/Utils.py +848 -0
- pandaserver/taskbuffer/WorkQueue.py +227 -0
- pandaserver/taskbuffer/WorkQueueMapper.py +203 -0
- pandaserver/taskbuffer/WorkerSpec.py +143 -0
- pandaserver/taskbuffer/WrappedCursor.py +487 -0
- pandaserver/taskbuffer/WrappedPickle.py +88 -0
- pandaserver/taskbuffer/WrappedPostgresConn.py +24 -0
- pandaserver/taskbuffer/__init__.py +0 -0
- pandaserver/taskbuffer/db_proxy_mods/OraDBProxy_structure.png +0 -0
- pandaserver/taskbuffer/db_proxy_mods/__init__.py +7 -0
- pandaserver/taskbuffer/db_proxy_mods/base_module.py +841 -0
- pandaserver/taskbuffer/db_proxy_mods/data_carousel_module.py +772 -0
- pandaserver/taskbuffer/db_proxy_mods/entity_module.py +3804 -0
- pandaserver/taskbuffer/db_proxy_mods/job_complex_module.py +5661 -0
- pandaserver/taskbuffer/db_proxy_mods/job_standalone_module.py +2640 -0
- pandaserver/taskbuffer/db_proxy_mods/metrics_module.py +874 -0
- pandaserver/taskbuffer/db_proxy_mods/misc_standalone_module.py +4272 -0
- pandaserver/taskbuffer/db_proxy_mods/task_complex_module.py +6172 -0
- pandaserver/taskbuffer/db_proxy_mods/task_event_module.py +4953 -0
- pandaserver/taskbuffer/db_proxy_mods/task_standalone_module.py +4999 -0
- pandaserver/taskbuffer/db_proxy_mods/task_utils_module.py +2063 -0
- pandaserver/taskbuffer/db_proxy_mods/worker_module.py +1944 -0
- pandaserver/taskbuffer/db_proxy_mods/workflow_module.py +832 -0
- pandaserver/taskbuffer/retryModule.py +552 -0
- pandaserver/taskbuffer/task_split_rules.py +308 -0
- pandaserver/taskbuffer/workflow_processor.py +249 -0
- pandaserver/taskbuffer/wrapped_oracle_conn.py +18 -0
- pandaserver/test/SchemaChecker.py +40 -0
- pandaserver/test/__init__.py +0 -0
- pandaserver/test/banUser.py +43 -0
- pandaserver/test/boostPrio.py +19 -0
- pandaserver/test/boostUser.py +35 -0
- pandaserver/test/callbackDDM.py +11 -0
- pandaserver/test/daod_on_demand.py +95 -0
- pandaserver/test/finishJob.py +184 -0
- pandaserver/test/finishTaskJEDI.py +11 -0
- pandaserver/test/getJobs.py +53 -0
- pandaserver/test/killJob.py +79 -0
- pandaserver/test/killJobLowPrio.py +99 -0
- pandaserver/test/killJobsInTask.py +56 -0
- pandaserver/test/killTask.py +80 -0
- pandaserver/test/killTaskJEDI.py +11 -0
- pandaserver/test/killUser.py +83 -0
- pandaserver/test/lsst/README.txt +7 -0
- pandaserver/test/lsst/lsstSubmit.py +139 -0
- pandaserver/test/lsst/lsstSubmitMERGEtest.py +141 -0
- pandaserver/test/lsst/lsstSubmitPhosim332.py +53 -0
- pandaserver/test/lsst/prepare-client-tarball-from-bigpanda-server.sh +41 -0
- pandaserver/test/reassignJobs.py +17 -0
- pandaserver/test/reassignSite.py +82 -0
- pandaserver/test/reassignTask.py +117 -0
- pandaserver/test/reassignWaiting.py +39 -0
- pandaserver/test/recoverLostFiles.py +7 -0
- pandaserver/test/reloadInputDS.py +51 -0
- pandaserver/test/sendCommandToJob.py +26 -0
- pandaserver/test/setDebugMode.py +38 -0
- pandaserver/test/setPriority.py +28 -0
- pandaserver/test/testEvgen.py +60 -0
- pandaserver/test/testEvgen17.py +58 -0
- pandaserver/test/testG4sim.py +81 -0
- pandaserver/test/testG4sim17.py +90 -0
- pandaserver/test/testGlobalShares.py +131 -0
- pandaserver/test/testReco.py +103 -0
- pandaserver/test/testSimulReco14.py +98 -0
- pandaserver/test/testSiteMap.py +26 -0
- pandaserver/test/testUpdateWorkerPilotStatus.py +9 -0
- pandaserver/userinterface/Client.py +1686 -0
- pandaserver/userinterface/UserIF.py +1799 -0
- pandaserver/userinterface/__init__.py +0 -0
- pandaserver/workflow/__init__.py +0 -0
- pandaserver/workflow/data_handler_plugins/base_data_handler.py +48 -0
- pandaserver/workflow/data_handler_plugins/ddm_collection_data_handler.py +100 -0
- pandaserver/workflow/data_handler_plugins/panda_task_data_handler.py +140 -0
- pandaserver/workflow/pcwl_test.py +122 -0
- pandaserver/workflow/pcwl_utils.py +384 -0
- pandaserver/workflow/psnakemake_container.json +68 -0
- pandaserver/workflow/psnakemake_task.json +62 -0
- pandaserver/workflow/psnakemake_test.py +60 -0
- pandaserver/workflow/snakeparser/__init__.py +3 -0
- pandaserver/workflow/snakeparser/extensions.py +74 -0
- pandaserver/workflow/snakeparser/log.py +53 -0
- pandaserver/workflow/snakeparser/names.py +3 -0
- pandaserver/workflow/snakeparser/parser.py +332 -0
- pandaserver/workflow/snakeparser/utils.py +29 -0
- pandaserver/workflow/step_handler_plugins/base_step_handler.py +85 -0
- pandaserver/workflow/step_handler_plugins/panda_task_step_handler.py +280 -0
- pandaserver/workflow/workflow_base.py +531 -0
- pandaserver/workflow/workflow_core.py +1974 -0
- pandaserver/workflow/workflow_core_smoketest.py +388 -0
- pandaserver/workflow/workflow_parser.py +222 -0
- 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
|