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,629 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import datetime
|
|
3
|
+
import math
|
|
4
|
+
import random
|
|
5
|
+
import sys
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
9
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
10
|
+
|
|
11
|
+
from pandajedi.jedicore import Interaction
|
|
12
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
13
|
+
from pandajedi.jedicore.ThreadUtils import (
|
|
14
|
+
ListWithLock,
|
|
15
|
+
MapWithLock,
|
|
16
|
+
ThreadPool,
|
|
17
|
+
WorkerThread,
|
|
18
|
+
)
|
|
19
|
+
from pandajedi.jedirefine import RefinerUtils
|
|
20
|
+
from pandaserver.dataservice import DataServiceUtils
|
|
21
|
+
|
|
22
|
+
from . import AtlasBrokerUtils
|
|
23
|
+
from .AtlasProdJobBroker import AtlasProdJobBroker
|
|
24
|
+
from .TaskBrokerBase import TaskBrokerBase
|
|
25
|
+
|
|
26
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# brokerage for ATLAS production
|
|
30
|
+
class AtlasProdTaskBroker(TaskBrokerBase):
|
|
31
|
+
# constructor
|
|
32
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
33
|
+
TaskBrokerBase.__init__(self, taskBufferIF, ddmIF)
|
|
34
|
+
|
|
35
|
+
# main to check
|
|
36
|
+
def doCheck(self, taskSpecList):
|
|
37
|
+
return self.SC_SUCCEEDED, {}
|
|
38
|
+
|
|
39
|
+
# main to assign
|
|
40
|
+
def doBrokerage(self, inputList, vo, prodSourceLabel, workQueue, resource_name):
|
|
41
|
+
# list with a lock
|
|
42
|
+
inputListWorld = ListWithLock([])
|
|
43
|
+
|
|
44
|
+
# make logger
|
|
45
|
+
tmpLog = MsgWrapper(logger)
|
|
46
|
+
tmpLog.debug("start doBrokerage")
|
|
47
|
+
|
|
48
|
+
# return for failure
|
|
49
|
+
retTmpError = self.SC_FAILED
|
|
50
|
+
tmpLog.debug(f"vo={vo} label={prodSourceLabel} queue={workQueue.queue_name} resource_name={resource_name} nTasks={len(inputList)}")
|
|
51
|
+
|
|
52
|
+
# build the map with Remaining Work by priority
|
|
53
|
+
allRwMap = {}
|
|
54
|
+
|
|
55
|
+
# loop over all tasks and build the list of WORLD tasks. Nowadays all tasks are WORLD
|
|
56
|
+
for tmpJediTaskID, tmpInputList in inputList:
|
|
57
|
+
for taskSpec, cloudName, inputChunk in tmpInputList:
|
|
58
|
+
if taskSpec.useWorldCloud():
|
|
59
|
+
inputListWorld.append((taskSpec, inputChunk))
|
|
60
|
+
|
|
61
|
+
# broker WORLD tasks
|
|
62
|
+
if inputListWorld:
|
|
63
|
+
# thread pool
|
|
64
|
+
threadPool = ThreadPool()
|
|
65
|
+
# get full RW for WORLD
|
|
66
|
+
fullRWs = self.taskBufferIF.calculateWorldRWwithPrio_JEDI(vo, prodSourceLabel, None, None)
|
|
67
|
+
if fullRWs is None:
|
|
68
|
+
tmpLog.error("failed to calculate full WORLD RW")
|
|
69
|
+
return retTmpError
|
|
70
|
+
# get RW per priority
|
|
71
|
+
for taskSpec, inputChunk in inputListWorld:
|
|
72
|
+
if taskSpec.currentPriority not in allRwMap:
|
|
73
|
+
tmpRW = self.taskBufferIF.calculateWorldRWwithPrio_JEDI(vo, prodSourceLabel, workQueue, taskSpec.currentPriority)
|
|
74
|
+
if tmpRW is None:
|
|
75
|
+
tmpLog.error(f"failed to calculate RW with prio={taskSpec.currentPriority}")
|
|
76
|
+
return retTmpError
|
|
77
|
+
allRwMap[taskSpec.currentPriority] = tmpRW
|
|
78
|
+
|
|
79
|
+
# live counter for RWs
|
|
80
|
+
liveCounter = MapWithLock(allRwMap)
|
|
81
|
+
# make workers
|
|
82
|
+
ddmIF = self.ddmIF.getInterface(vo)
|
|
83
|
+
for iWorker in range(4):
|
|
84
|
+
thr = AtlasProdTaskBrokerThread(inputListWorld, threadPool, self.taskBufferIF, ddmIF, fullRWs, liveCounter, workQueue)
|
|
85
|
+
thr.start()
|
|
86
|
+
threadPool.join(60 * 10)
|
|
87
|
+
# return
|
|
88
|
+
tmpLog.debug("doBrokerage done")
|
|
89
|
+
return self.SC_SUCCEEDED
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# thread for real worker
|
|
93
|
+
class AtlasProdTaskBrokerThread(WorkerThread):
|
|
94
|
+
# constructor
|
|
95
|
+
def __init__(self, inputList, threadPool, taskbufferIF, ddmIF, fullRW, prioRW, workQueue):
|
|
96
|
+
# initialize woker with no semaphore
|
|
97
|
+
WorkerThread.__init__(self, None, threadPool, logger)
|
|
98
|
+
# attributres
|
|
99
|
+
self.inputList = inputList
|
|
100
|
+
self.taskBufferIF = taskbufferIF
|
|
101
|
+
self.ddmIF = ddmIF
|
|
102
|
+
self.msgType = "taskbrokerage"
|
|
103
|
+
self.fullRW = fullRW
|
|
104
|
+
self.prioRW = prioRW
|
|
105
|
+
self.numTasks = 0
|
|
106
|
+
self.workQueue = workQueue
|
|
107
|
+
self.summaryList = None
|
|
108
|
+
|
|
109
|
+
# init summary list
|
|
110
|
+
def init_summary_list(self, header, comment, initial_list):
|
|
111
|
+
self.summaryList = []
|
|
112
|
+
self.summaryList.append(f"===== {header} =====")
|
|
113
|
+
if comment:
|
|
114
|
+
self.summaryList.append(comment)
|
|
115
|
+
self.summaryList.append(f"the number of initial candidates: {len(initial_list)}")
|
|
116
|
+
|
|
117
|
+
# dump summary
|
|
118
|
+
def dump_summary(self, tmp_log, final_candidates=None):
|
|
119
|
+
if not self.summaryList:
|
|
120
|
+
return
|
|
121
|
+
tmp_log.info("")
|
|
122
|
+
for m in self.summaryList:
|
|
123
|
+
tmp_log.info(m)
|
|
124
|
+
if not final_candidates:
|
|
125
|
+
final_candidates = []
|
|
126
|
+
tmp_log.info(f"the number of final candidates: {len(final_candidates)}")
|
|
127
|
+
tmp_log.info("")
|
|
128
|
+
|
|
129
|
+
# make summary
|
|
130
|
+
def add_summary_message(self, old_list, new_list, message):
|
|
131
|
+
if old_list and len(old_list) != len(new_list):
|
|
132
|
+
red = int(math.ceil(((len(old_list) - len(new_list)) * 100) / len(old_list)))
|
|
133
|
+
self.summaryList.append(f"{len(old_list):>5} -> {len(new_list):>3} candidates, {red:>3}% cut : {message}")
|
|
134
|
+
|
|
135
|
+
# post-process for errors
|
|
136
|
+
def post_process_for_error(self, task_spec, tmp_log, msg, dump_summary=True):
|
|
137
|
+
if dump_summary:
|
|
138
|
+
self.dump_summary(tmp_log)
|
|
139
|
+
tmp_log.error(msg)
|
|
140
|
+
task_spec.resetChangedList()
|
|
141
|
+
task_spec.setErrDiag(tmp_log.uploadLog(task_spec.jediTaskID))
|
|
142
|
+
self.taskBufferIF.updateTask_JEDI(task_spec, {"jediTaskID": task_spec.jediTaskID}, oldStatus=["assigning"], updateDEFT=False, setFrozenTime=False)
|
|
143
|
+
|
|
144
|
+
# main function
|
|
145
|
+
def runImpl(self):
|
|
146
|
+
# cutoff for disk in TB
|
|
147
|
+
diskThreshold = self.taskBufferIF.getConfigValue(self.msgType, f"DISK_THRESHOLD_{self.workQueue.queue_name}", "jedi", "atlas")
|
|
148
|
+
if diskThreshold is None:
|
|
149
|
+
diskThreshold = self.taskBufferIF.getConfigValue(self.msgType, "DISK_THRESHOLD", "jedi", "atlas")
|
|
150
|
+
if diskThreshold is None:
|
|
151
|
+
diskThreshold = 100
|
|
152
|
+
diskThreshold *= 1024
|
|
153
|
+
# cutoff for free disk in TB
|
|
154
|
+
free_disk_cutoff = self.taskBufferIF.getConfigValue(self.msgType, f"FREE_DISK_CUTOFF", "jedi", "atlas")
|
|
155
|
+
if free_disk_cutoff is None:
|
|
156
|
+
free_disk_cutoff = 1000
|
|
157
|
+
# dataset type to ignore file availability check
|
|
158
|
+
datasetTypeToSkipCheck = ["log"]
|
|
159
|
+
# thresholds for data availability check
|
|
160
|
+
thrInputSize = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_SIZE_THRESHOLD", "jedi", "atlas")
|
|
161
|
+
if thrInputSize is None:
|
|
162
|
+
thrInputSize = 1
|
|
163
|
+
thrInputSize *= 1024 * 1024 * 1024
|
|
164
|
+
thrInputNum = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_NUM_THRESHOLD", "jedi", "atlas")
|
|
165
|
+
if thrInputNum is None:
|
|
166
|
+
thrInputNum = 100
|
|
167
|
+
thrInputSizeFrac = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_SIZE_FRACTION", "jedi", "atlas")
|
|
168
|
+
if thrInputSizeFrac is None:
|
|
169
|
+
thrInputSizeFrac = 10
|
|
170
|
+
thrInputSizeFrac = float(thrInputSizeFrac) / 100
|
|
171
|
+
thrInputNumFrac = self.taskBufferIF.getConfigValue(self.msgType, "INPUT_NUM_FRACTION", "jedi", "atlas")
|
|
172
|
+
if thrInputNumFrac is None:
|
|
173
|
+
thrInputNumFrac = 10
|
|
174
|
+
thrInputNumFrac = float(thrInputNumFrac) / 100
|
|
175
|
+
cutOffRW = 50
|
|
176
|
+
negWeightTape = 0.001
|
|
177
|
+
minIoIntensityWithLD = self.taskBufferIF.getConfigValue(self.msgType, "MIN_IO_INTENSITY_WITH_LOCAL_DATA", "jedi", "atlas")
|
|
178
|
+
if minIoIntensityWithLD is None:
|
|
179
|
+
minIoIntensityWithLD = 200
|
|
180
|
+
minInputSizeWithLD = self.taskBufferIF.getConfigValue(self.msgType, "MIN_INPUT_SIZE_WITH_LOCAL_DATA", "jedi", "atlas")
|
|
181
|
+
if minInputSizeWithLD is None:
|
|
182
|
+
minInputSizeWithLD = 10000
|
|
183
|
+
maxTaskPrioWithLD = self.taskBufferIF.getConfigValue(self.msgType, "MAX_TASK_PRIO_WITH_LOCAL_DATA", "jedi", "atlas")
|
|
184
|
+
if maxTaskPrioWithLD is None:
|
|
185
|
+
maxTaskPrioWithLD = 800
|
|
186
|
+
# ignore data locality once the period passes (in days)
|
|
187
|
+
data_location_check_period = self.taskBufferIF.getConfigValue(self.msgType, "DATA_LOCATION_CHECK_PERIOD", "jedi", "atlas")
|
|
188
|
+
if not data_location_check_period:
|
|
189
|
+
data_location_check_period = 7
|
|
190
|
+
# main
|
|
191
|
+
lastJediTaskID = None
|
|
192
|
+
siteMapper = self.taskBufferIF.get_site_mapper()
|
|
193
|
+
while True:
|
|
194
|
+
try:
|
|
195
|
+
taskInputList = self.inputList.get(1)
|
|
196
|
+
# no more datasets
|
|
197
|
+
if len(taskInputList) == 0:
|
|
198
|
+
self.logger.debug(f"{self.__class__.__name__} terminating after processing {self.numTasks} tasks since no more inputs ")
|
|
199
|
+
return
|
|
200
|
+
# loop over all tasks
|
|
201
|
+
for taskSpec, inputChunk in taskInputList:
|
|
202
|
+
lastJediTaskID = taskSpec.jediTaskID
|
|
203
|
+
# make logger
|
|
204
|
+
tmpLog = MsgWrapper(self.logger, f"<jediTaskID={taskSpec.jediTaskID}>", monToken=f"jediTaskID={taskSpec.jediTaskID}")
|
|
205
|
+
tmpLog.debug("start")
|
|
206
|
+
tmpLog.info(f"thrInputSize:{thrInputSize} thrInputNum:{thrInputNum} thrInputSizeFrac:{thrInputSizeFrac} thrInputNumFrac:{thrInputNumFrac}")
|
|
207
|
+
tmpLog.info(f"full-chain:{taskSpec.get_full_chain()} ioIntensity={taskSpec.ioIntensity}")
|
|
208
|
+
# read task parameters
|
|
209
|
+
try:
|
|
210
|
+
taskParam = self.taskBufferIF.getTaskParamsWithID_JEDI(taskSpec.jediTaskID)
|
|
211
|
+
taskParamMap = RefinerUtils.decodeJSON(taskParam)
|
|
212
|
+
except Exception:
|
|
213
|
+
tmpLog.error("failed to read task params")
|
|
214
|
+
taskSpec.resetChangedList()
|
|
215
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
216
|
+
self.taskBufferIF.updateTask_JEDI(
|
|
217
|
+
taskSpec, {"jediTaskID": taskSpec.jediTaskID}, oldStatus=["assigning"], updateDEFT=False, setFrozenTime=False
|
|
218
|
+
)
|
|
219
|
+
continue
|
|
220
|
+
# RW
|
|
221
|
+
taskRW = self.taskBufferIF.calculateTaskWorldRW_JEDI(taskSpec.jediTaskID)
|
|
222
|
+
# get nuclei
|
|
223
|
+
nucleusList = copy.copy(siteMapper.nuclei)
|
|
224
|
+
if taskSpec.get_full_chain():
|
|
225
|
+
# use satellites with bareNucleus as nuclei for full chain
|
|
226
|
+
for tmpNucleus, tmpNucleusSpec in siteMapper.satellites.items():
|
|
227
|
+
if tmpNucleusSpec.get_bare_nucleus_mode():
|
|
228
|
+
nucleusList[tmpNucleus] = tmpNucleusSpec
|
|
229
|
+
# init summary list
|
|
230
|
+
self.init_summary_list("Task brokerage summary", None, nucleusList)
|
|
231
|
+
if taskSpec.nucleus in siteMapper.nuclei:
|
|
232
|
+
candidateNucleus = taskSpec.nucleus
|
|
233
|
+
elif taskSpec.nucleus in siteMapper.satellites:
|
|
234
|
+
nucleusList = siteMapper.satellites
|
|
235
|
+
candidateNucleus = taskSpec.nucleus
|
|
236
|
+
else:
|
|
237
|
+
tmpLog.info(f"got {len(nucleusList)} candidates")
|
|
238
|
+
######################################
|
|
239
|
+
# check data
|
|
240
|
+
dataset_availability_info = {}
|
|
241
|
+
to_skip = False
|
|
242
|
+
for datasetSpec in inputChunk.getDatasets():
|
|
243
|
+
# only for real datasets
|
|
244
|
+
if datasetSpec.isPseudo():
|
|
245
|
+
continue
|
|
246
|
+
# ignore DBR
|
|
247
|
+
if DataServiceUtils.isDBR(datasetSpec.datasetName):
|
|
248
|
+
continue
|
|
249
|
+
# skip locality check
|
|
250
|
+
if DataServiceUtils.getDatasetType(datasetSpec.datasetName) in datasetTypeToSkipCheck:
|
|
251
|
+
continue
|
|
252
|
+
# primary only
|
|
253
|
+
if taskParamMap.get("taskBrokerOnMaster") is True and not datasetSpec.isMaster():
|
|
254
|
+
continue
|
|
255
|
+
# use deep scan for primary dataset unless data carousel
|
|
256
|
+
if datasetSpec.isMaster() and not taskSpec.inputPreStaging():
|
|
257
|
+
deepScan = True
|
|
258
|
+
else:
|
|
259
|
+
deepScan = False
|
|
260
|
+
# get nuclei where data is available
|
|
261
|
+
tmp_status, tmp_data_map, remote_source_available = AtlasBrokerUtils.getNucleiWithData(
|
|
262
|
+
siteMapper, self.ddmIF, datasetSpec.datasetName, list(nucleusList.keys()), deepScan
|
|
263
|
+
)
|
|
264
|
+
if tmp_status != Interaction.SC_SUCCEEDED:
|
|
265
|
+
self.post_process_for_error(taskSpec, tmpLog, f"failed to get nuclei where data is available, since {tmp_data_map}", False)
|
|
266
|
+
to_skip = True
|
|
267
|
+
break
|
|
268
|
+
# sum
|
|
269
|
+
for tmpNucleus, tmpVals in tmp_data_map.items():
|
|
270
|
+
if tmpNucleus not in dataset_availability_info:
|
|
271
|
+
dataset_availability_info[tmpNucleus] = tmpVals
|
|
272
|
+
else:
|
|
273
|
+
dataset_availability_info[tmpNucleus] = dict(
|
|
274
|
+
(k, v + tmpVals[k]) for (k, v) in dataset_availability_info[tmpNucleus].items() if not isinstance(v, bool)
|
|
275
|
+
)
|
|
276
|
+
# set remote_source_available to True if any is readable over WAN
|
|
277
|
+
if tmpVals.get("can_be_remote_source"):
|
|
278
|
+
dataset_availability_info[tmpNucleus]["can_be_remote_source"] = True
|
|
279
|
+
if not remote_source_available:
|
|
280
|
+
self.post_process_for_error(
|
|
281
|
+
taskSpec, tmpLog, f"dataset={datasetSpec.datasetName} is incomplete/missing at online endpoints with read_wan=ON", False
|
|
282
|
+
)
|
|
283
|
+
to_skip = True
|
|
284
|
+
break
|
|
285
|
+
if to_skip:
|
|
286
|
+
continue
|
|
287
|
+
######################################
|
|
288
|
+
# check status
|
|
289
|
+
newNucleusList = {}
|
|
290
|
+
oldNucleusList = copy.copy(nucleusList)
|
|
291
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
292
|
+
if tmpNucleusSpec.state not in ["ACTIVE"]:
|
|
293
|
+
tmpLog.info(f" skip nucleus={tmpNucleus} due to status={tmpNucleusSpec.state} criteria=-status")
|
|
294
|
+
else:
|
|
295
|
+
newNucleusList[tmpNucleus] = tmpNucleusSpec
|
|
296
|
+
nucleusList = newNucleusList
|
|
297
|
+
tmpLog.info(f"{len(nucleusList)} candidates passed status check")
|
|
298
|
+
self.add_summary_message(oldNucleusList, nucleusList, "status check")
|
|
299
|
+
if not nucleusList:
|
|
300
|
+
self.post_process_for_error(taskSpec, tmpLog, "no candidates")
|
|
301
|
+
continue
|
|
302
|
+
######################################
|
|
303
|
+
# check status of transfer backlog
|
|
304
|
+
t1Weight = taskSpec.getT1Weight()
|
|
305
|
+
if t1Weight < 0:
|
|
306
|
+
tmpLog.info("skip transfer backlog check due to negative T1Weight")
|
|
307
|
+
else:
|
|
308
|
+
newNucleusList = {}
|
|
309
|
+
oldNucleusList = copy.copy(nucleusList)
|
|
310
|
+
backlogged_nuclei = self.taskBufferIF.getBackloggedNuclei()
|
|
311
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
312
|
+
if tmpNucleus in backlogged_nuclei:
|
|
313
|
+
tmpLog.info(f" skip nucleus={tmpNucleus} due to long transfer backlog criteria=-transfer_backlog")
|
|
314
|
+
else:
|
|
315
|
+
newNucleusList[tmpNucleus] = tmpNucleusSpec
|
|
316
|
+
nucleusList = newNucleusList
|
|
317
|
+
tmpLog.info(f"{len(nucleusList)} candidates passed transfer backlog check")
|
|
318
|
+
self.add_summary_message(oldNucleusList, nucleusList, "transfer backlog check")
|
|
319
|
+
if not nucleusList:
|
|
320
|
+
self.post_process_for_error(taskSpec, tmpLog, "no candidates")
|
|
321
|
+
continue
|
|
322
|
+
######################################
|
|
323
|
+
# check endpoint
|
|
324
|
+
fractionFreeSpace = {}
|
|
325
|
+
newNucleusList = {}
|
|
326
|
+
oldNucleusList = copy.copy(nucleusList)
|
|
327
|
+
tmpStat, tmpDatasetSpecList = self.taskBufferIF.getDatasetsWithJediTaskID_JEDI(taskSpec.jediTaskID, ["output", "log"])
|
|
328
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
329
|
+
to_skip = False
|
|
330
|
+
origNucleusSpec = tmpNucleusSpec
|
|
331
|
+
for tmpDatasetSpec in tmpDatasetSpecList:
|
|
332
|
+
tmpNucleusSpec = origNucleusSpec
|
|
333
|
+
# use secondary nucleus for full-chain if defined
|
|
334
|
+
if taskSpec.get_full_chain() and tmpNucleusSpec.get_secondary_nucleus():
|
|
335
|
+
tmpNucleusSpec = siteMapper.getNucleus(tmpNucleusSpec.get_secondary_nucleus())
|
|
336
|
+
# ignore distributed datasets
|
|
337
|
+
if DataServiceUtils.getDistributedDestination(tmpDatasetSpec.storageToken) is not None:
|
|
338
|
+
continue
|
|
339
|
+
# get endpoint with the pattern
|
|
340
|
+
tmpEP = tmpNucleusSpec.getAssociatedEndpoint(tmpDatasetSpec.storageToken)
|
|
341
|
+
tmp_ddm_endpoint_name = tmpEP["ddm_endpoint_name"]
|
|
342
|
+
if tmpEP is None:
|
|
343
|
+
tmpLog.info(f" skip nucleus={tmpNucleus} since no endpoint with {tmpDatasetSpec.storageToken} criteria=-match")
|
|
344
|
+
to_skip = True
|
|
345
|
+
break
|
|
346
|
+
# check blacklist
|
|
347
|
+
read_wan_status = tmpEP["detailed_status"].get("read_wan")
|
|
348
|
+
if read_wan_status in ["OFF", "TEST"]:
|
|
349
|
+
tmpLog.info(
|
|
350
|
+
f" skip nucleus={tmpNucleus} since {tmp_ddm_endpoint_name} has read_wan={read_wan_status} criteria=-source_blacklist"
|
|
351
|
+
)
|
|
352
|
+
to_skip = True
|
|
353
|
+
break
|
|
354
|
+
write_wan_status = tmpEP["detailed_status"].get("write_wan")
|
|
355
|
+
if write_wan_status in ["OFF", "TEST"]:
|
|
356
|
+
tmpLog.info(
|
|
357
|
+
f" skip nucleus={tmpNucleus} since {tmp_ddm_endpoint_name} has write_wan={read_wan_status} criteria=-destination_blacklist"
|
|
358
|
+
)
|
|
359
|
+
to_skip = True
|
|
360
|
+
break
|
|
361
|
+
# check space
|
|
362
|
+
tmpSpaceSize = 0
|
|
363
|
+
if tmpEP["space_free"]:
|
|
364
|
+
tmpSpaceSize += tmpEP["space_free"]
|
|
365
|
+
if tmpEP["space_expired"]:
|
|
366
|
+
tmpSpaceSize += tmpEP["space_expired"]
|
|
367
|
+
tmpSpaceToUse = 0
|
|
368
|
+
if tmpNucleus in self.fullRW:
|
|
369
|
+
# 0.25GB per cpuTime/corePower/day
|
|
370
|
+
tmpSpaceToUse = int(self.fullRW[tmpNucleus] / 10 / 24 / 3600 * 0.25)
|
|
371
|
+
if tmpSpaceSize - tmpSpaceToUse < diskThreshold:
|
|
372
|
+
tmpLog.info(
|
|
373
|
+
" skip nucleus={0} since disk shortage (free {1} TB - reserved {2} TB < thr {3} TB) at endpoint {4} criteria=-space".format(
|
|
374
|
+
tmpNucleus, tmpSpaceSize // 1024, tmpSpaceToUse // 1024, diskThreshold // 1024, tmpEP["ddm_endpoint_name"]
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
to_skip = True
|
|
378
|
+
break
|
|
379
|
+
# keep fraction of free space
|
|
380
|
+
if tmpNucleus not in fractionFreeSpace:
|
|
381
|
+
fractionFreeSpace[tmpNucleus] = {"total": 0, "free": 0}
|
|
382
|
+
try:
|
|
383
|
+
tmpOld = float(fractionFreeSpace[tmpNucleus]["free"]) / float(fractionFreeSpace[tmpNucleus]["total"])
|
|
384
|
+
except Exception:
|
|
385
|
+
tmpOld = None
|
|
386
|
+
try:
|
|
387
|
+
tmpNew = float(tmpSpaceSize - tmpSpaceToUse) / float(tmpEP["space_total"])
|
|
388
|
+
except Exception:
|
|
389
|
+
tmpNew = None
|
|
390
|
+
if tmpNew is not None and (tmpOld is None or tmpNew < tmpOld):
|
|
391
|
+
fractionFreeSpace[tmpNucleus] = {"total": tmpEP["space_total"], "free": tmpSpaceSize - tmpSpaceToUse}
|
|
392
|
+
if not to_skip:
|
|
393
|
+
newNucleusList[tmpNucleus] = origNucleusSpec
|
|
394
|
+
nucleusList = newNucleusList
|
|
395
|
+
tmpLog.info(f"{len(nucleusList)} candidates passed endpoint check with DISK_THRESHOLD={diskThreshold // 1024} TB")
|
|
396
|
+
self.add_summary_message(oldNucleusList, nucleusList, "storage endpoint check")
|
|
397
|
+
if not nucleusList:
|
|
398
|
+
self.post_process_for_error(taskSpec, tmpLog, "no candidates")
|
|
399
|
+
continue
|
|
400
|
+
######################################
|
|
401
|
+
# ability to execute jobs
|
|
402
|
+
newNucleusList = {}
|
|
403
|
+
oldNucleusList = copy.copy(nucleusList)
|
|
404
|
+
# get all panda sites
|
|
405
|
+
tmpSiteList = []
|
|
406
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
407
|
+
tmpSiteList += tmpNucleusSpec.allPandaSites
|
|
408
|
+
tmpSiteList = list(set(tmpSiteList))
|
|
409
|
+
tmpLog.debug("===== start for job check")
|
|
410
|
+
jobBroker = AtlasProdJobBroker(self.ddmIF, self.taskBufferIF)
|
|
411
|
+
tmp_status, tmp_data_map = jobBroker.doBrokerage(taskSpec, taskSpec.cloud, inputChunk, None, True, tmpSiteList, tmpLog)
|
|
412
|
+
tmpLog.debug("===== done for job check")
|
|
413
|
+
if tmp_status != Interaction.SC_SUCCEEDED:
|
|
414
|
+
tmpLog.error("no sites can run jobs")
|
|
415
|
+
taskSpec.resetChangedList()
|
|
416
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
417
|
+
self.taskBufferIF.updateTask_JEDI(
|
|
418
|
+
taskSpec, {"jediTaskID": taskSpec.jediTaskID}, oldStatus=["assigning"], updateDEFT=False, setFrozenTime=False
|
|
419
|
+
)
|
|
420
|
+
continue
|
|
421
|
+
okNuclei = set()
|
|
422
|
+
for tmpSite in tmp_data_map:
|
|
423
|
+
siteSpec = siteMapper.getSite(tmpSite)
|
|
424
|
+
okNuclei.add(siteSpec.pandasite)
|
|
425
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
426
|
+
if tmpNucleus in okNuclei:
|
|
427
|
+
newNucleusList[tmpNucleus] = tmpNucleusSpec
|
|
428
|
+
else:
|
|
429
|
+
tmpLog.info(f" skip nucleus={tmpNucleus} due to missing ability to run jobs criteria=-job")
|
|
430
|
+
nucleusList = newNucleusList
|
|
431
|
+
tmpLog.info(f"{len(nucleusList)} candidates passed job check")
|
|
432
|
+
self.add_summary_message(oldNucleusList, nucleusList, "job check")
|
|
433
|
+
if not nucleusList:
|
|
434
|
+
self.post_process_for_error(taskSpec, tmpLog, "no candidates")
|
|
435
|
+
continue
|
|
436
|
+
######################################
|
|
437
|
+
# data locality
|
|
438
|
+
time_now = naive_utcnow()
|
|
439
|
+
if taskSpec.frozenTime and time_now - taskSpec.frozenTime > datetime.timedelta(days=data_location_check_period):
|
|
440
|
+
tmpLog.info(f"disabled data check since the task was in assigning for " f"{data_location_check_period} days")
|
|
441
|
+
else:
|
|
442
|
+
dataset_availability_info = {k: v for k, v in dataset_availability_info.items() if k in nucleusList}
|
|
443
|
+
if dataset_availability_info != {}:
|
|
444
|
+
newNucleusList = {}
|
|
445
|
+
oldNucleusList = copy.copy(nucleusList)
|
|
446
|
+
# skip if no data
|
|
447
|
+
skipMsgList = []
|
|
448
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
449
|
+
if taskSpec.inputPreStaging() and dataset_availability_info[tmpNucleus]["ava_num_any"] > 0:
|
|
450
|
+
# use incomplete replicas for data carousel since the completeness is guaranteed
|
|
451
|
+
newNucleusList[tmpNucleus] = tmpNucleusSpec
|
|
452
|
+
elif (
|
|
453
|
+
dataset_availability_info[tmpNucleus]["tot_size"] > thrInputSize
|
|
454
|
+
and dataset_availability_info[tmpNucleus]["ava_size_any"]
|
|
455
|
+
< dataset_availability_info[tmpNucleus]["tot_size"] * thrInputSizeFrac
|
|
456
|
+
):
|
|
457
|
+
tmpMsg = " skip nucleus={0} due to insufficient input size {1}B < {2}*{3} criteria=-insize".format(
|
|
458
|
+
tmpNucleus,
|
|
459
|
+
dataset_availability_info[tmpNucleus]["ava_size_any"],
|
|
460
|
+
dataset_availability_info[tmpNucleus]["tot_size"],
|
|
461
|
+
thrInputSizeFrac,
|
|
462
|
+
)
|
|
463
|
+
skipMsgList.append(tmpMsg)
|
|
464
|
+
elif (
|
|
465
|
+
dataset_availability_info[tmpNucleus]["tot_num"] > thrInputNum
|
|
466
|
+
and dataset_availability_info[tmpNucleus]["ava_num_any"]
|
|
467
|
+
< dataset_availability_info[tmpNucleus]["tot_num"] * thrInputNumFrac
|
|
468
|
+
):
|
|
469
|
+
tmpMsg = " skip nucleus={0} due to short number of input files {1} < {2}*{3} criteria=-innum".format(
|
|
470
|
+
tmpNucleus,
|
|
471
|
+
dataset_availability_info[tmpNucleus]["ava_num_any"],
|
|
472
|
+
dataset_availability_info[tmpNucleus]["tot_num"],
|
|
473
|
+
thrInputNumFrac,
|
|
474
|
+
)
|
|
475
|
+
skipMsgList.append(tmpMsg)
|
|
476
|
+
else:
|
|
477
|
+
newNucleusList[tmpNucleus] = tmpNucleusSpec
|
|
478
|
+
totInputSize = list(dataset_availability_info.values())[0]["tot_size"] / 1024 / 1024 / 1024
|
|
479
|
+
data_locality_check_str = (
|
|
480
|
+
f"(ioIntensity ({taskSpec.ioIntensity}) is None or less than {minIoIntensityWithLD} kBPerS "
|
|
481
|
+
f"and input size ({int(totInputSize)} GB) is less than "
|
|
482
|
+
f"{minInputSizeWithLD}) "
|
|
483
|
+
f"or (task.currentPriority ("
|
|
484
|
+
f"{taskSpec.currentPriority}) is higher than or equal to {maxTaskPrioWithLD}) "
|
|
485
|
+
f"or the task is in assigning for {data_location_check_period} days"
|
|
486
|
+
)
|
|
487
|
+
if len(newNucleusList) > 0:
|
|
488
|
+
nucleusList = newNucleusList
|
|
489
|
+
for tmpMsg in skipMsgList:
|
|
490
|
+
tmpLog.info(tmpMsg)
|
|
491
|
+
elif (
|
|
492
|
+
(taskSpec.ioIntensity is None or taskSpec.ioIntensity <= minIoIntensityWithLD) and totInputSize <= minInputSizeWithLD
|
|
493
|
+
) or taskSpec.currentPriority >= maxTaskPrioWithLD:
|
|
494
|
+
dataset_availability_info = {}
|
|
495
|
+
tmpLog.info(f" disable data locality check since no nucleus has input data, {data_locality_check_str}")
|
|
496
|
+
else:
|
|
497
|
+
# no candidate + unavoidable data locality check
|
|
498
|
+
nucleusList = newNucleusList
|
|
499
|
+
for tmpMsg in skipMsgList:
|
|
500
|
+
tmpLog.info(tmpMsg)
|
|
501
|
+
tmpLog.info(f" the following conditions required to disable data locality check: {data_locality_check_str}")
|
|
502
|
+
tmpLog.info(f"{len(nucleusList)} candidates passed data check")
|
|
503
|
+
self.add_summary_message(oldNucleusList, nucleusList, "data check")
|
|
504
|
+
if not nucleusList:
|
|
505
|
+
self.post_process_for_error(taskSpec, tmpLog, "no candidates")
|
|
506
|
+
continue
|
|
507
|
+
######################################
|
|
508
|
+
# check for full chain
|
|
509
|
+
newNucleusList = {}
|
|
510
|
+
oldNucleusList = copy.copy(nucleusList)
|
|
511
|
+
parent_full_chain = False
|
|
512
|
+
if taskSpec.get_full_chain() and taskSpec.jediTaskID != taskSpec.parent_tid:
|
|
513
|
+
tmpStat, parentTaskSpec = self.taskBufferIF.getTaskWithID_JEDI(taskSpec.parent_tid, False)
|
|
514
|
+
|
|
515
|
+
if not tmpStat or parentTaskSpec is None:
|
|
516
|
+
self.post_process_for_error(taskSpec, tmpLog, f"failed to get parent taskSpec with jediTaskID={taskSpec.parent_tid}", False)
|
|
517
|
+
continue
|
|
518
|
+
parent_full_chain = parentTaskSpec.check_full_chain_with_nucleus(siteMapper.getNucleus(parentTaskSpec.nucleus))
|
|
519
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
520
|
+
# nucleus to run only full-chain tasks
|
|
521
|
+
if tmpNucleusSpec.get_bare_nucleus_mode() == "only" and taskSpec.get_full_chain() is None:
|
|
522
|
+
tmpLog.info(f" skip nucleus={tmpNucleus} since only full-chain tasks are allowed criteria=-full_chain")
|
|
523
|
+
continue
|
|
524
|
+
# task requirement to run on nucleus with full-chain support
|
|
525
|
+
if tmpNucleusSpec.get_bare_nucleus_mode() is None and (
|
|
526
|
+
taskSpec.check_full_chain_with_mode("only") or taskSpec.check_full_chain_with_mode("require")
|
|
527
|
+
):
|
|
528
|
+
tmpLog.info(f" skip nucleus={tmpNucleus} due to lack of full-chain capability criteria=-full_chain")
|
|
529
|
+
continue
|
|
530
|
+
# check parent task
|
|
531
|
+
if taskSpec.get_full_chain() and parent_full_chain:
|
|
532
|
+
if tmpNucleus != parentTaskSpec.nucleus:
|
|
533
|
+
tmpLog.info(f" skip nucleus={tmpNucleus} since the parent of the full-chain ran elsewhere criteria=-full_chain")
|
|
534
|
+
continue
|
|
535
|
+
newNucleusList[tmpNucleus] = tmpNucleusSpec
|
|
536
|
+
nucleusList = newNucleusList
|
|
537
|
+
tmpLog.info(f"{len(nucleusList)} candidates passed full-chain check")
|
|
538
|
+
self.add_summary_message(oldNucleusList, nucleusList, "full-chain check")
|
|
539
|
+
if not nucleusList:
|
|
540
|
+
self.post_process_for_error(taskSpec, tmpLog, "no candidates")
|
|
541
|
+
continue
|
|
542
|
+
self.dump_summary(tmpLog, nucleusList)
|
|
543
|
+
######################################
|
|
544
|
+
# weight
|
|
545
|
+
self.prioRW.acquire()
|
|
546
|
+
nucleusRW = self.prioRW[taskSpec.currentPriority]
|
|
547
|
+
self.prioRW.release()
|
|
548
|
+
totalWeight = 0
|
|
549
|
+
nucleusweights = []
|
|
550
|
+
for tmpNucleus, tmpNucleusSpec in nucleusList.items():
|
|
551
|
+
if tmpNucleus not in nucleusRW:
|
|
552
|
+
nucleusRW[tmpNucleus] = 0
|
|
553
|
+
wStr = "1"
|
|
554
|
+
# with RW
|
|
555
|
+
if tmpNucleus in nucleusRW and nucleusRW[tmpNucleus] and nucleusRW[tmpNucleus] >= cutOffRW:
|
|
556
|
+
weight = 1 / float(nucleusRW[tmpNucleus])
|
|
557
|
+
wStr += f"/( RW={nucleusRW[tmpNucleus]} )"
|
|
558
|
+
else:
|
|
559
|
+
weight = 1
|
|
560
|
+
wStr += f"/(1 : RW={nucleusRW[tmpNucleus]}<{cutOffRW})"
|
|
561
|
+
# with data
|
|
562
|
+
if dataset_availability_info != {}:
|
|
563
|
+
if dataset_availability_info[tmpNucleus]["tot_size"] > 0:
|
|
564
|
+
# use input size only when high IO intensity
|
|
565
|
+
if taskSpec.ioIntensity is None or taskSpec.ioIntensity > minIoIntensityWithLD:
|
|
566
|
+
weight *= float(dataset_availability_info[tmpNucleus]["ava_size_any"])
|
|
567
|
+
weight /= float(dataset_availability_info[tmpNucleus]["tot_size"])
|
|
568
|
+
wStr += f"* ( available_input_size_DISKTAPE={dataset_availability_info[tmpNucleus]['ava_size_any']} )"
|
|
569
|
+
wStr += f"/ ( total_input_size={dataset_availability_info[tmpNucleus]['tot_size']} )"
|
|
570
|
+
# negative weight for tape
|
|
571
|
+
if dataset_availability_info[tmpNucleus]["ava_size_any"] > dataset_availability_info[tmpNucleus]["ava_size_disk"]:
|
|
572
|
+
weight *= negWeightTape
|
|
573
|
+
wStr += f"*( weight_TAPE={negWeightTape} )"
|
|
574
|
+
# fraction of free space
|
|
575
|
+
if tmpNucleus in fractionFreeSpace:
|
|
576
|
+
try:
|
|
577
|
+
tmpFrac = float(fractionFreeSpace[tmpNucleus]["free"]) / float(fractionFreeSpace[tmpNucleus]["total"])
|
|
578
|
+
weight *= tmpFrac
|
|
579
|
+
wStr += f"*( free_space={fractionFreeSpace[tmpNucleus]['free'] // 1024} TB )/( total_space={fractionFreeSpace[tmpNucleus]['total'] // 1024} TB )"
|
|
580
|
+
free_disk_in_tb = fractionFreeSpace[tmpNucleus]["free"] // 1024
|
|
581
|
+
free_disk_term = min(free_disk_cutoff, free_disk_in_tb)
|
|
582
|
+
weight *= free_disk_term
|
|
583
|
+
wStr += f"*min( free_space={free_disk_in_tb} TB, FREE_DISK_CUTOFF={free_disk_cutoff} TB )"
|
|
584
|
+
except Exception:
|
|
585
|
+
pass
|
|
586
|
+
tmpLog.info(f" use nucleus={tmpNucleus} weight={weight} {wStr} criteria=+use")
|
|
587
|
+
totalWeight += weight
|
|
588
|
+
nucleusweights.append((tmpNucleus, weight))
|
|
589
|
+
######################################
|
|
590
|
+
# final selection
|
|
591
|
+
tgtWeight = random.uniform(0, totalWeight)
|
|
592
|
+
candidateNucleus = None
|
|
593
|
+
for tmpNucleus, weight in nucleusweights:
|
|
594
|
+
tgtWeight -= weight
|
|
595
|
+
if tgtWeight <= 0:
|
|
596
|
+
candidateNucleus = tmpNucleus
|
|
597
|
+
break
|
|
598
|
+
if candidateNucleus is None:
|
|
599
|
+
candidateNucleus = nucleusweights[-1][0]
|
|
600
|
+
######################################
|
|
601
|
+
# update
|
|
602
|
+
nucleusSpec = nucleusList[candidateNucleus]
|
|
603
|
+
# get output/log datasets
|
|
604
|
+
tmpStat, tmpDatasetSpecs = self.taskBufferIF.getDatasetsWithJediTaskID_JEDI(taskSpec.jediTaskID, ["output", "log"])
|
|
605
|
+
# get destinations
|
|
606
|
+
retMap = {taskSpec.jediTaskID: AtlasBrokerUtils.getDictToSetNucleus(nucleusSpec, tmpDatasetSpecs)}
|
|
607
|
+
tmp_ret = self.taskBufferIF.setCloudToTasks_JEDI(retMap)
|
|
608
|
+
tmpLog.info(f" set nucleus={candidateNucleus} with {tmp_ret} criteria=+set")
|
|
609
|
+
if tmp_ret:
|
|
610
|
+
tmpMsg = "set task_status=ready"
|
|
611
|
+
tmpLog.sendMsg(tmpMsg, self.msgType)
|
|
612
|
+
# update RW table
|
|
613
|
+
self.prioRW.acquire()
|
|
614
|
+
for prio, rwMap in self.prioRW.items():
|
|
615
|
+
if prio > taskSpec.currentPriority:
|
|
616
|
+
continue
|
|
617
|
+
if candidateNucleus in rwMap:
|
|
618
|
+
if not rwMap[candidateNucleus]:
|
|
619
|
+
rwMap[candidateNucleus] = 0
|
|
620
|
+
rwMap[candidateNucleus] += taskRW
|
|
621
|
+
else:
|
|
622
|
+
rwMap[candidateNucleus] = taskRW
|
|
623
|
+
self.prioRW.release()
|
|
624
|
+
except Exception:
|
|
625
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
626
|
+
errMsg = f"{self.__class__.__name__}.runImpl() failed with {errtype.__name__} {errvalue} "
|
|
627
|
+
errMsg += f"lastJediTaskID={lastJediTaskID} "
|
|
628
|
+
errMsg += traceback.format_exc()
|
|
629
|
+
logger.error(errMsg)
|