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,785 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import math
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import socket
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
import traceback
|
|
9
|
+
import uuid
|
|
10
|
+
|
|
11
|
+
from pandajedi.jediconfig import jedi_config
|
|
12
|
+
from pandajedi.jedicore import Interaction
|
|
13
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
14
|
+
from pandajedi.jedicore.ThreadUtils import ListWithLock, ThreadPool, WorkerThread
|
|
15
|
+
from pandajedi.jedirefine import RefinerUtils
|
|
16
|
+
|
|
17
|
+
from .JediKnight import JediKnight
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
import idds.common.constants
|
|
21
|
+
import idds.common.utils
|
|
22
|
+
from idds.client.client import Client as iDDS_Client
|
|
23
|
+
except ImportError:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
27
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
28
|
+
|
|
29
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# worker class to take care of DatasetContents table
|
|
33
|
+
class ContentsFeeder(JediKnight):
|
|
34
|
+
# constructor
|
|
35
|
+
def __init__(self, commuChannel, taskBufferIF, ddmIF, vos, prodSourceLabels):
|
|
36
|
+
self.vos = self.parseInit(vos)
|
|
37
|
+
self.prodSourceLabels = self.parseInit(prodSourceLabels)
|
|
38
|
+
self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}_{os.getpgrp()}-con"
|
|
39
|
+
JediKnight.__init__(self, commuChannel, taskBufferIF, ddmIF, logger)
|
|
40
|
+
|
|
41
|
+
# main
|
|
42
|
+
def start(self):
|
|
43
|
+
# start base class
|
|
44
|
+
JediKnight.start(self)
|
|
45
|
+
# go into main loop
|
|
46
|
+
while True:
|
|
47
|
+
startTime = naive_utcnow()
|
|
48
|
+
try:
|
|
49
|
+
# loop over all vos
|
|
50
|
+
for vo in self.vos:
|
|
51
|
+
# loop over all sourceLabels
|
|
52
|
+
for prodSourceLabel in self.prodSourceLabels:
|
|
53
|
+
# get the list of datasets to feed contents to DB
|
|
54
|
+
tmpList = self.taskBufferIF.getDatasetsToFeedContents_JEDI(vo, prodSourceLabel)
|
|
55
|
+
if tmpList is None:
|
|
56
|
+
# failed
|
|
57
|
+
logger.error("failed to get the list of datasets to feed contents")
|
|
58
|
+
else:
|
|
59
|
+
logger.debug(f"got {len(tmpList)} datasets")
|
|
60
|
+
# put to a locked list
|
|
61
|
+
dsList = ListWithLock(tmpList)
|
|
62
|
+
# make thread pool
|
|
63
|
+
threadPool = ThreadPool()
|
|
64
|
+
# make workers
|
|
65
|
+
nWorker = jedi_config.confeeder.nWorkers
|
|
66
|
+
for iWorker in range(nWorker):
|
|
67
|
+
thr = ContentsFeederThread(dsList, threadPool, self.taskBufferIF, self.ddmIF, self.pid)
|
|
68
|
+
thr.start()
|
|
69
|
+
# join
|
|
70
|
+
threadPool.join()
|
|
71
|
+
except Exception:
|
|
72
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
73
|
+
logger.error(f"failed in {self.__class__.__name__}.start() with {errtype.__name__} {errvalue}")
|
|
74
|
+
# sleep if needed
|
|
75
|
+
loopCycle = jedi_config.confeeder.loopCycle
|
|
76
|
+
timeDelta = naive_utcnow() - startTime
|
|
77
|
+
sleepPeriod = loopCycle - timeDelta.seconds
|
|
78
|
+
if sleepPeriod > 0:
|
|
79
|
+
time.sleep(sleepPeriod)
|
|
80
|
+
# randomize cycle
|
|
81
|
+
self.randomSleep(max_val=loopCycle)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# thread for real worker
|
|
85
|
+
class ContentsFeederThread(WorkerThread):
|
|
86
|
+
# constructor
|
|
87
|
+
def __init__(self, taskDsList, threadPool, taskbufferIF, ddmIF, pid):
|
|
88
|
+
# initialize woker with no semaphore
|
|
89
|
+
WorkerThread.__init__(self, None, threadPool, logger)
|
|
90
|
+
# attributres
|
|
91
|
+
self.taskDsList = taskDsList
|
|
92
|
+
self.taskBufferIF = taskbufferIF
|
|
93
|
+
self.ddmIF = ddmIF
|
|
94
|
+
self.msgType = "contentsfeeder"
|
|
95
|
+
self.pid = pid
|
|
96
|
+
|
|
97
|
+
# main
|
|
98
|
+
def runImpl(self):
|
|
99
|
+
while True:
|
|
100
|
+
try:
|
|
101
|
+
# get a part of list
|
|
102
|
+
nTasks = 10
|
|
103
|
+
taskDsList = self.taskDsList.get(nTasks)
|
|
104
|
+
# no more datasets
|
|
105
|
+
if len(taskDsList) == 0:
|
|
106
|
+
self.logger.debug(f"{self.__class__.__name__} terminating since no more items")
|
|
107
|
+
return
|
|
108
|
+
# feed to tasks
|
|
109
|
+
self.feed_contents_to_tasks(taskDsList)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error(f"{self.__class__.__name__} failed in runImpl() with {str(e)}: {traceback.format_exc()}")
|
|
112
|
+
|
|
113
|
+
# feed contents to tasks
|
|
114
|
+
def feed_contents_to_tasks(self, task_ds_list, real_run=True):
|
|
115
|
+
# max number of file records per dataset
|
|
116
|
+
maxFileRecords = 200000
|
|
117
|
+
# loop over all tasks
|
|
118
|
+
for jediTaskID, dsList in task_ds_list:
|
|
119
|
+
allUpdated = True
|
|
120
|
+
taskBroken = False
|
|
121
|
+
taskOnHold = False
|
|
122
|
+
runningTask = False
|
|
123
|
+
taskToFinish = False
|
|
124
|
+
missingMap = {}
|
|
125
|
+
datasetsIdxConsistency = []
|
|
126
|
+
|
|
127
|
+
# get task
|
|
128
|
+
tmpStat, taskSpec = self.taskBufferIF.getTaskWithID_JEDI(jediTaskID, False, real_run, self.pid, 10, clearError=True)
|
|
129
|
+
if not tmpStat or taskSpec is None:
|
|
130
|
+
self.logger.debug(f"failed to get taskSpec for jediTaskID={jediTaskID}")
|
|
131
|
+
continue
|
|
132
|
+
|
|
133
|
+
# make logger
|
|
134
|
+
try:
|
|
135
|
+
gshare = "_".join(taskSpec.gshare.split(" "))
|
|
136
|
+
except Exception:
|
|
137
|
+
gshare = "Undefined"
|
|
138
|
+
tmpLog = MsgWrapper(self.logger, f"<jediTaskID={jediTaskID} gshare={gshare}>")
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
# get task parameters
|
|
142
|
+
taskParam = self.taskBufferIF.getTaskParamsWithID_JEDI(jediTaskID)
|
|
143
|
+
taskParamMap = RefinerUtils.decodeJSON(taskParam)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
tmpLog.error(f"task param conversion from json failed with {str(e)}")
|
|
146
|
+
# unlock
|
|
147
|
+
tmpStat = self.taskBufferIF.unlockSingleTask_JEDI(jediTaskID, self.pid)
|
|
148
|
+
tmpLog.debug(f"unlocked with {tmpStat}")
|
|
149
|
+
continue
|
|
150
|
+
# renaming of parameters
|
|
151
|
+
if "nEventsPerInputFile" in taskParamMap:
|
|
152
|
+
taskParamMap["nEventsPerFile"] = taskParamMap["nEventsPerInputFile"]
|
|
153
|
+
# the number of files per job
|
|
154
|
+
nFilesPerJob = taskSpec.getNumFilesPerJob()
|
|
155
|
+
# the number of chunks used by scout
|
|
156
|
+
nChunksForScout = 10
|
|
157
|
+
# load XML
|
|
158
|
+
if taskSpec.useLoadXML():
|
|
159
|
+
xmlConfig = taskParamMap["loadXML"]
|
|
160
|
+
else:
|
|
161
|
+
xmlConfig = None
|
|
162
|
+
# skip files used by another task
|
|
163
|
+
if "skipFilesUsedBy" in taskParamMap:
|
|
164
|
+
skipFilesUsedBy = taskParamMap["skipFilesUsedBy"]
|
|
165
|
+
else:
|
|
166
|
+
skipFilesUsedBy = None
|
|
167
|
+
# check no wait
|
|
168
|
+
noWaitParent = False
|
|
169
|
+
parentOutDatasets = set()
|
|
170
|
+
if taskSpec.noWaitParent() and taskSpec.parent_tid not in [None, taskSpec.jediTaskID]:
|
|
171
|
+
tmpStat = self.taskBufferIF.checkParentTask_JEDI(taskSpec.parent_tid, taskSpec.jediTaskID)
|
|
172
|
+
if tmpStat is None or tmpStat == "running":
|
|
173
|
+
noWaitParent = True
|
|
174
|
+
# get output datasets from parent task
|
|
175
|
+
tmpParentStat, tmpParentOutDatasets = self.taskBufferIF.getDatasetsWithJediTaskID_JEDI(taskSpec.parent_tid, ["output", "log"])
|
|
176
|
+
# collect dataset names
|
|
177
|
+
for tmpParentOutDataset in tmpParentOutDatasets:
|
|
178
|
+
parentOutDatasets.add(tmpParentOutDataset.datasetName)
|
|
179
|
+
if tmpParentOutDataset.containerName:
|
|
180
|
+
if tmpParentOutDataset.containerName.endswith("/"):
|
|
181
|
+
parentOutDatasets.add(tmpParentOutDataset.containerName)
|
|
182
|
+
parentOutDatasets.add(tmpParentOutDataset.containerName[:-1])
|
|
183
|
+
else:
|
|
184
|
+
parentOutDatasets.add(tmpParentOutDataset.containerName)
|
|
185
|
+
parentOutDatasets.add(tmpParentOutDataset.containerName + "/")
|
|
186
|
+
# loop over all datasets
|
|
187
|
+
nFilesMaster = 0
|
|
188
|
+
nFilesMasterReady = 0
|
|
189
|
+
checkedMaster = False
|
|
190
|
+
setFrozenTime = True
|
|
191
|
+
master_offset = None
|
|
192
|
+
master_is_open = False
|
|
193
|
+
if not taskBroken:
|
|
194
|
+
ddmIF = self.ddmIF.getInterface(taskSpec.vo, taskSpec.cloud)
|
|
195
|
+
origNumFiles = None
|
|
196
|
+
if "nFiles" in taskParamMap:
|
|
197
|
+
origNumFiles = taskParamMap["nFiles"]
|
|
198
|
+
id_to_container = {}
|
|
199
|
+
[id_to_container.update({datasetSpec.datasetID: datasetSpec.containerName}) for datasetSpec in dsList]
|
|
200
|
+
skip_secondaries = False
|
|
201
|
+
for datasetSpec in dsList:
|
|
202
|
+
tmpLog.debug(f"start loop for {datasetSpec.datasetName}(id={datasetSpec.datasetID})")
|
|
203
|
+
if skip_secondaries and not datasetSpec.isMaster():
|
|
204
|
+
tmpLog.debug(f"skip {datasetSpec.datasetName} since it is secondary")
|
|
205
|
+
continue
|
|
206
|
+
# index consistency
|
|
207
|
+
if datasetSpec.indexConsistent():
|
|
208
|
+
datasetsIdxConsistency.append(datasetSpec.datasetID)
|
|
209
|
+
# prestaging
|
|
210
|
+
if taskSpec.inputPreStaging() and (datasetSpec.isMaster() or datasetSpec.isSeqNumber()):
|
|
211
|
+
if datasetSpec.is_no_staging():
|
|
212
|
+
inputPreStaging = False
|
|
213
|
+
elif (
|
|
214
|
+
(nStaging := self.taskBufferIF.getNumStagingFiles_JEDI(taskSpec.jediTaskID)) is not None
|
|
215
|
+
and nStaging == 0
|
|
216
|
+
and datasetSpec.nFiles > 0
|
|
217
|
+
):
|
|
218
|
+
inputPreStaging = False
|
|
219
|
+
else:
|
|
220
|
+
inputPreStaging = True
|
|
221
|
+
else:
|
|
222
|
+
inputPreStaging = False
|
|
223
|
+
# get dataset metadata
|
|
224
|
+
tmpLog.debug("get metadata")
|
|
225
|
+
stateUpdateTime = naive_utcnow()
|
|
226
|
+
try:
|
|
227
|
+
if not datasetSpec.isPseudo():
|
|
228
|
+
tmpMetadata = ddmIF.getDatasetMetaData(datasetSpec.datasetName, ignore_missing=True)
|
|
229
|
+
else:
|
|
230
|
+
# dummy metadata for pseudo dataset
|
|
231
|
+
tmpMetadata = {"state": "closed"}
|
|
232
|
+
# check if master is open
|
|
233
|
+
if datasetSpec.isMaster() and tmpMetadata["state"] == "open":
|
|
234
|
+
master_is_open = True
|
|
235
|
+
# set mutable when the dataset is open and parent is running or task is configured to run until the dataset is closed
|
|
236
|
+
if (noWaitParent or taskSpec.runUntilClosed() or inputPreStaging) and (
|
|
237
|
+
tmpMetadata["state"] == "open"
|
|
238
|
+
or datasetSpec.datasetName in parentOutDatasets
|
|
239
|
+
or datasetSpec.datasetName.split(":")[-1] in parentOutDatasets
|
|
240
|
+
or inputPreStaging
|
|
241
|
+
):
|
|
242
|
+
# dummy metadata when parent is running
|
|
243
|
+
tmpMetadata = {"state": "mutable"}
|
|
244
|
+
# set mutable when workflow holdup is set
|
|
245
|
+
if taskSpec.is_workflow_holdup():
|
|
246
|
+
tmpMetadata = {"state": "mutable"}
|
|
247
|
+
except Exception:
|
|
248
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
249
|
+
tmpLog.error(f"{self.__class__.__name__} failed to get metadata to {errtype.__name__}:{errvalue}")
|
|
250
|
+
if errtype == Interaction.JEDIFatalError:
|
|
251
|
+
# fatal error
|
|
252
|
+
datasetStatus = "broken"
|
|
253
|
+
taskBroken = True
|
|
254
|
+
# update dataset status
|
|
255
|
+
self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog)
|
|
256
|
+
else:
|
|
257
|
+
if not taskSpec.ignoreMissingInDS():
|
|
258
|
+
# temporary error
|
|
259
|
+
taskOnHold = True
|
|
260
|
+
else:
|
|
261
|
+
# ignore missing
|
|
262
|
+
datasetStatus = "failed"
|
|
263
|
+
# update dataset status
|
|
264
|
+
self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog)
|
|
265
|
+
taskSpec.setErrDiag(f"failed to get metadata for {datasetSpec.datasetName}")
|
|
266
|
+
if not taskSpec.ignoreMissingInDS():
|
|
267
|
+
allUpdated = False
|
|
268
|
+
else:
|
|
269
|
+
# to skip missing dataset
|
|
270
|
+
if tmpMetadata["state"] == "missing":
|
|
271
|
+
# ignore missing
|
|
272
|
+
datasetStatus = "finished"
|
|
273
|
+
# update dataset status
|
|
274
|
+
self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog, "closed")
|
|
275
|
+
tmpLog.debug(f"disabled missing {datasetSpec.datasetName}")
|
|
276
|
+
continue
|
|
277
|
+
# get file list specified in task parameters
|
|
278
|
+
if taskSpec.is_work_segmented() and not datasetSpec.isPseudo() and not datasetSpec.isMaster():
|
|
279
|
+
fileList = []
|
|
280
|
+
includePatt = []
|
|
281
|
+
excludePatt = []
|
|
282
|
+
try:
|
|
283
|
+
segment_id = int(id_to_container[datasetSpec.masterID].split("/")[-1])
|
|
284
|
+
for item in taskParamMap["segmentSpecs"]:
|
|
285
|
+
if item["id"] == segment_id:
|
|
286
|
+
if "files" in item:
|
|
287
|
+
fileList = item["files"]
|
|
288
|
+
elif "datasets" in item:
|
|
289
|
+
for tmpDatasetName in item["datasets"]:
|
|
290
|
+
tmpRet = ddmIF.getFilesInDataset(tmpDatasetName)
|
|
291
|
+
fileList += [tmpAttr["lfn"] for tmpAttr in tmpRet.values()]
|
|
292
|
+
except Exception:
|
|
293
|
+
pass
|
|
294
|
+
else:
|
|
295
|
+
fileList, includePatt, excludePatt = RefinerUtils.extractFileList(taskParamMap, datasetSpec.datasetName)
|
|
296
|
+
# get the number of events in metadata
|
|
297
|
+
if "getNumEventsInMetadata" in taskParamMap:
|
|
298
|
+
getNumEvents = True
|
|
299
|
+
else:
|
|
300
|
+
getNumEvents = False
|
|
301
|
+
# get file list from DDM
|
|
302
|
+
tmpLog.debug("get files")
|
|
303
|
+
try:
|
|
304
|
+
useInFilesWithNewAttemptNr = False
|
|
305
|
+
skipDuplicate = not datasetSpec.useDuplicatedFiles()
|
|
306
|
+
if not datasetSpec.isPseudo():
|
|
307
|
+
if fileList != [] and "useInFilesInContainer" in taskParamMap and datasetSpec.containerName not in ["", None]:
|
|
308
|
+
# read files from container if file list is specified in task parameters
|
|
309
|
+
tmpDatasetName = datasetSpec.containerName
|
|
310
|
+
else:
|
|
311
|
+
tmpDatasetName = datasetSpec.datasetName
|
|
312
|
+
# use long format for LB
|
|
313
|
+
longFormat = False
|
|
314
|
+
if taskSpec.respectLumiblock() or taskSpec.orderByLB():
|
|
315
|
+
longFormat = True
|
|
316
|
+
tmpRet = ddmIF.getFilesInDataset(tmpDatasetName, getNumEvents=getNumEvents, skipDuplicate=skipDuplicate, longFormat=longFormat)
|
|
317
|
+
tmpLog.debug(f"got {len(tmpRet)} files in {tmpDatasetName}")
|
|
318
|
+
else:
|
|
319
|
+
if datasetSpec.isSeqNumber():
|
|
320
|
+
# make dummy files for seq_number
|
|
321
|
+
if datasetSpec.getNumRecords() is not None:
|
|
322
|
+
nPFN = datasetSpec.getNumRecords()
|
|
323
|
+
elif origNumFiles is not None:
|
|
324
|
+
nPFN = origNumFiles
|
|
325
|
+
if "nEventsPerFile" in taskParamMap and taskSpec.get_min_granularity():
|
|
326
|
+
nPFN = nPFN * taskParamMap["nEventsPerFile"] // taskSpec.get_min_granularity()
|
|
327
|
+
elif (
|
|
328
|
+
"nEventsPerJob" in taskParamMap
|
|
329
|
+
and "nEventsPerFile" in taskParamMap
|
|
330
|
+
and taskParamMap["nEventsPerFile"] > taskParamMap["nEventsPerJob"]
|
|
331
|
+
):
|
|
332
|
+
nPFN = nPFN * math.ceil(taskParamMap["nEventsPerFile"] / taskParamMap["nEventsPerJob"])
|
|
333
|
+
elif "nEventsPerJob" in taskParamMap and "nEventsPerFile" not in taskParamMap:
|
|
334
|
+
max_events_in_file = self.taskBufferIF.get_max_events_in_dataset(jediTaskID, datasetSpec.masterID)
|
|
335
|
+
if max_events_in_file and max_events_in_file > taskParamMap["nEventsPerJob"]:
|
|
336
|
+
nPFN = nPFN * math.ceil(max_events_in_file / taskParamMap["nEventsPerJob"])
|
|
337
|
+
elif "nEvents" in taskParamMap and "nEventsPerJob" in taskParamMap:
|
|
338
|
+
nPFN = math.ceil(taskParamMap["nEvents"] / taskParamMap["nEventsPerJob"])
|
|
339
|
+
elif "nEvents" in taskParamMap and "nEventsPerFile" in taskParamMap and taskSpec.getNumFilesPerJob() is not None:
|
|
340
|
+
nPFN = math.ceil(taskParamMap["nEvents"] / taskParamMap["nEventsPerFile"] / taskSpec.getNumFilesPerJob())
|
|
341
|
+
else:
|
|
342
|
+
# the default number of records for seq_number
|
|
343
|
+
seqDefNumRecords = 10000
|
|
344
|
+
# get nFiles of the master
|
|
345
|
+
tmpMasterAtt = self.taskBufferIF.getDatasetAttributes_JEDI(datasetSpec.jediTaskID, datasetSpec.masterID, ["nFiles"])
|
|
346
|
+
# use nFiles of the master as the number of records if it is larger than the default
|
|
347
|
+
if "nFiles" in tmpMasterAtt and tmpMasterAtt["nFiles"] > seqDefNumRecords:
|
|
348
|
+
nPFN = tmpMasterAtt["nFiles"]
|
|
349
|
+
else:
|
|
350
|
+
nPFN = seqDefNumRecords
|
|
351
|
+
# check usedBy
|
|
352
|
+
if skipFilesUsedBy is not None:
|
|
353
|
+
for tmpJediTaskID in str(skipFilesUsedBy).split(","):
|
|
354
|
+
tmpParentAtt = self.taskBufferIF.getDatasetAttributesWithMap_JEDI(
|
|
355
|
+
tmpJediTaskID, {"datasetName": datasetSpec.datasetName}, ["nFiles"]
|
|
356
|
+
)
|
|
357
|
+
if "nFiles" in tmpParentAtt and tmpParentAtt["nFiles"]:
|
|
358
|
+
nPFN += tmpParentAtt["nFiles"]
|
|
359
|
+
if nPFN > maxFileRecords:
|
|
360
|
+
raise Interaction.JEDIFatalError(f"Too many file records for seq_number >{maxFileRecords}")
|
|
361
|
+
tmpRet = {}
|
|
362
|
+
# get offset
|
|
363
|
+
tmpOffset = datasetSpec.getOffset()
|
|
364
|
+
tmpOffset += 1
|
|
365
|
+
for iPFN in range(nPFN):
|
|
366
|
+
tmpRet[str(uuid.uuid4())] = {
|
|
367
|
+
"lfn": iPFN + tmpOffset,
|
|
368
|
+
"scope": None,
|
|
369
|
+
"filesize": 0,
|
|
370
|
+
"checksum": None,
|
|
371
|
+
}
|
|
372
|
+
elif not taskSpec.useListPFN():
|
|
373
|
+
# dummy file list for pseudo dataset
|
|
374
|
+
tmpRet = {
|
|
375
|
+
str(uuid.uuid4()): {
|
|
376
|
+
"lfn": "pseudo_lfn",
|
|
377
|
+
"scope": None,
|
|
378
|
+
"filesize": 0,
|
|
379
|
+
"checksum": None,
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
else:
|
|
383
|
+
# make dummy file list for PFN list
|
|
384
|
+
if "nFiles" in taskParamMap:
|
|
385
|
+
nPFN = taskParamMap["nFiles"]
|
|
386
|
+
else:
|
|
387
|
+
nPFN = 1
|
|
388
|
+
tmpRet = {}
|
|
389
|
+
for iPFN in range(nPFN):
|
|
390
|
+
base_name = taskParamMap["pfnList"][iPFN].split("/")[-1]
|
|
391
|
+
n_events = None
|
|
392
|
+
if "^" in base_name:
|
|
393
|
+
base_name, n_events = base_name.split("^")
|
|
394
|
+
|
|
395
|
+
tmpRet[str(uuid.uuid4())] = {
|
|
396
|
+
"lfn": f"{iPFN:06d}:{base_name}",
|
|
397
|
+
"scope": None,
|
|
398
|
+
"filesize": 0,
|
|
399
|
+
"checksum": None,
|
|
400
|
+
"events": n_events,
|
|
401
|
+
}
|
|
402
|
+
except Exception:
|
|
403
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
404
|
+
reason = str(errvalue)
|
|
405
|
+
tmpLog.error(f"failed to get files in {self.__class__.__name__}:{errtype.__name__} due to {reason}")
|
|
406
|
+
if errtype == Interaction.JEDIFatalError:
|
|
407
|
+
# fatal error
|
|
408
|
+
datasetStatus = "broken"
|
|
409
|
+
taskBroken = True
|
|
410
|
+
# update dataset status
|
|
411
|
+
self.updateDatasetStatus(datasetSpec, datasetStatus, tmpLog)
|
|
412
|
+
else:
|
|
413
|
+
# temporary error
|
|
414
|
+
taskOnHold = True
|
|
415
|
+
taskSpec.setErrDiag(f"failed to get files for {datasetSpec.datasetName} due to {reason}")
|
|
416
|
+
allUpdated = False
|
|
417
|
+
else:
|
|
418
|
+
# parameters for master input
|
|
419
|
+
respectLB = False
|
|
420
|
+
useRealNumEvents = False
|
|
421
|
+
if datasetSpec.isMaster():
|
|
422
|
+
# respect LB boundaries
|
|
423
|
+
respectLB = taskSpec.respectLumiblock()
|
|
424
|
+
# use real number of events
|
|
425
|
+
useRealNumEvents = taskSpec.useRealNumEvents()
|
|
426
|
+
# the number of events per file
|
|
427
|
+
nEventsPerFile = None
|
|
428
|
+
nEventsPerJob = None
|
|
429
|
+
nEventsPerRange = None
|
|
430
|
+
tgtNumEventsPerJob = None
|
|
431
|
+
if (datasetSpec.isMaster() and ("nEventsPerFile" in taskParamMap or useRealNumEvents)) or (
|
|
432
|
+
datasetSpec.isPseudo() and "nEvents" in taskParamMap and not datasetSpec.isSeqNumber()
|
|
433
|
+
):
|
|
434
|
+
if "nEventsPerFile" in taskParamMap:
|
|
435
|
+
nEventsPerFile = taskParamMap["nEventsPerFile"]
|
|
436
|
+
elif datasetSpec.isMaster() and datasetSpec.isPseudo() and "nEvents" in taskParamMap:
|
|
437
|
+
# use nEvents as nEventsPerFile for pseudo input
|
|
438
|
+
nEventsPerFile = taskParamMap["nEvents"]
|
|
439
|
+
if taskSpec.get_min_granularity():
|
|
440
|
+
nEventsPerRange = taskSpec.get_min_granularity()
|
|
441
|
+
elif "nEventsPerJob" in taskParamMap:
|
|
442
|
+
nEventsPerJob = taskParamMap["nEventsPerJob"]
|
|
443
|
+
if "tgtNumEventsPerJob" in taskParamMap:
|
|
444
|
+
tgtNumEventsPerJob = taskParamMap["tgtNumEventsPerJob"]
|
|
445
|
+
# reset nEventsPerJob
|
|
446
|
+
nEventsPerJob = None
|
|
447
|
+
# max attempts
|
|
448
|
+
maxAttempt = None
|
|
449
|
+
maxFailure = None
|
|
450
|
+
if datasetSpec.isMaster() or datasetSpec.toKeepTrack():
|
|
451
|
+
# max attempts
|
|
452
|
+
if taskSpec.disableAutoRetry():
|
|
453
|
+
# disable auto retry
|
|
454
|
+
maxAttempt = 1
|
|
455
|
+
elif "maxAttempt" in taskParamMap:
|
|
456
|
+
maxAttempt = taskParamMap["maxAttempt"]
|
|
457
|
+
else:
|
|
458
|
+
# use default value
|
|
459
|
+
maxAttempt = 3
|
|
460
|
+
# max failure
|
|
461
|
+
if "maxFailure" in taskParamMap:
|
|
462
|
+
maxFailure = taskParamMap["maxFailure"]
|
|
463
|
+
# first event number
|
|
464
|
+
firstEventNumber = None
|
|
465
|
+
if datasetSpec.isMaster():
|
|
466
|
+
# first event number
|
|
467
|
+
firstEventNumber = 1 + taskSpec.getFirstEventOffset()
|
|
468
|
+
# nMaxEvents
|
|
469
|
+
nMaxEvents = None
|
|
470
|
+
if datasetSpec.isMaster() and "nEvents" in taskParamMap:
|
|
471
|
+
nMaxEvents = taskParamMap["nEvents"]
|
|
472
|
+
# nMaxFiles
|
|
473
|
+
nMaxFiles = None
|
|
474
|
+
if "nFiles" in taskParamMap:
|
|
475
|
+
if datasetSpec.isMaster():
|
|
476
|
+
nMaxFiles = taskParamMap["nFiles"]
|
|
477
|
+
else:
|
|
478
|
+
# calculate for secondary
|
|
479
|
+
if not datasetSpec.isPseudo():
|
|
480
|
+
# check nFilesPerJob
|
|
481
|
+
nFilesPerJobSec = datasetSpec.getNumFilesPerJob()
|
|
482
|
+
if nFilesPerJobSec is not None:
|
|
483
|
+
nMaxFiles = origNumFiles * nFilesPerJobSec
|
|
484
|
+
# check ratio
|
|
485
|
+
if nMaxFiles is None:
|
|
486
|
+
nMaxFiles = datasetSpec.getNumMultByRatio(origNumFiles)
|
|
487
|
+
# multiplied by the number of jobs per file for event-level splitting
|
|
488
|
+
if nMaxFiles is not None:
|
|
489
|
+
if "nEventsPerFile" in taskParamMap:
|
|
490
|
+
if taskSpec.get_min_granularity():
|
|
491
|
+
if taskParamMap["nEventsPerFile"] > taskSpec.get_min_granularity():
|
|
492
|
+
nMaxFiles *= float(taskParamMap["nEventsPerFile"]) / float(taskSpec.get_min_granularity())
|
|
493
|
+
nMaxFiles = int(math.ceil(nMaxFiles))
|
|
494
|
+
elif "nEventsPerJob" in taskParamMap:
|
|
495
|
+
if taskParamMap["nEventsPerFile"] > taskParamMap["nEventsPerJob"]:
|
|
496
|
+
nMaxFiles *= float(taskParamMap["nEventsPerFile"]) / float(taskParamMap["nEventsPerJob"])
|
|
497
|
+
nMaxFiles = int(math.ceil(nMaxFiles))
|
|
498
|
+
elif "useRealNumEvents" in taskParamMap:
|
|
499
|
+
# reset nMaxFiles since it is unknown
|
|
500
|
+
nMaxFiles = None
|
|
501
|
+
# use scout
|
|
502
|
+
useScout = False
|
|
503
|
+
if datasetSpec.isMaster() and taskSpec.useScout() and (datasetSpec.status != "toupdate" or not taskSpec.isPostScout()):
|
|
504
|
+
useScout = True
|
|
505
|
+
# use files with new attempt numbers
|
|
506
|
+
useFilesWithNewAttemptNr = False
|
|
507
|
+
if not datasetSpec.isPseudo() and fileList != [] and "useInFilesWithNewAttemptNr" in taskParamMap:
|
|
508
|
+
useFilesWithNewAttemptNr = True
|
|
509
|
+
# ramCount
|
|
510
|
+
ramCount = 0
|
|
511
|
+
# skip short input
|
|
512
|
+
if (
|
|
513
|
+
datasetSpec.isMaster()
|
|
514
|
+
and not datasetSpec.isPseudo()
|
|
515
|
+
and nEventsPerFile is not None
|
|
516
|
+
and nEventsPerJob is not None
|
|
517
|
+
and nEventsPerFile >= nEventsPerJob
|
|
518
|
+
and "skipShortInput" in taskParamMap
|
|
519
|
+
and taskParamMap["skipShortInput"] is True
|
|
520
|
+
):
|
|
521
|
+
skipShortInput = True
|
|
522
|
+
else:
|
|
523
|
+
skipShortInput = False
|
|
524
|
+
# skip short output
|
|
525
|
+
if datasetSpec.isMaster() and not datasetSpec.isPseudo() and taskParamMap.get("skipShortOutput"):
|
|
526
|
+
skip_short_output = True
|
|
527
|
+
else:
|
|
528
|
+
skip_short_output = False
|
|
529
|
+
# order by
|
|
530
|
+
if taskSpec.order_input_by() and datasetSpec.isMaster() and not datasetSpec.isPseudo():
|
|
531
|
+
orderBy = taskSpec.order_input_by()
|
|
532
|
+
else:
|
|
533
|
+
orderBy = None
|
|
534
|
+
# feed files to the contents table
|
|
535
|
+
tmpLog.debug("update contents")
|
|
536
|
+
res_dict = self.taskBufferIF.insertFilesForDataset_JEDI(
|
|
537
|
+
datasetSpec,
|
|
538
|
+
tmpRet,
|
|
539
|
+
tmpMetadata["state"],
|
|
540
|
+
stateUpdateTime,
|
|
541
|
+
nEventsPerFile,
|
|
542
|
+
nEventsPerJob,
|
|
543
|
+
maxAttempt,
|
|
544
|
+
firstEventNumber,
|
|
545
|
+
nMaxFiles,
|
|
546
|
+
nMaxEvents,
|
|
547
|
+
useScout,
|
|
548
|
+
fileList,
|
|
549
|
+
useFilesWithNewAttemptNr,
|
|
550
|
+
nFilesPerJob,
|
|
551
|
+
nEventsPerRange,
|
|
552
|
+
nChunksForScout,
|
|
553
|
+
includePatt,
|
|
554
|
+
excludePatt,
|
|
555
|
+
xmlConfig,
|
|
556
|
+
noWaitParent,
|
|
557
|
+
taskSpec.parent_tid,
|
|
558
|
+
self.pid,
|
|
559
|
+
maxFailure,
|
|
560
|
+
useRealNumEvents,
|
|
561
|
+
respectLB,
|
|
562
|
+
tgtNumEventsPerJob,
|
|
563
|
+
skipFilesUsedBy,
|
|
564
|
+
ramCount,
|
|
565
|
+
taskSpec,
|
|
566
|
+
skipShortInput,
|
|
567
|
+
inputPreStaging,
|
|
568
|
+
orderBy,
|
|
569
|
+
maxFileRecords,
|
|
570
|
+
skip_short_output,
|
|
571
|
+
)
|
|
572
|
+
retDB = res_dict["ret_val"]
|
|
573
|
+
missingFileList = res_dict["missingFileList"]
|
|
574
|
+
nFilesUnique = res_dict["numUniqueLfn"]
|
|
575
|
+
diagMap = res_dict["diagMap"]
|
|
576
|
+
if retDB is False:
|
|
577
|
+
taskSpec.setErrDiag(f"failed to insert files for {datasetSpec.datasetName}. {diagMap['errMsg']}")
|
|
578
|
+
allUpdated = False
|
|
579
|
+
taskBroken = True
|
|
580
|
+
break
|
|
581
|
+
elif retDB is None:
|
|
582
|
+
# the dataset is locked by another or status is not applicable
|
|
583
|
+
allUpdated = False
|
|
584
|
+
tmpLog.debug("escape since task or dataset is locked")
|
|
585
|
+
break
|
|
586
|
+
elif missingFileList != []:
|
|
587
|
+
# files are missing
|
|
588
|
+
tmpErrStr = f"{len(missingFileList)} files missing in {datasetSpec.datasetName}"
|
|
589
|
+
tmpLog.debug(tmpErrStr)
|
|
590
|
+
taskSpec.setErrDiag(tmpErrStr)
|
|
591
|
+
allUpdated = False
|
|
592
|
+
taskOnHold = True
|
|
593
|
+
missingMap[datasetSpec.datasetName] = {"datasetSpec": datasetSpec, "missingFiles": missingFileList}
|
|
594
|
+
else:
|
|
595
|
+
# reduce the number of files to be read
|
|
596
|
+
if "nFiles" in taskParamMap:
|
|
597
|
+
if datasetSpec.isMaster():
|
|
598
|
+
taskParamMap["nFiles"] -= nFilesUnique
|
|
599
|
+
# reduce the number of files for scout
|
|
600
|
+
if useScout:
|
|
601
|
+
nChunksForScout = diagMap["nChunksForScout"]
|
|
602
|
+
# number of master input files
|
|
603
|
+
if datasetSpec.isMaster():
|
|
604
|
+
checkedMaster = True
|
|
605
|
+
nFilesMaster += nFilesUnique
|
|
606
|
+
nFilesMasterReady += res_dict.get("nReady", 0)
|
|
607
|
+
master_offset = datasetSpec.getOffset()
|
|
608
|
+
# running task
|
|
609
|
+
if diagMap["isRunningTask"]:
|
|
610
|
+
runningTask = True
|
|
611
|
+
# no activated pending input for noWait
|
|
612
|
+
if (
|
|
613
|
+
(noWaitParent or inputPreStaging)
|
|
614
|
+
and diagMap["nActivatedPending"] == 0
|
|
615
|
+
and not (useScout and nChunksForScout <= 0)
|
|
616
|
+
and tmpMetadata["state"] != "closed"
|
|
617
|
+
and datasetSpec.isMaster()
|
|
618
|
+
):
|
|
619
|
+
tmpErrStr = "insufficient inputs are ready. "
|
|
620
|
+
if inputPreStaging:
|
|
621
|
+
# message for pending for staging
|
|
622
|
+
tmpErrStr += "inputs are staging from a tape. "
|
|
623
|
+
tmpErrStr += diagMap["errMsg"]
|
|
624
|
+
tmpLog.debug(tmpErrStr + ". skipping secondary datasets from now")
|
|
625
|
+
taskSpec.setErrDiag(tmpErrStr)
|
|
626
|
+
taskOnHold = True
|
|
627
|
+
setFrozenTime = False
|
|
628
|
+
skip_secondaries = True
|
|
629
|
+
tmpLog.debug("end loop")
|
|
630
|
+
# task holdup by workflow if no master inputs are ready
|
|
631
|
+
if not taskOnHold and not taskBroken and allUpdated and nFilesMasterReady == 0 and checkedMaster and taskSpec.is_workflow_holdup():
|
|
632
|
+
# hold up by the workflow
|
|
633
|
+
taskOnHold = True
|
|
634
|
+
tmpLog.debug("task to hold up by workflow")
|
|
635
|
+
# no master input
|
|
636
|
+
if not taskOnHold and not taskBroken and allUpdated and nFilesMaster == 0 and checkedMaster:
|
|
637
|
+
tmpErrStr = "no master input files. input dataset is empty"
|
|
638
|
+
if master_offset:
|
|
639
|
+
tmpErrStr += f" with offset={master_offset}"
|
|
640
|
+
tmpLog.error(tmpErrStr)
|
|
641
|
+
taskSpec.setErrDiag(tmpErrStr, None)
|
|
642
|
+
if noWaitParent:
|
|
643
|
+
# parent is running
|
|
644
|
+
taskOnHold = True
|
|
645
|
+
else:
|
|
646
|
+
# the task has no parent or parent is finished
|
|
647
|
+
if master_is_open and taskSpec.runUntilClosed():
|
|
648
|
+
# wait until the input dataset is closed
|
|
649
|
+
taskOnHold = True
|
|
650
|
+
elif not taskSpec.allowEmptyInput():
|
|
651
|
+
# empty input is not allowed
|
|
652
|
+
taskBroken = True
|
|
653
|
+
else:
|
|
654
|
+
# finish the task with empty input
|
|
655
|
+
taskToFinish = True
|
|
656
|
+
# index consistency
|
|
657
|
+
if not taskOnHold and not taskBroken and len(datasetsIdxConsistency) > 0:
|
|
658
|
+
self.taskBufferIF.removeFilesIndexInconsistent_JEDI(jediTaskID, datasetsIdxConsistency)
|
|
659
|
+
# update task status
|
|
660
|
+
if taskBroken or taskToFinish:
|
|
661
|
+
if taskBroken:
|
|
662
|
+
# task is broken
|
|
663
|
+
taskSpec.status = "tobroken"
|
|
664
|
+
else:
|
|
665
|
+
taskSpec.status = "finishing"
|
|
666
|
+
tmpMsg = f"set task_status={taskSpec.status}"
|
|
667
|
+
tmpLog.info(tmpMsg)
|
|
668
|
+
tmpLog.sendMsg(tmpMsg, self.msgType)
|
|
669
|
+
allRet = self.taskBufferIF.updateTaskStatusByContFeeder_JEDI(jediTaskID, taskSpec, pid=self.pid)
|
|
670
|
+
# change task status unless the task is running
|
|
671
|
+
if not runningTask:
|
|
672
|
+
# send prestaging request
|
|
673
|
+
if taskSpec.inputPreStaging() and taskSpec.is_first_contents_feed():
|
|
674
|
+
tmpStat, tmpErrStr = self.send_prestaging_request(taskSpec, taskParamMap, dsList, tmpLog)
|
|
675
|
+
if tmpStat:
|
|
676
|
+
taskSpec.set_first_contents_feed(False)
|
|
677
|
+
else:
|
|
678
|
+
tmpLog.debug(tmpErrStr)
|
|
679
|
+
taskSpec.setErrDiag(tmpErrStr)
|
|
680
|
+
taskOnHold = True
|
|
681
|
+
if taskOnHold:
|
|
682
|
+
# go to pending state
|
|
683
|
+
if taskSpec.status not in ["broken", "tobroken", "finishing"]:
|
|
684
|
+
taskSpec.setOnHold()
|
|
685
|
+
tmpMsg = f"set task_status={taskSpec.status}"
|
|
686
|
+
tmpLog.info(tmpMsg)
|
|
687
|
+
tmpLog.sendMsg(tmpMsg, self.msgType)
|
|
688
|
+
allRet = self.taskBufferIF.updateTaskStatusByContFeeder_JEDI(jediTaskID, taskSpec, pid=self.pid, setFrozenTime=setFrozenTime)
|
|
689
|
+
elif allUpdated:
|
|
690
|
+
# all OK
|
|
691
|
+
allRet, newTaskStatus = self.taskBufferIF.updateTaskStatusByContFeeder_JEDI(
|
|
692
|
+
jediTaskID, getTaskStatus=True, pid=self.pid, useWorldCloud=taskSpec.useWorldCloud()
|
|
693
|
+
)
|
|
694
|
+
tmpMsg = f"set task_status={newTaskStatus}"
|
|
695
|
+
tmpLog.info(tmpMsg)
|
|
696
|
+
tmpLog.sendMsg(tmpMsg, self.msgType)
|
|
697
|
+
# just unlock
|
|
698
|
+
retUnlock = self.taskBufferIF.unlockSingleTask_JEDI(jediTaskID, self.pid)
|
|
699
|
+
tmpLog.debug(f"unlock not-running task with {retUnlock}")
|
|
700
|
+
else:
|
|
701
|
+
# just unlock
|
|
702
|
+
retUnlock = self.taskBufferIF.unlockSingleTask_JEDI(jediTaskID, self.pid)
|
|
703
|
+
tmpLog.debug(f"unlock task with {retUnlock}")
|
|
704
|
+
# send message to job generator if new inputs are ready
|
|
705
|
+
if not taskOnHold and not taskBroken and allUpdated and taskSpec.is_msg_driven():
|
|
706
|
+
push_ret = self.taskBufferIF.push_task_trigger_message("jedi_job_generator", jediTaskID, task_spec=taskSpec)
|
|
707
|
+
if push_ret:
|
|
708
|
+
tmpLog.debug("pushed trigger message to jedi_job_generator")
|
|
709
|
+
else:
|
|
710
|
+
tmpLog.warning("failed to push trigger message to jedi_job_generator")
|
|
711
|
+
tmpLog.debug("done")
|
|
712
|
+
|
|
713
|
+
# update dataset
|
|
714
|
+
def updateDatasetStatus(self, datasetSpec, datasetStatus, tmpLog, datasetState=None):
|
|
715
|
+
# update dataset status
|
|
716
|
+
datasetSpec.status = datasetStatus
|
|
717
|
+
datasetSpec.lockedBy = None
|
|
718
|
+
if datasetState:
|
|
719
|
+
datasetSpec.state = datasetState
|
|
720
|
+
tmpLog.info(f"update dataset status to {datasetSpec.status} state to {datasetSpec.state}")
|
|
721
|
+
self.taskBufferIF.updateDataset_JEDI(datasetSpec, {"datasetID": datasetSpec.datasetID, "jediTaskID": datasetSpec.jediTaskID}, lockTask=True)
|
|
722
|
+
|
|
723
|
+
# send prestaging request
|
|
724
|
+
def send_prestaging_request(self, task_spec, task_params_map, ds_list, tmp_log):
|
|
725
|
+
try:
|
|
726
|
+
c = iDDS_Client(idds.common.utils.get_rest_host())
|
|
727
|
+
for datasetSpec in ds_list:
|
|
728
|
+
if datasetSpec.is_no_staging():
|
|
729
|
+
# skip no_staging
|
|
730
|
+
continue
|
|
731
|
+
# get rule
|
|
732
|
+
try:
|
|
733
|
+
tmp_scope, tmp_name = datasetSpec.datasetName.split(":")
|
|
734
|
+
tmp_name = re.sub("/$", "", tmp_name)
|
|
735
|
+
except Exception:
|
|
736
|
+
continue
|
|
737
|
+
try:
|
|
738
|
+
if "prestagingRuleID" in task_params_map:
|
|
739
|
+
if tmp_name not in task_params_map["prestagingRuleID"]:
|
|
740
|
+
continue
|
|
741
|
+
rule_id = task_params_map["prestagingRuleID"][tmp_name]
|
|
742
|
+
elif "selfPrestagingRule" in task_params_map:
|
|
743
|
+
if not datasetSpec.isMaster() or datasetSpec.isPseudo():
|
|
744
|
+
continue
|
|
745
|
+
rule_id = self.ddmIF.getInterface(task_spec.vo, task_spec.cloud).make_staging_rule(
|
|
746
|
+
tmp_scope + ":" + tmp_name, task_params_map["selfPrestagingRule"]
|
|
747
|
+
)
|
|
748
|
+
if not rule_id:
|
|
749
|
+
continue
|
|
750
|
+
else:
|
|
751
|
+
rule_id = self.ddmIF.getInterface(task_spec.vo, task_spec.cloud).getActiveStagingRule(tmp_scope + ":" + tmp_name)
|
|
752
|
+
if rule_id is None:
|
|
753
|
+
continue
|
|
754
|
+
except Exception as e:
|
|
755
|
+
return False, f"DDM error : {str(e)}"
|
|
756
|
+
# request
|
|
757
|
+
tmp_log.debug(f"sending request to iDDS for {datasetSpec.datasetName}")
|
|
758
|
+
req = {
|
|
759
|
+
"scope": tmp_scope,
|
|
760
|
+
"name": tmp_name,
|
|
761
|
+
"requester": "panda",
|
|
762
|
+
"request_type": idds.common.constants.RequestType.StageIn,
|
|
763
|
+
"transform_tag": idds.common.constants.RequestType.StageIn.value,
|
|
764
|
+
"status": idds.common.constants.RequestStatus.New,
|
|
765
|
+
"priority": 0,
|
|
766
|
+
"lifetime": 30,
|
|
767
|
+
"request_metadata": {
|
|
768
|
+
"workload_id": task_spec.jediTaskID,
|
|
769
|
+
"rule_id": rule_id,
|
|
770
|
+
},
|
|
771
|
+
}
|
|
772
|
+
tmp_log.debug(f"req {str(req)}")
|
|
773
|
+
ret = c.add_request(**req)
|
|
774
|
+
tmp_log.debug(f"got requestID={str(ret)}")
|
|
775
|
+
except Exception as e:
|
|
776
|
+
return False, f"iDDS error : {str(e)}"
|
|
777
|
+
return True, None
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
# launch
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
def launcher(commuChannel, taskBufferIF, ddmIF, vos=None, prodSourceLabels=None):
|
|
784
|
+
p = ContentsFeeder(commuChannel, taskBufferIF, ddmIF, vos, prodSourceLabels)
|
|
785
|
+
p.start()
|