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,381 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
5
|
+
|
|
6
|
+
from pandajedi.jedicore import Interaction
|
|
7
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
8
|
+
from pandajedi.jedicore.SiteCandidate import SiteCandidate
|
|
9
|
+
from pandajedi.jedirefine import RefinerUtils
|
|
10
|
+
from pandaserver.srvcore import CoreUtils
|
|
11
|
+
|
|
12
|
+
from . import AtlasBrokerUtils
|
|
13
|
+
from .JobBrokerBase import JobBrokerBase
|
|
14
|
+
|
|
15
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# brokerage for general purpose
|
|
19
|
+
class GenJobBroker(JobBrokerBase):
|
|
20
|
+
# constructor
|
|
21
|
+
def __init__(self, ddmIF, taskBufferIF):
|
|
22
|
+
JobBrokerBase.__init__(self, ddmIF, taskBufferIF)
|
|
23
|
+
|
|
24
|
+
# main
|
|
25
|
+
def doBrokerage(self, taskSpec, cloudName, inputChunk, taskParamMap):
|
|
26
|
+
# make logger
|
|
27
|
+
tmpLog = MsgWrapper(logger, f"<jediTaskID={taskSpec.jediTaskID}>")
|
|
28
|
+
tmpLog.debug("start")
|
|
29
|
+
# return for failure
|
|
30
|
+
retFatal = self.SC_FATAL, inputChunk
|
|
31
|
+
retTmpError = self.SC_FAILED, inputChunk
|
|
32
|
+
# set cloud
|
|
33
|
+
try:
|
|
34
|
+
if not taskParamMap:
|
|
35
|
+
taskParam = self.taskBufferIF.getTaskParamsWithID_JEDI(taskSpec.jediTaskID)
|
|
36
|
+
taskParamMap = RefinerUtils.decodeJSON(taskParam)
|
|
37
|
+
if not taskSpec.cloud and "cloud" in taskParamMap:
|
|
38
|
+
taskSpec.cloud = taskParamMap["cloud"]
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
# get sites in the cloud
|
|
42
|
+
site_preassigned = True
|
|
43
|
+
if taskSpec.site not in ["", None]:
|
|
44
|
+
tmpLog.debug(f"site={taskSpec.site} is pre-assigned")
|
|
45
|
+
if self.siteMapper.checkSite(taskSpec.site):
|
|
46
|
+
scanSiteList = [taskSpec.site]
|
|
47
|
+
else:
|
|
48
|
+
scanSiteList = []
|
|
49
|
+
for tmpSite in self.siteMapper.getCloud(taskSpec.cloud)["sites"]:
|
|
50
|
+
if re.search(taskSpec.site, tmpSite):
|
|
51
|
+
scanSiteList.append(tmpSite)
|
|
52
|
+
if not scanSiteList:
|
|
53
|
+
tmpLog.error(f"unknown site={taskSpec.site}")
|
|
54
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
55
|
+
return retTmpError
|
|
56
|
+
elif inputChunk.getPreassignedSite() is not None:
|
|
57
|
+
scanSiteList = [inputChunk.getPreassignedSite()]
|
|
58
|
+
tmpLog.debug(f"site={inputChunk.getPreassignedSite()} is pre-assigned in masterDS")
|
|
59
|
+
else:
|
|
60
|
+
site_preassigned = False
|
|
61
|
+
scanSiteList = self.siteMapper.getCloud(taskSpec.cloud)["sites"]
|
|
62
|
+
# remove NA
|
|
63
|
+
if "NA" in scanSiteList:
|
|
64
|
+
scanSiteList.remove("NA")
|
|
65
|
+
tmpLog.debug(f"cloud={taskSpec.cloud} has {len(scanSiteList)} candidates")
|
|
66
|
+
tmpLog.debug(f"initial {len(scanSiteList)} candidates")
|
|
67
|
+
######################################
|
|
68
|
+
# selection for status and PandaSite
|
|
69
|
+
newScanSiteList = []
|
|
70
|
+
for tmpSiteName in scanSiteList:
|
|
71
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
72
|
+
# check site status
|
|
73
|
+
if tmpSiteSpec.status != "online" and not site_preassigned:
|
|
74
|
+
tmpLog.debug(f" skip {tmpSiteName} due to status={tmpSiteSpec.status}")
|
|
75
|
+
continue
|
|
76
|
+
# check PandaSite
|
|
77
|
+
if "PandaSite" in taskParamMap and taskParamMap["PandaSite"]:
|
|
78
|
+
if tmpSiteSpec.pandasite != taskParamMap["PandaSite"]:
|
|
79
|
+
tmpLog.debug(f" skip {tmpSiteName} due to wrong PandaSite={tmpSiteSpec.pandasite} <> {taskParamMap['PandaSite']}")
|
|
80
|
+
continue
|
|
81
|
+
newScanSiteList.append(tmpSiteName)
|
|
82
|
+
scanSiteList = newScanSiteList
|
|
83
|
+
tmpLog.debug(f"{len(scanSiteList)} candidates passed site status check")
|
|
84
|
+
if scanSiteList == []:
|
|
85
|
+
tmpLog.error("no candidates")
|
|
86
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
87
|
+
return retTmpError
|
|
88
|
+
######################################
|
|
89
|
+
# selection for scratch disk
|
|
90
|
+
minDiskCountS = taskSpec.getOutDiskSize() + taskSpec.getWorkDiskSize() + inputChunk.getMaxAtomSize()
|
|
91
|
+
minDiskCountS = minDiskCountS // 1024 // 1024
|
|
92
|
+
# size for direct IO sites
|
|
93
|
+
if taskSpec.useLocalIO():
|
|
94
|
+
minDiskCountR = minDiskCountS
|
|
95
|
+
else:
|
|
96
|
+
minDiskCountR = taskSpec.getOutDiskSize() + taskSpec.getWorkDiskSize()
|
|
97
|
+
minDiskCountR = minDiskCountR // 1024 // 1024
|
|
98
|
+
newScanSiteList = []
|
|
99
|
+
for tmpSiteName in scanSiteList:
|
|
100
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
101
|
+
# check at the site
|
|
102
|
+
if tmpSiteSpec.maxwdir:
|
|
103
|
+
if CoreUtils.use_direct_io_for_job(taskSpec, tmpSiteSpec, inputChunk):
|
|
104
|
+
minDiskCount = minDiskCountR
|
|
105
|
+
else:
|
|
106
|
+
minDiskCount = minDiskCountS
|
|
107
|
+
if minDiskCount > tmpSiteSpec.maxwdir:
|
|
108
|
+
tmpLog.debug(f" skip {tmpSiteName} due to small scratch disk={tmpSiteSpec.maxwdir} < {minDiskCount}")
|
|
109
|
+
continue
|
|
110
|
+
newScanSiteList.append(tmpSiteName)
|
|
111
|
+
scanSiteList = newScanSiteList
|
|
112
|
+
tmpLog.debug(f"{len(scanSiteList)} candidates passed scratch disk check")
|
|
113
|
+
if scanSiteList == []:
|
|
114
|
+
tmpLog.error("no candidates")
|
|
115
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
116
|
+
return retTmpError
|
|
117
|
+
######################################
|
|
118
|
+
# selection for available space in SE
|
|
119
|
+
newScanSiteList = []
|
|
120
|
+
for tmpSiteName in scanSiteList:
|
|
121
|
+
# check at the site
|
|
122
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
123
|
+
# free space must be >= 200GB
|
|
124
|
+
diskThreshold = 200
|
|
125
|
+
tmpSpaceSize = tmpSiteSpec.space
|
|
126
|
+
if tmpSiteSpec.space and tmpSpaceSize < diskThreshold:
|
|
127
|
+
tmpLog.debug(f" skip {tmpSiteName} due to disk shortage in SE = {tmpSiteSpec.space} < {diskThreshold}GB")
|
|
128
|
+
continue
|
|
129
|
+
newScanSiteList.append(tmpSiteName)
|
|
130
|
+
scanSiteList = newScanSiteList
|
|
131
|
+
tmpLog.debug(f"{len(scanSiteList)} candidates passed SE space check")
|
|
132
|
+
if not scanSiteList:
|
|
133
|
+
tmpLog.error("no candidates")
|
|
134
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
135
|
+
return retTmpError
|
|
136
|
+
######################################
|
|
137
|
+
# selection for walltime
|
|
138
|
+
minWalltime = taskSpec.walltime
|
|
139
|
+
if minWalltime not in [0, None]:
|
|
140
|
+
newScanSiteList = []
|
|
141
|
+
for tmpSiteName in scanSiteList:
|
|
142
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
143
|
+
# check at the site
|
|
144
|
+
if tmpSiteSpec.maxtime != 0 and minWalltime > tmpSiteSpec.maxtime:
|
|
145
|
+
tmpLog.debug(f" skip {tmpSiteName} due to short site walltime={tmpSiteSpec.maxtime}(site upper limit) < {minWalltime}")
|
|
146
|
+
continue
|
|
147
|
+
if tmpSiteSpec.mintime != 0 and minWalltime < tmpSiteSpec.mintime:
|
|
148
|
+
tmpLog.debug(f" skip {tmpSiteName} due to short job walltime={tmpSiteSpec.mintime}(site lower limit) > {minWalltime}")
|
|
149
|
+
continue
|
|
150
|
+
newScanSiteList.append(tmpSiteName)
|
|
151
|
+
scanSiteList = newScanSiteList
|
|
152
|
+
tmpLog.debug(f"{len(scanSiteList)} candidates passed walltime check ={minWalltime}{taskSpec.walltimeUnit}")
|
|
153
|
+
if not scanSiteList:
|
|
154
|
+
tmpLog.error("no candidates")
|
|
155
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
156
|
+
return retTmpError
|
|
157
|
+
######################################
|
|
158
|
+
# selection for MP
|
|
159
|
+
if taskSpec.coreCount is not None and taskSpec.coreCount >= 0:
|
|
160
|
+
if not site_preassigned:
|
|
161
|
+
newScanSiteList = []
|
|
162
|
+
for tmpSiteName in scanSiteList:
|
|
163
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
164
|
+
# check at the site
|
|
165
|
+
is_ok = False
|
|
166
|
+
if taskSpec.coreCount == 0:
|
|
167
|
+
# any
|
|
168
|
+
is_ok = True
|
|
169
|
+
elif taskSpec.coreCount == 1:
|
|
170
|
+
# score
|
|
171
|
+
if tmpSiteSpec.coreCount in [0, 1, -1, None]:
|
|
172
|
+
is_ok = True
|
|
173
|
+
else:
|
|
174
|
+
# mcore
|
|
175
|
+
if tmpSiteSpec.coreCount in [0, -1]:
|
|
176
|
+
is_ok = True
|
|
177
|
+
elif tmpSiteSpec.coreCount and tmpSiteSpec.coreCount >= taskSpec.coreCount:
|
|
178
|
+
is_ok = True
|
|
179
|
+
if is_ok:
|
|
180
|
+
newScanSiteList.append(tmpSiteName)
|
|
181
|
+
else:
|
|
182
|
+
tmpLog.info(
|
|
183
|
+
f" skip site={tmpSiteName} due to core mismatch site:{tmpSiteSpec.coreCount} <> task:{taskSpec.coreCount} criteria=-cpucore"
|
|
184
|
+
)
|
|
185
|
+
scanSiteList = newScanSiteList
|
|
186
|
+
tmpLog.info(f"{len(scanSiteList)} candidates passed for core count check")
|
|
187
|
+
if not scanSiteList:
|
|
188
|
+
self.dump_summary(tmpLog)
|
|
189
|
+
tmpLog.error("no candidates")
|
|
190
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
191
|
+
return retTmpError
|
|
192
|
+
######################################
|
|
193
|
+
# selection for memory
|
|
194
|
+
origMinRamCount = inputChunk.getMaxRamCount()
|
|
195
|
+
if not site_preassigned and origMinRamCount:
|
|
196
|
+
newScanSiteList = []
|
|
197
|
+
for tmpSiteName in scanSiteList:
|
|
198
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
199
|
+
# job memory requirement
|
|
200
|
+
if taskSpec.ramPerCore():
|
|
201
|
+
if tmpSiteSpec.coreCount and tmpSiteSpec.coreCount > 0:
|
|
202
|
+
minRamCount = origMinRamCount * tmpSiteSpec.coreCount
|
|
203
|
+
else:
|
|
204
|
+
minRamCount = origMinRamCount * (taskSpec.coreCount if taskSpec.coreCount else 1)
|
|
205
|
+
minRamCount += taskSpec.baseRamCount if taskSpec.baseRamCount else 0
|
|
206
|
+
else:
|
|
207
|
+
minRamCount = origMinRamCount
|
|
208
|
+
# site max memory requirement
|
|
209
|
+
site_maxmemory = tmpSiteSpec.maxrss if tmpSiteSpec.maxrss else 0
|
|
210
|
+
# check at the site
|
|
211
|
+
if site_maxmemory and minRamCount and minRamCount > site_maxmemory:
|
|
212
|
+
tmpMsg = f" skip site={tmpSiteName} due to site RAM shortage. {site_maxmemory} (site upper limit) less than {minRamCount} "
|
|
213
|
+
tmpLog.debug(tmpMsg)
|
|
214
|
+
continue
|
|
215
|
+
# site min memory requirement
|
|
216
|
+
site_minmemory = tmpSiteSpec.minrss if tmpSiteSpec.minrss else 0
|
|
217
|
+
if site_minmemory and minRamCount and minRamCount < site_minmemory:
|
|
218
|
+
tmpMsg = f" skip site={tmpSiteName} due to job RAM shortage. {site_minmemory} (site lower limit) greater than {minRamCount} "
|
|
219
|
+
tmpLog.info(tmpMsg)
|
|
220
|
+
continue
|
|
221
|
+
newScanSiteList.append(tmpSiteName)
|
|
222
|
+
scanSiteList = newScanSiteList
|
|
223
|
+
tmpLog.debug(f"{len(scanSiteList)} candidates passed memory check")
|
|
224
|
+
if not scanSiteList:
|
|
225
|
+
tmpLog.error("no candidates")
|
|
226
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
227
|
+
return retTmpError
|
|
228
|
+
######################################
|
|
229
|
+
# selection with processing types
|
|
230
|
+
newScanSiteList = []
|
|
231
|
+
for tmpSiteName in scanSiteList:
|
|
232
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
233
|
+
allowed_list = tmpSiteSpec.get_allowed_processing_types()
|
|
234
|
+
exclude_list = tmpSiteSpec.get_excluded_processing_types()
|
|
235
|
+
if allowed_list is not None:
|
|
236
|
+
if taskSpec.processingType not in allowed_list:
|
|
237
|
+
tmpLog.debug(f" skip {tmpSiteName} due to processing type not in allowed list")
|
|
238
|
+
continue
|
|
239
|
+
if exclude_list is not None:
|
|
240
|
+
if taskSpec.processingType in exclude_list:
|
|
241
|
+
tmpLog.debug(f" skip {tmpSiteName} due to processing type in excluded list")
|
|
242
|
+
continue
|
|
243
|
+
newScanSiteList.append(tmpSiteName)
|
|
244
|
+
scanSiteList = newScanSiteList
|
|
245
|
+
tmpLog.debug(f"{len(scanSiteList)} candidates passed allowed/excluded processing type check")
|
|
246
|
+
if not scanSiteList:
|
|
247
|
+
tmpLog.error("no candidates")
|
|
248
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
249
|
+
return retTmpError
|
|
250
|
+
######################################
|
|
251
|
+
# selection for nPilot
|
|
252
|
+
nWNmap = self.taskBufferIF.getCurrentSiteData()
|
|
253
|
+
newScanSiteList = []
|
|
254
|
+
for tmpSiteName in scanSiteList:
|
|
255
|
+
tmp_site_spec = self.siteMapper.getSite(tmpSiteName)
|
|
256
|
+
# check at the site
|
|
257
|
+
nPilot = 0
|
|
258
|
+
if tmpSiteName in nWNmap:
|
|
259
|
+
nPilot = nWNmap[tmpSiteName]["getJob"] + nWNmap[tmpSiteName]["updateJob"]
|
|
260
|
+
if nPilot == 0 and taskSpec.prodSourceLabel not in ["test"] and not tmp_site_spec.hasValueInCatchall("allow_no_pilot"):
|
|
261
|
+
tmpLog.debug(f" skip {tmpSiteName} due to no pilot")
|
|
262
|
+
continue
|
|
263
|
+
newScanSiteList.append(tmpSiteName)
|
|
264
|
+
scanSiteList = newScanSiteList
|
|
265
|
+
tmpLog.debug(f"{len(scanSiteList)} candidates passed pilot activity check")
|
|
266
|
+
if scanSiteList == []:
|
|
267
|
+
tmpLog.error("no candidates")
|
|
268
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
269
|
+
return retTmpError
|
|
270
|
+
######################################
|
|
271
|
+
# sites already used by task
|
|
272
|
+
tmpSt, sitesUsedByTask = self.taskBufferIF.getSitesUsedByTask_JEDI(taskSpec.jediTaskID)
|
|
273
|
+
if not tmpSt:
|
|
274
|
+
tmpLog.error("failed to get sites which already used by task")
|
|
275
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
276
|
+
return retTmpError
|
|
277
|
+
######################################
|
|
278
|
+
# get list of available files
|
|
279
|
+
availableFileMap = {}
|
|
280
|
+
for datasetSpec in inputChunk.getDatasets():
|
|
281
|
+
try:
|
|
282
|
+
# get list of site to be scanned
|
|
283
|
+
tmpLog.debug(f"getting the list of available files for {datasetSpec.datasetName}")
|
|
284
|
+
fileScanSiteList = []
|
|
285
|
+
for tmpPseudoSiteName in scanSiteList:
|
|
286
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpPseudoSiteName)
|
|
287
|
+
tmpSiteName = tmpSiteSpec.get_unified_name()
|
|
288
|
+
if tmpSiteName in fileScanSiteList:
|
|
289
|
+
continue
|
|
290
|
+
fileScanSiteList.append(tmpSiteName)
|
|
291
|
+
# mapping between sites and input storage endpoints
|
|
292
|
+
siteStorageEP = AtlasBrokerUtils.getSiteInputStorageEndpointMap(fileScanSiteList, self.siteMapper, taskSpec.prodSourceLabel, None)
|
|
293
|
+
# disable file lookup for merge jobs
|
|
294
|
+
if inputChunk.isMerging:
|
|
295
|
+
checkCompleteness = False
|
|
296
|
+
else:
|
|
297
|
+
checkCompleteness = True
|
|
298
|
+
if not datasetSpec.isMaster():
|
|
299
|
+
useCompleteOnly = True
|
|
300
|
+
else:
|
|
301
|
+
useCompleteOnly = False
|
|
302
|
+
# get available files per site/endpoint
|
|
303
|
+
tmpAvFileMap = self.ddmIF.getAvailableFiles(
|
|
304
|
+
datasetSpec,
|
|
305
|
+
siteStorageEP,
|
|
306
|
+
self.siteMapper,
|
|
307
|
+
check_completeness=checkCompleteness,
|
|
308
|
+
file_scan_in_container=False,
|
|
309
|
+
complete_only=useCompleteOnly,
|
|
310
|
+
use_deep=True,
|
|
311
|
+
)
|
|
312
|
+
if tmpAvFileMap is None:
|
|
313
|
+
raise Interaction.JEDITemporaryError("ddmIF.getAvailableFiles failed")
|
|
314
|
+
availableFileMap[datasetSpec.datasetName] = tmpAvFileMap
|
|
315
|
+
except Exception as e:
|
|
316
|
+
tmpLog.error(f"failed to get available files with {e}")
|
|
317
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
318
|
+
return retTmpError
|
|
319
|
+
######################################
|
|
320
|
+
# calculate weight
|
|
321
|
+
tmpSt, jobStatPrioMap = self.taskBufferIF.getJobStatisticsByGlobalShare(taskSpec.vo)
|
|
322
|
+
if not tmpSt:
|
|
323
|
+
tmpLog.error("failed to get job statistics with priority")
|
|
324
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
325
|
+
return retTmpError
|
|
326
|
+
######################################
|
|
327
|
+
# final procedure
|
|
328
|
+
tmpLog.debug(f"final {len(scanSiteList)} candidates")
|
|
329
|
+
weightMap = {}
|
|
330
|
+
candidateSpecList = []
|
|
331
|
+
preSiteCandidateSpec = None
|
|
332
|
+
for tmpSiteName in scanSiteList:
|
|
333
|
+
# get number of jobs in each job status. Using workQueueID=None to include non-JEDI jobs
|
|
334
|
+
nRunning = AtlasBrokerUtils.getNumJobs(jobStatPrioMap, tmpSiteName, "running", None, None)
|
|
335
|
+
nAssigned = AtlasBrokerUtils.getNumJobs(jobStatPrioMap, tmpSiteName, "defined", None, None)
|
|
336
|
+
nActivated = AtlasBrokerUtils.getNumJobs(jobStatPrioMap, tmpSiteName, "activated", None, None)
|
|
337
|
+
weight = float(nRunning + 1) / float(nActivated + nAssigned + 1) / float(nAssigned + 1)
|
|
338
|
+
# make candidate
|
|
339
|
+
siteCandidateSpec = SiteCandidate(tmpSiteName)
|
|
340
|
+
# set weight
|
|
341
|
+
siteCandidateSpec.weight = weight
|
|
342
|
+
# files
|
|
343
|
+
for tmpDatasetName, availableFiles in availableFileMap.items():
|
|
344
|
+
if tmpSiteName in availableFiles:
|
|
345
|
+
siteCandidateSpec.add_local_disk_files(availableFiles[tmpSiteName]["localdisk"])
|
|
346
|
+
# append
|
|
347
|
+
if tmpSiteName in sitesUsedByTask:
|
|
348
|
+
candidateSpecList.append(siteCandidateSpec)
|
|
349
|
+
else:
|
|
350
|
+
if weight not in weightMap:
|
|
351
|
+
weightMap[weight] = []
|
|
352
|
+
weightMap[weight].append(siteCandidateSpec)
|
|
353
|
+
# limit the number of sites
|
|
354
|
+
maxNumSites = 5
|
|
355
|
+
weightList = sorted(weightMap.keys())
|
|
356
|
+
weightList.reverse()
|
|
357
|
+
for weightVal in weightList:
|
|
358
|
+
if len(candidateSpecList) >= maxNumSites:
|
|
359
|
+
break
|
|
360
|
+
sitesWithWeight = weightMap[weightVal]
|
|
361
|
+
random.shuffle(sitesWithWeight)
|
|
362
|
+
candidateSpecList += sitesWithWeight[: (maxNumSites - len(candidateSpecList))]
|
|
363
|
+
# collect site names
|
|
364
|
+
scanSiteList = []
|
|
365
|
+
for siteCandidateSpec in candidateSpecList:
|
|
366
|
+
scanSiteList.append(siteCandidateSpec.siteName)
|
|
367
|
+
# append candidates
|
|
368
|
+
newScanSiteList = []
|
|
369
|
+
for siteCandidateSpec in candidateSpecList:
|
|
370
|
+
# append
|
|
371
|
+
inputChunk.addSiteCandidate(siteCandidateSpec)
|
|
372
|
+
newScanSiteList.append(siteCandidateSpec.siteName)
|
|
373
|
+
tmpLog.debug(f" use {siteCandidateSpec.siteName} with weight={siteCandidateSpec.weight} nFiles={len(siteCandidateSpec.localDiskFiles)}")
|
|
374
|
+
scanSiteList = newScanSiteList
|
|
375
|
+
if scanSiteList == []:
|
|
376
|
+
tmpLog.error("no candidates")
|
|
377
|
+
taskSpec.setErrDiag(tmpLog.uploadLog(taskSpec.jediTaskID))
|
|
378
|
+
return retTmpError
|
|
379
|
+
# return
|
|
380
|
+
tmpLog.debug("done")
|
|
381
|
+
return self.SC_SUCCEEDED, inputChunk
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pandajedi.jedicore import Interaction
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# base class for job brokerage
|
|
8
|
+
class JobBrokerBase(object):
|
|
9
|
+
def __init__(self, ddmIF, taskBufferIF):
|
|
10
|
+
self.ddmIF = ddmIF
|
|
11
|
+
self.taskBufferIF = taskBufferIF
|
|
12
|
+
self.liveCounter = None
|
|
13
|
+
self.lockID = None
|
|
14
|
+
self.baseLockID = None
|
|
15
|
+
self.useLock = False
|
|
16
|
+
self.testMode = False
|
|
17
|
+
self.refresh()
|
|
18
|
+
self.task_common = None
|
|
19
|
+
self.summaryList = None
|
|
20
|
+
|
|
21
|
+
# set task common dictionary
|
|
22
|
+
def set_task_common_dict(self, task_common):
|
|
23
|
+
self.task_common = task_common
|
|
24
|
+
|
|
25
|
+
# get task common attribute
|
|
26
|
+
def get_task_common(self, attr_name):
|
|
27
|
+
if self.task_common:
|
|
28
|
+
return self.task_common.get(attr_name)
|
|
29
|
+
|
|
30
|
+
# set task common attribute
|
|
31
|
+
def set_task_common(self, attr_name, attr_value):
|
|
32
|
+
self.task_common[attr_name] = attr_value
|
|
33
|
+
|
|
34
|
+
def refresh(self):
|
|
35
|
+
self.siteMapper = self.taskBufferIF.get_site_mapper()
|
|
36
|
+
|
|
37
|
+
def setLiveCounter(self, liveCounter):
|
|
38
|
+
self.liveCounter = liveCounter
|
|
39
|
+
|
|
40
|
+
def getLiveCount(self, siteName):
|
|
41
|
+
if self.liveCounter is None:
|
|
42
|
+
return 0
|
|
43
|
+
return self.liveCounter.get(siteName)
|
|
44
|
+
|
|
45
|
+
def setLockID(self, pid, tid):
|
|
46
|
+
self.baseLockID = f"{pid}-jbr"
|
|
47
|
+
self.lockID = f"{self.baseLockID}-{tid}"
|
|
48
|
+
|
|
49
|
+
def getBaseLockID(self):
|
|
50
|
+
if self.useLock:
|
|
51
|
+
return self.baseLockID
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
def checkSiteLock(self, vo, prodSourceLabel, siteName, queue_id, resource_name):
|
|
55
|
+
return self.taskBufferIF.checkProcessLock_JEDI(
|
|
56
|
+
vo=vo,
|
|
57
|
+
prodSourceLabel=prodSourceLabel,
|
|
58
|
+
cloud=siteName,
|
|
59
|
+
workqueue_id=queue_id,
|
|
60
|
+
resource_name=resource_name,
|
|
61
|
+
component=None,
|
|
62
|
+
pid=self.baseLockID,
|
|
63
|
+
checkBase=True,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def setTestMode(self):
|
|
67
|
+
self.testMode = True
|
|
68
|
+
|
|
69
|
+
# get list of unified sites
|
|
70
|
+
def get_unified_sites(self, scan_site_list):
|
|
71
|
+
unified_list = set()
|
|
72
|
+
for tmpSiteName in scan_site_list:
|
|
73
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
74
|
+
unifiedName = tmpSiteSpec.get_unified_name()
|
|
75
|
+
unified_list.add(unifiedName)
|
|
76
|
+
return tuple(unified_list)
|
|
77
|
+
|
|
78
|
+
# get list of pseudo sites
|
|
79
|
+
def get_pseudo_sites(self, unified_list, scan_site_list):
|
|
80
|
+
unified_list = set(unified_list)
|
|
81
|
+
pseudo_list = set()
|
|
82
|
+
for tmpSiteName in scan_site_list:
|
|
83
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
84
|
+
if tmpSiteSpec.get_unified_name() in unified_list:
|
|
85
|
+
pseudo_list.add(tmpSiteName)
|
|
86
|
+
return tuple(pseudo_list)
|
|
87
|
+
|
|
88
|
+
# add pseudo sites to skip
|
|
89
|
+
def add_pseudo_sites_to_skip(self, unified_dict, scan_site_list, skipped_dict):
|
|
90
|
+
for tmpSiteName in scan_site_list:
|
|
91
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpSiteName)
|
|
92
|
+
if tmpSiteSpec.get_unified_name() in unified_dict:
|
|
93
|
+
skipped_dict[tmpSiteName] = unified_dict[tmpSiteSpec.get_unified_name()]
|
|
94
|
+
return skipped_dict
|
|
95
|
+
|
|
96
|
+
# init summary list
|
|
97
|
+
def init_summary_list(self, header, comment, initial_list):
|
|
98
|
+
self.summaryList = []
|
|
99
|
+
self.summaryList.append(f"===== {header} =====")
|
|
100
|
+
if comment:
|
|
101
|
+
self.summaryList.append(comment)
|
|
102
|
+
self.summaryList.append(f"the number of initial candidates: {len(initial_list)}")
|
|
103
|
+
|
|
104
|
+
# dump summary
|
|
105
|
+
def dump_summary(self, tmp_log, final_candidates=None):
|
|
106
|
+
if not self.summaryList:
|
|
107
|
+
return
|
|
108
|
+
tmp_log.info("")
|
|
109
|
+
for m in self.summaryList:
|
|
110
|
+
tmp_log.info(m)
|
|
111
|
+
if not final_candidates:
|
|
112
|
+
final_candidates = []
|
|
113
|
+
tmp_log.info(f"the number of final candidates: {len(final_candidates)}")
|
|
114
|
+
tmp_log.info("")
|
|
115
|
+
|
|
116
|
+
# add summary entry and show intermediate message
|
|
117
|
+
def add_summary_message(self, old_list: list, new_list: list, message: str, tmp_log: Any, msg_map: dict):
|
|
118
|
+
# consolidate lists to emit messages only for unified sites
|
|
119
|
+
old_list = self.get_unified_sites(old_list)
|
|
120
|
+
new_list = self.get_unified_sites(new_list)
|
|
121
|
+
# get skipped sites
|
|
122
|
+
skipped = [i for i in old_list if i not in new_list]
|
|
123
|
+
for skipped_site in skipped:
|
|
124
|
+
if skipped_site in msg_map:
|
|
125
|
+
tmp_log.info(msg_map[skipped_site])
|
|
126
|
+
tmp_log.info(f"{len(new_list)} candidates passed {message}")
|
|
127
|
+
# add a summary entry
|
|
128
|
+
if old_list and len(old_list) != len(new_list):
|
|
129
|
+
red = int(math.ceil(((len(old_list) - len(new_list)) * 100) / len(old_list)))
|
|
130
|
+
self.summaryList.append(f"{len(old_list):>5} -> {len(new_list):>3} candidates, {red:>3}% cut : {message}")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
Interaction.installSC(JobBrokerBase)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from pandajedi.jedicore import Interaction
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# base class for task brokerge
|
|
5
|
+
class TaskBrokerBase(object):
|
|
6
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
7
|
+
self.ddmIF = ddmIF
|
|
8
|
+
self.taskBufferIF = taskBufferIF
|
|
9
|
+
self.refresh()
|
|
10
|
+
|
|
11
|
+
def refresh(self):
|
|
12
|
+
self.siteMapper = self.taskBufferIF.get_site_mapper()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Interaction.installSC(TaskBrokerBase)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from liveconfigparser.LiveConfigParser import LiveConfigParser, expand_values
|
|
6
|
+
|
|
7
|
+
# get ConfigParser
|
|
8
|
+
tmp_conf = LiveConfigParser()
|
|
9
|
+
|
|
10
|
+
# read
|
|
11
|
+
tmp_conf.read("panda_jedi.cfg")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# dummy section class
|
|
15
|
+
class _SectionClass:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# load configmap
|
|
20
|
+
config_map_data = {}
|
|
21
|
+
if "PANDA_HOME" in os.environ:
|
|
22
|
+
config_map_name = "panda_jedi_config.json"
|
|
23
|
+
config_map_path = os.path.join(os.environ["PANDA_HOME"], "etc/config_json", config_map_name)
|
|
24
|
+
if os.path.exists(config_map_path):
|
|
25
|
+
with open(config_map_path) as f:
|
|
26
|
+
config_map_data = json.load(f)
|
|
27
|
+
|
|
28
|
+
# loop over all sections
|
|
29
|
+
for tmp_section in tmp_conf.sections():
|
|
30
|
+
# read section
|
|
31
|
+
tmp_dict = getattr(tmp_conf, tmp_section)
|
|
32
|
+
# load configmap
|
|
33
|
+
if tmp_section in config_map_data:
|
|
34
|
+
tmp_dict.update(config_map_data[tmp_section])
|
|
35
|
+
# make section class
|
|
36
|
+
tmp_self = _SectionClass()
|
|
37
|
+
# update module dict
|
|
38
|
+
sys.modules[__name__].__dict__[tmp_section] = tmp_self
|
|
39
|
+
# expand all values
|
|
40
|
+
expand_values(tmp_self, tmp_dict)
|