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,245 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import socket
|
|
3
|
+
import sys
|
|
4
|
+
import traceback
|
|
5
|
+
|
|
6
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
7
|
+
|
|
8
|
+
from pandajedi.jedibrokerage import AtlasBrokerUtils
|
|
9
|
+
from pandajedi.jediconfig import jedi_config
|
|
10
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
11
|
+
from pandaserver.dataservice import DataServiceUtils
|
|
12
|
+
|
|
13
|
+
from .WatchDogBase import WatchDogBase
|
|
14
|
+
|
|
15
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# task withholder watchdog for ATLAS
|
|
19
|
+
class AtlasTaskWithholderWatchDog(WatchDogBase):
|
|
20
|
+
# constructor
|
|
21
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
22
|
+
WatchDogBase.__init__(self, taskBufferIF, ddmIF)
|
|
23
|
+
self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}-dog"
|
|
24
|
+
# self.cronActions = {'forPrestage': 'atlas_prs'}
|
|
25
|
+
self.vo = "atlas"
|
|
26
|
+
self.prodSourceLabelList = ["managed"]
|
|
27
|
+
# call refresh
|
|
28
|
+
self.refresh()
|
|
29
|
+
|
|
30
|
+
# get process lock
|
|
31
|
+
def _get_lock(self):
|
|
32
|
+
return self.taskBufferIF.lockProcess_JEDI(
|
|
33
|
+
vo=self.vo,
|
|
34
|
+
prodSourceLabel="managed",
|
|
35
|
+
cloud=None,
|
|
36
|
+
workqueue_id=None,
|
|
37
|
+
resource_name=None,
|
|
38
|
+
component="AtlasTaskWithholderWatchDog",
|
|
39
|
+
pid=self.pid,
|
|
40
|
+
timeLimit=5,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# refresh information stored in the instance
|
|
44
|
+
def refresh(self):
|
|
45
|
+
# work queue mapper
|
|
46
|
+
self.workQueueMapper = self.taskBufferIF.getWorkQueueMap()
|
|
47
|
+
# site mapper
|
|
48
|
+
self.siteMapper = self.taskBufferIF.get_site_mapper()
|
|
49
|
+
# all sites
|
|
50
|
+
allSiteList = []
|
|
51
|
+
for siteName, tmpSiteSpec in self.siteMapper.siteSpecList.items():
|
|
52
|
+
# if tmpSiteSpec.type == 'analysis' or tmpSiteSpec.is_grandly_unified():
|
|
53
|
+
allSiteList.append(siteName)
|
|
54
|
+
self.allSiteList = allSiteList
|
|
55
|
+
|
|
56
|
+
# get map of site to list of RSEs and blacklisted RSEs
|
|
57
|
+
def get_site_rse_map_and_blacklisted_rse_set(self, prod_source_label):
|
|
58
|
+
site_rse_map = {}
|
|
59
|
+
blacklisted_rse_set = set()
|
|
60
|
+
for tmpPseudoSiteName in self.allSiteList:
|
|
61
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpPseudoSiteName)
|
|
62
|
+
tmpSiteName = tmpSiteSpec.get_unified_name()
|
|
63
|
+
scope_input, scope_output = DataServiceUtils.select_scope(tmpSiteSpec, prod_source_label, prod_source_label)
|
|
64
|
+
try:
|
|
65
|
+
tmp_ddm_spec = tmpSiteSpec.ddm_endpoints_input[scope_input]
|
|
66
|
+
endpoint_name = tmpSiteSpec.ddm_input[scope_input]
|
|
67
|
+
endpoint_token_map = tmp_ddm_spec.getTokenMap("input")
|
|
68
|
+
tmp_endpoint = tmp_ddm_spec.getEndPoint(endpoint_name)
|
|
69
|
+
except KeyError:
|
|
70
|
+
continue
|
|
71
|
+
else:
|
|
72
|
+
# fill site rse map
|
|
73
|
+
site_rse_map[tmpSiteName] = list(endpoint_token_map.values())
|
|
74
|
+
# blacklisted rse
|
|
75
|
+
if tmp_endpoint is not None and tmp_endpoint["blacklisted"] == "Y":
|
|
76
|
+
blacklisted_rse_set.add(endpoint_name)
|
|
77
|
+
# return
|
|
78
|
+
return site_rse_map, blacklisted_rse_set
|
|
79
|
+
|
|
80
|
+
# get busy sites
|
|
81
|
+
def get_busy_sites(self, gshare, cutoff):
|
|
82
|
+
busy_sites_list = []
|
|
83
|
+
# get global share
|
|
84
|
+
tmpSt, jobStatPrioMap = self.taskBufferIF.getJobStatisticsByGlobalShare(self.vo)
|
|
85
|
+
if not tmpSt:
|
|
86
|
+
# got nothing...
|
|
87
|
+
return busy_sites_list
|
|
88
|
+
for tmpPseudoSiteName in self.allSiteList:
|
|
89
|
+
tmpSiteSpec = self.siteMapper.getSite(tmpPseudoSiteName)
|
|
90
|
+
tmpSiteName = tmpSiteSpec.get_unified_name()
|
|
91
|
+
# get nQueue and nRunning
|
|
92
|
+
nRunning = AtlasBrokerUtils.getNumJobs(jobStatPrioMap, tmpSiteName, "running", workQueue_tag=gshare)
|
|
93
|
+
nQueue = 0
|
|
94
|
+
for jobStatus in ["defined", "assigned", "activated", "starting"]:
|
|
95
|
+
nQueue += AtlasBrokerUtils.getNumJobs(jobStatPrioMap, tmpSiteName, jobStatus, workQueue_tag=gshare)
|
|
96
|
+
# busy sites
|
|
97
|
+
if nQueue > max(cutoff, nRunning * 2):
|
|
98
|
+
busy_sites_list.append(tmpSiteName)
|
|
99
|
+
# return
|
|
100
|
+
return busy_sites_list
|
|
101
|
+
|
|
102
|
+
# # handle waiting jobs
|
|
103
|
+
# def do_make_tasks_pending(self, task_list):
|
|
104
|
+
# tmpLog = MsgWrapper(logger, 'do_make_tasks_pending')
|
|
105
|
+
# tmpLog.debug('start')
|
|
106
|
+
# # check every x min
|
|
107
|
+
# checkInterval = 20
|
|
108
|
+
# # make task pending
|
|
109
|
+
# for taskID, pending_reason in task_list:
|
|
110
|
+
# tmpLog = MsgWrapper(logger, '< #ATM #KV do_make_tasks_pending jediTaskID={0}>'.format(taskID))
|
|
111
|
+
# retVal = self.taskBufferIF.makeTaskPending_JEDI(taskID, reason=pending_reason)
|
|
112
|
+
# tmpLog.debug('done with {0}'.format(retVal))
|
|
113
|
+
|
|
114
|
+
# set tasks to be pending due to condition of data locality
|
|
115
|
+
def do_for_data_locality(self):
|
|
116
|
+
tmp_log = MsgWrapper(logger)
|
|
117
|
+
# refresh
|
|
118
|
+
self.refresh()
|
|
119
|
+
# list of resource type
|
|
120
|
+
# resource_type_list = [ rt.resource_name for rt in self.taskBufferIF.load_resource_types() ]
|
|
121
|
+
# loop
|
|
122
|
+
for prod_source_label in self.prodSourceLabelList:
|
|
123
|
+
# site-rse map and blacklisted rses
|
|
124
|
+
site_rse_map, blacklisted_rse_set = self.get_site_rse_map_and_blacklisted_rse_set(prod_source_label)
|
|
125
|
+
tmp_log.debug(f"Found {len(blacklisted_rse_set)} blacklisted RSEs : {','.join(list(blacklisted_rse_set))}")
|
|
126
|
+
# parameter from GDP config
|
|
127
|
+
upplimit_ioIntensity = self.taskBufferIF.getConfigValue("task_withholder", f"LIMIT_IOINTENSITY_{prod_source_label}", "jedi", self.vo)
|
|
128
|
+
lowlimit_currentPriority = self.taskBufferIF.getConfigValue("task_withholder", f"LIMIT_PRIORITY_{prod_source_label}", "jedi", self.vo)
|
|
129
|
+
if upplimit_ioIntensity is None:
|
|
130
|
+
upplimit_ioIntensity = 999999
|
|
131
|
+
if lowlimit_currentPriority is None:
|
|
132
|
+
lowlimit_currentPriority = -999999
|
|
133
|
+
upplimit_ioIntensity = max(upplimit_ioIntensity, 100)
|
|
134
|
+
# get work queue for gshare
|
|
135
|
+
work_queue_list = self.workQueueMapper.getAlignedQueueList(self.vo, prod_source_label)
|
|
136
|
+
# loop over work queue
|
|
137
|
+
for work_queue in work_queue_list:
|
|
138
|
+
gshare = work_queue.queue_name
|
|
139
|
+
# get cutoff
|
|
140
|
+
cutoff = self.taskBufferIF.getConfigValue("jobbroker", f"NQUEUELIMITSITE_{gshare}", "jedi", self.vo)
|
|
141
|
+
if not cutoff:
|
|
142
|
+
cutoff = 20
|
|
143
|
+
# busy sites
|
|
144
|
+
busy_sites_list = self.get_busy_sites(gshare, cutoff)
|
|
145
|
+
# rses of busy sites
|
|
146
|
+
busy_rses = set()
|
|
147
|
+
for site in busy_sites_list:
|
|
148
|
+
try:
|
|
149
|
+
busy_rses.update(set(site_rse_map[site]))
|
|
150
|
+
except KeyError:
|
|
151
|
+
continue
|
|
152
|
+
# make sql parameters of rses
|
|
153
|
+
to_exclude_rses = list(busy_rses | blacklisted_rse_set)
|
|
154
|
+
if not to_exclude_rses:
|
|
155
|
+
continue
|
|
156
|
+
rse_params_list = []
|
|
157
|
+
rse_params_map = {}
|
|
158
|
+
for j, rse in enumerate(to_exclude_rses):
|
|
159
|
+
rse_param = f":rse_{j + 1}"
|
|
160
|
+
rse_params_list.append(rse_param)
|
|
161
|
+
rse_params_map[rse_param] = rse
|
|
162
|
+
rse_params_str = ",".join(rse_params_list)
|
|
163
|
+
# sql
|
|
164
|
+
sql_query = (
|
|
165
|
+
"SELECT t.jediTaskID "
|
|
166
|
+
"FROM {jedi_schema}.JEDI_Tasks t "
|
|
167
|
+
"WHERE t.status IN ('ready','running','scouting') AND t.lockedBy IS NULL "
|
|
168
|
+
"AND t.gshare=:gshare "
|
|
169
|
+
"AND t.ioIntensity>=:ioIntensity AND t.currentPriority<:currentPriority "
|
|
170
|
+
"AND EXISTS ( "
|
|
171
|
+
"SELECT * FROM {jedi_schema}.JEDI_Datasets d "
|
|
172
|
+
"WHERE d.jediTaskID=t.jediTaskID "
|
|
173
|
+
"AND d.type='input' "
|
|
174
|
+
") "
|
|
175
|
+
"AND NOT EXISTS ( "
|
|
176
|
+
"SELECT * FROM {jedi_schema}.JEDI_Dataset_Locality dl "
|
|
177
|
+
"WHERE dl.jediTaskID=t.jediTaskID "
|
|
178
|
+
"AND dl.rse NOT IN ({rse_params_str}) "
|
|
179
|
+
") "
|
|
180
|
+
).format(jedi_schema=jedi_config.db.schemaJEDI, rse_params_str=rse_params_str)
|
|
181
|
+
# params map
|
|
182
|
+
params_map = {
|
|
183
|
+
":gshare": gshare,
|
|
184
|
+
":ioIntensity": upplimit_ioIntensity,
|
|
185
|
+
":currentPriority": lowlimit_currentPriority,
|
|
186
|
+
}
|
|
187
|
+
params_map.update(rse_params_map)
|
|
188
|
+
# pending reason
|
|
189
|
+
reason = (
|
|
190
|
+
"no local input data, ioIntensity>={ioIntensity}, currentPriority<{currentPriority},"
|
|
191
|
+
"nQueue>max({cutOff},nRunning*2) at all sites where the task can run".format(
|
|
192
|
+
ioIntensity=upplimit_ioIntensity, currentPriority=lowlimit_currentPriority, cutOff=cutoff
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
# set pending
|
|
196
|
+
dry_run = False
|
|
197
|
+
if dry_run:
|
|
198
|
+
dry_sql_query = (
|
|
199
|
+
"SELECT t.jediTaskID "
|
|
200
|
+
"FROM {jedi_schema}.JEDI_Tasks t "
|
|
201
|
+
"WHERE t.status IN ('ready','running','scouting') AND t.lockedBy IS NULL "
|
|
202
|
+
"AND t.gshare=:gshare "
|
|
203
|
+
"AND t.ioIntensity>=:ioIntensity AND t.currentPriority<:currentPriority "
|
|
204
|
+
"AND EXISTS ( "
|
|
205
|
+
"SELECT * FROM {jedi_schema}.JEDI_Datasets d "
|
|
206
|
+
"WHERE d.jediTaskID=t.jediTaskID "
|
|
207
|
+
"AND d.type='input' "
|
|
208
|
+
") "
|
|
209
|
+
"AND NOT EXISTS ( "
|
|
210
|
+
"SELECT * FROM {jedi_schema}.JEDI_Dataset_Locality dl "
|
|
211
|
+
"WHERE dl.jediTaskID=t.jediTaskID "
|
|
212
|
+
"AND dl.rse NOT IN ({rse_params_str}) "
|
|
213
|
+
") "
|
|
214
|
+
).format(jedi_schema=jedi_config.db.schemaJEDI, rse_params_str=rse_params_str)
|
|
215
|
+
res = self.taskBufferIF.querySQL(dry_sql_query, params_map)
|
|
216
|
+
n_tasks = 0 if res is None else len(res)
|
|
217
|
+
if n_tasks > 0:
|
|
218
|
+
result = [x[0] for x in res]
|
|
219
|
+
tmp_log.debug(f'[dry run] gshare: {gshare:<16} {n_tasks:>5} tasks would be pending : {result} ; reason="{reason}" ')
|
|
220
|
+
else:
|
|
221
|
+
n_tasks = self.taskBufferIF.queryTasksToBePending_JEDI(sql_query, params_map, reason)
|
|
222
|
+
if n_tasks is not None and n_tasks > 0:
|
|
223
|
+
tmp_log.info(f'gshare: {gshare:<16} {str(n_tasks):>5} tasks got pending ; reason="{reason}" ')
|
|
224
|
+
|
|
225
|
+
# main
|
|
226
|
+
def doAction(self):
|
|
227
|
+
try:
|
|
228
|
+
# get logger
|
|
229
|
+
origTmpLog = MsgWrapper(logger)
|
|
230
|
+
origTmpLog.debug("start")
|
|
231
|
+
# lock
|
|
232
|
+
got_lock = self._get_lock()
|
|
233
|
+
if not got_lock:
|
|
234
|
+
origTmpLog.debug("locked by another process. Skipped")
|
|
235
|
+
return self.SC_SUCCEEDED
|
|
236
|
+
origTmpLog.debug("got lock")
|
|
237
|
+
# make tasks pending under certain conditions
|
|
238
|
+
self.do_for_data_locality()
|
|
239
|
+
except Exception:
|
|
240
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
241
|
+
err_str = traceback.format_exc()
|
|
242
|
+
origTmpLog.error(f"failed with {errtype} {errvalue} ; {err_str}")
|
|
243
|
+
# return
|
|
244
|
+
origTmpLog.debug("done")
|
|
245
|
+
return self.SC_SUCCEEDED
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
# logger
|
|
5
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
6
|
+
|
|
7
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
8
|
+
from pandaserver.workflow.workflow_core import WorkflowInterface
|
|
9
|
+
|
|
10
|
+
from .WatchDogBase import WatchDogBase
|
|
11
|
+
|
|
12
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AtlasWorkflowManagerWatchDog(WatchDogBase):
|
|
16
|
+
"""
|
|
17
|
+
Workflow manager watchdog for ATLAS
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# constructor
|
|
21
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
22
|
+
WatchDogBase.__init__(self, taskBufferIF, ddmIF)
|
|
23
|
+
self.vo = "atlas"
|
|
24
|
+
self.workflow_interface = WorkflowInterface(taskBufferIF)
|
|
25
|
+
|
|
26
|
+
def doProcessWorkflows(self):
|
|
27
|
+
"""
|
|
28
|
+
Action to process active workflows
|
|
29
|
+
"""
|
|
30
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doProcessWorkflows")
|
|
31
|
+
tmpLog.debug("start")
|
|
32
|
+
try:
|
|
33
|
+
# watchdog lock
|
|
34
|
+
got_lock = self.get_process_lock("AtlasWFManagerDog.doProcessWorkflows", timeLimit=5)
|
|
35
|
+
if not got_lock:
|
|
36
|
+
tmpLog.debug("locked by another watchdog process. Skipped")
|
|
37
|
+
return
|
|
38
|
+
tmpLog.debug("got watchdog lock")
|
|
39
|
+
# process active workflows
|
|
40
|
+
stats = self.workflow_interface.process_active_workflows()
|
|
41
|
+
tmpLog.info(f"processed workflows: {stats}")
|
|
42
|
+
# done
|
|
43
|
+
tmpLog.debug("done")
|
|
44
|
+
except Exception:
|
|
45
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
46
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
47
|
+
|
|
48
|
+
# main
|
|
49
|
+
def doAction(self):
|
|
50
|
+
try:
|
|
51
|
+
# get logger
|
|
52
|
+
origTmpLog = MsgWrapper(logger)
|
|
53
|
+
origTmpLog.debug("start")
|
|
54
|
+
# clean up
|
|
55
|
+
# check
|
|
56
|
+
# process workflows
|
|
57
|
+
self.doProcessWorkflows()
|
|
58
|
+
except Exception:
|
|
59
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
60
|
+
origTmpLog.error(f"failed with {errtype} {errvalue}")
|
|
61
|
+
# return
|
|
62
|
+
origTmpLog.debug("done")
|
|
63
|
+
return self.SC_SUCCEEDED
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
2
|
+
|
|
3
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
4
|
+
|
|
5
|
+
from .TypicalWatchDogBase import TypicalWatchDogBase
|
|
6
|
+
|
|
7
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# watchdog for general purpose
|
|
11
|
+
class GenWatchDog(TypicalWatchDogBase):
|
|
12
|
+
# constructor
|
|
13
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
14
|
+
TypicalWatchDogBase.__init__(self, taskBufferIF, ddmIF)
|
|
15
|
+
|
|
16
|
+
# main
|
|
17
|
+
def doAction(self):
|
|
18
|
+
tmpLog = MsgWrapper(logger)
|
|
19
|
+
tmpLog.debug("start")
|
|
20
|
+
tmpLog.debug("done")
|
|
21
|
+
return self.SC_SUCCEEDED
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import socket
|
|
3
|
+
import sys
|
|
4
|
+
import traceback
|
|
5
|
+
|
|
6
|
+
from pandaserver.taskbuffer.JediTaskSpec import JediTaskSpec
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# watchdog to take actions for jumbo jobs
|
|
10
|
+
class JumboWatchDog:
|
|
11
|
+
# constructor
|
|
12
|
+
def __init__(self, taskBufferIF, ddmIF, log, vo, prodSourceLabel):
|
|
13
|
+
self.taskBufferIF = taskBufferIF
|
|
14
|
+
self.ddmIF = ddmIF
|
|
15
|
+
self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}_{os.getpgrp()}-jumbo"
|
|
16
|
+
self.log = log
|
|
17
|
+
self.vo = vo
|
|
18
|
+
self.prodSourceLabel = prodSourceLabel
|
|
19
|
+
self.component = "JumboWatchDog"
|
|
20
|
+
self.dryRun = True
|
|
21
|
+
|
|
22
|
+
# main
|
|
23
|
+
def run(self):
|
|
24
|
+
try:
|
|
25
|
+
# get process lock
|
|
26
|
+
locked = self.taskBufferIF.lockProcess_JEDI(
|
|
27
|
+
vo=self.vo,
|
|
28
|
+
prodSourceLabel=self.prodSourceLabel,
|
|
29
|
+
cloud=None,
|
|
30
|
+
workqueue_id=None,
|
|
31
|
+
resource_name=None,
|
|
32
|
+
component=self.component,
|
|
33
|
+
pid=self.pid,
|
|
34
|
+
timeLimit=10,
|
|
35
|
+
)
|
|
36
|
+
if not locked:
|
|
37
|
+
self.log.debug(f"component={self.component} skipped since locked by another")
|
|
38
|
+
return
|
|
39
|
+
# get parameters for conversion
|
|
40
|
+
self.log.debug(f"component={self.component} start")
|
|
41
|
+
maxTasks = self.taskBufferIF.getConfigValue(self.component, "JUMBO_MAX_TASKS", "jedi", self.vo)
|
|
42
|
+
if maxTasks is None:
|
|
43
|
+
maxTasks = 1
|
|
44
|
+
nEventsToDisable = self.taskBufferIF.getConfigValue(self.component, "JUMBO_MIN_EVENTS_DISABLE", "jedi", self.vo)
|
|
45
|
+
if nEventsToDisable is None:
|
|
46
|
+
nEventsToDisable = 100000
|
|
47
|
+
nEventsToEnable = self.taskBufferIF.getConfigValue(self.component, "JUMBO_MIN_EVENTS_ENABLE", "jedi", self.vo)
|
|
48
|
+
if nEventsToEnable is None:
|
|
49
|
+
nEventsToEnable = nEventsToDisable * 10
|
|
50
|
+
maxEvents = self.taskBufferIF.getConfigValue(self.component, "JUMBO_MAX_EVENTS", "jedi", self.vo)
|
|
51
|
+
if maxEvents is None:
|
|
52
|
+
maxEvents = maxTasks * nEventsToEnable // 2
|
|
53
|
+
nJumboPerTask = self.taskBufferIF.getConfigValue(self.component, "JUMBO_PER_TASK", "jedi", self.vo)
|
|
54
|
+
if nJumboPerTask is None:
|
|
55
|
+
nJumboPerTask = 1
|
|
56
|
+
nJumboPerSite = self.taskBufferIF.getConfigValue(self.component, "JUMBO_PER_SITE", "jedi", self.vo)
|
|
57
|
+
if nJumboPerSite is None:
|
|
58
|
+
nJumboPerSite = 1
|
|
59
|
+
maxPrio = self.taskBufferIF.getConfigValue(self.component, "JUMBO_MAX_CURR_PRIO", "jedi", self.vo)
|
|
60
|
+
if maxPrio is None:
|
|
61
|
+
maxPrio = 500
|
|
62
|
+
progressToBoost = self.taskBufferIF.getConfigValue(self.component, "JUMBO_PROG_TO_BOOST", "jedi", self.vo)
|
|
63
|
+
if progressToBoost is None:
|
|
64
|
+
progressToBoost = 95
|
|
65
|
+
maxFilesToBoost = self.taskBufferIF.getConfigValue(self.component, "JUMBO_MAX_FILES_TO_BOOST", "jedi", self.vo)
|
|
66
|
+
if maxFilesToBoost is None:
|
|
67
|
+
maxFilesToBoost = 500
|
|
68
|
+
prioToBoost = 900
|
|
69
|
+
prioWhenDisabled = self.taskBufferIF.getConfigValue(self.component, "JUMBO_PRIO_DISABLED", "jedi", self.vo)
|
|
70
|
+
if prioWhenDisabled is None:
|
|
71
|
+
prioWhenDisabled = 500
|
|
72
|
+
# get current info
|
|
73
|
+
tasksWithJumbo = self.taskBufferIF.getTaskWithJumbo_JEDI(self.vo, self.prodSourceLabel)
|
|
74
|
+
totEvents = 0
|
|
75
|
+
doneEvents = 0
|
|
76
|
+
nTasks = 0
|
|
77
|
+
for jediTaskID, taskData in tasksWithJumbo.items():
|
|
78
|
+
# disable jumbo
|
|
79
|
+
if taskData["useJumbo"] != JediTaskSpec.enum_useJumbo["disabled"] and taskData["site"] is None:
|
|
80
|
+
if taskData["nEvents"] - taskData["nEventsDone"] < nEventsToDisable:
|
|
81
|
+
# disable
|
|
82
|
+
self.log.info(
|
|
83
|
+
"component={0} disable jumbo in jediTaskID={1} due to n_events_to_process={2} < {3}".format(
|
|
84
|
+
self.component, jediTaskID, taskData["nEvents"] - taskData["nEventsDone"], nEventsToDisable
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
self.taskBufferIF.enableJumboJobs(jediTaskID, 0, 0)
|
|
88
|
+
else:
|
|
89
|
+
# wait
|
|
90
|
+
nTasks += 1
|
|
91
|
+
totEvents += taskData["nEvents"]
|
|
92
|
+
doneEvents += taskData["nEventsDone"]
|
|
93
|
+
self.log.info(
|
|
94
|
+
"component={0} keep jumbo in jediTaskID={1} due to n_events_to_process={2} > {3}".format(
|
|
95
|
+
self.component, jediTaskID, taskData["nEvents"] - taskData["nEventsDone"], nEventsToDisable
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
# increase priority for jumbo disabled
|
|
99
|
+
if taskData["useJumbo"] == JediTaskSpec.enum_useJumbo["disabled"] and taskData["currentPriority"] < prioWhenDisabled:
|
|
100
|
+
self.taskBufferIF.changeTaskPriorityPanda(jediTaskID, prioWhenDisabled)
|
|
101
|
+
self.log.info(f"component={self.component} priority boost to {prioWhenDisabled} after disabing jumbo in in jediTaskID={jediTaskID}")
|
|
102
|
+
# increase priority when close to completion
|
|
103
|
+
if (
|
|
104
|
+
taskData["nEvents"] > 0
|
|
105
|
+
and (taskData["nEvents"] - taskData["nEventsDone"]) * 100 // taskData["nEvents"] < progressToBoost
|
|
106
|
+
and taskData["currentPriority"] < prioToBoost
|
|
107
|
+
and (taskData["nFiles"] - taskData["nFilesDone"]) < maxFilesToBoost
|
|
108
|
+
):
|
|
109
|
+
# boost
|
|
110
|
+
tmpStr = "component={0} priority boost to {5} for jediTaskID={1} due to n_events_done={2} > {3}*{4}% ".format(
|
|
111
|
+
self.component, jediTaskID, taskData["nEventsDone"], taskData["nEvents"], progressToBoost, prioToBoost
|
|
112
|
+
)
|
|
113
|
+
tmpStr += f"n_files_remaining={taskData['nFiles'] - taskData['nFilesDone']} < {maxFilesToBoost}"
|
|
114
|
+
self.log.info(tmpStr)
|
|
115
|
+
self.taskBufferIF.changeTaskPriorityPanda(jediTaskID, prioToBoost)
|
|
116
|
+
# kick pending
|
|
117
|
+
if taskData["taskStatus"] in ["pending", "running"] and taskData["useJumbo"] in [
|
|
118
|
+
JediTaskSpec.enum_useJumbo["pending"],
|
|
119
|
+
JediTaskSpec.enum_useJumbo["running"],
|
|
120
|
+
]:
|
|
121
|
+
nActiveJumbo = 0
|
|
122
|
+
for computingSite, jobStatusMap in taskData["jumboJobs"].items():
|
|
123
|
+
for jobStatus, nJobs in jobStatusMap.items():
|
|
124
|
+
if jobStatus in ["defined", "assigned", "activated", "sent", "starting", "running", "transferring", "holding"]:
|
|
125
|
+
nActiveJumbo += nJobs
|
|
126
|
+
if nActiveJumbo == 0:
|
|
127
|
+
self.log.info(f"component={self.component} kick jumbo in {taskData['taskStatus']} jediTaskID={jediTaskID}")
|
|
128
|
+
self.taskBufferIF.kickPendingTasksWithJumbo_JEDI(jediTaskID)
|
|
129
|
+
# reset input to re-generate co-jumbo
|
|
130
|
+
if taskData["currentPriority"] >= prioToBoost:
|
|
131
|
+
nReset = self.taskBufferIF.resetInputToReGenCoJumbo_JEDI(jediTaskID)
|
|
132
|
+
if nReset is not None and nReset > 0:
|
|
133
|
+
self.log.info(f"component={self.component} reset {nReset} inputs to regenerate co-jumbo for jediTaskID={jediTaskID}")
|
|
134
|
+
else:
|
|
135
|
+
self.log.debug(f"component={self.component} tried to reset inputs to regenerate co-jumbo with {nReset} for jediTaskID={jediTaskID}")
|
|
136
|
+
self.log.info(
|
|
137
|
+
f"component={self.component} total_events={totEvents} n_events_to_process={totEvents - doneEvents} n_tasks={nTasks} available for jumbo"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
self.log.debug(f"component={self.component} done")
|
|
141
|
+
except Exception:
|
|
142
|
+
# error
|
|
143
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
144
|
+
errStr = f": {errtype.__name__} {errvalue}"
|
|
145
|
+
errStr.strip()
|
|
146
|
+
errStr += traceback.format_exc()
|
|
147
|
+
self.log.error(errStr)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import socket
|
|
3
|
+
|
|
4
|
+
from pandajedi.jediconfig import jedi_config
|
|
5
|
+
from pandajedi.jedicore import Interaction
|
|
6
|
+
from pandaserver.proxycache.token_cache import TokenCache
|
|
7
|
+
from pandaserver.srvcore import CoreUtils
|
|
8
|
+
|
|
9
|
+
from .WatchDogBase import WatchDogBase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# base class for typical watchdog (for production and analysis, etc.)
|
|
13
|
+
class TypicalWatchDogBase(WatchDogBase):
|
|
14
|
+
# pre-action
|
|
15
|
+
def pre_action(self, tmpLog, vo, prodSourceLabel, pid, *args, **kwargs):
|
|
16
|
+
# rescue picked files
|
|
17
|
+
tmpLog.info(f"rescue tasks with picked files for vo={vo} label={prodSourceLabel}")
|
|
18
|
+
tmpRet = self.taskBufferIF.rescuePickedFiles_JEDI(vo, prodSourceLabel, jedi_config.watchdog.waitForPicked)
|
|
19
|
+
if tmpRet is None:
|
|
20
|
+
# failed
|
|
21
|
+
tmpLog.error("failed to rescue")
|
|
22
|
+
else:
|
|
23
|
+
tmpLog.info(f"rescued {tmpRet} tasks")
|
|
24
|
+
# reactivate pending tasks
|
|
25
|
+
tmpLog.info(f"reactivate pending tasks for vo={vo} label={prodSourceLabel}")
|
|
26
|
+
timeoutForPending = self.taskBufferIF.getConfigValue("watchdog", f"PENDING_TIMEOUT_{prodSourceLabel}", "jedi", vo)
|
|
27
|
+
if timeoutForPending is None:
|
|
28
|
+
if hasattr(jedi_config.watchdog, "timeoutForPendingVoLabel"):
|
|
29
|
+
timeoutForPending = CoreUtils.getConfigParam(jedi_config.watchdog.timeoutForPendingVoLabel, vo, prodSourceLabel)
|
|
30
|
+
if timeoutForPending is None:
|
|
31
|
+
timeoutForPending = jedi_config.watchdog.timeoutForPending
|
|
32
|
+
timeoutForPending = int(timeoutForPending) * 24
|
|
33
|
+
tmpRet, msg_driven_taskid_set = self.taskBufferIF.reactivatePendingTasks_JEDI(
|
|
34
|
+
vo, prodSourceLabel, jedi_config.watchdog.waitForPending, timeoutForPending
|
|
35
|
+
)
|
|
36
|
+
if tmpRet is None:
|
|
37
|
+
# failed
|
|
38
|
+
tmpLog.error("failed to reactivate")
|
|
39
|
+
else:
|
|
40
|
+
tmpLog.info(f"reactivated {tmpRet} tasks")
|
|
41
|
+
for jeditaskid in msg_driven_taskid_set:
|
|
42
|
+
push_ret = self.taskBufferIF.push_task_trigger_message("jedi_job_generator", jeditaskid)
|
|
43
|
+
if push_ret:
|
|
44
|
+
tmpLog.debug(f"pushed trigger message to jedi_job_generator for jeditaskid={jeditaskid}")
|
|
45
|
+
else:
|
|
46
|
+
tmpLog.warning(f"failed to push trigger message to jedi_job_generator for jeditaskid={jeditaskid}")
|
|
47
|
+
# unlock tasks
|
|
48
|
+
tmpLog.info(f"unlock tasks for vo={vo} label={prodSourceLabel} host={socket.getfqdn().split('.')[0]} pgid={os.getpgrp()}")
|
|
49
|
+
tmpRet = self.taskBufferIF.unlockTasks_JEDI(vo, prodSourceLabel, 10, socket.getfqdn().split(".")[0], os.getpgrp())
|
|
50
|
+
if tmpRet is None:
|
|
51
|
+
# failed
|
|
52
|
+
tmpLog.error("failed to unlock")
|
|
53
|
+
else:
|
|
54
|
+
tmpLog.info(f"unlock {tmpRet} tasks")
|
|
55
|
+
# unlock tasks
|
|
56
|
+
tmpLog.info(f"unlock tasks for vo={vo} label={prodSourceLabel}")
|
|
57
|
+
tmpRet = self.taskBufferIF.unlockTasks_JEDI(vo, prodSourceLabel, jedi_config.watchdog.waitForLocked)
|
|
58
|
+
if tmpRet is None:
|
|
59
|
+
# failed
|
|
60
|
+
tmpLog.error("failed to unlock")
|
|
61
|
+
else:
|
|
62
|
+
tmpLog.info(f"unlock {tmpRet} tasks")
|
|
63
|
+
# restart contents update
|
|
64
|
+
tmpLog.info(f"restart contents update for vo={vo} label={prodSourceLabel}")
|
|
65
|
+
tmpRet, msg_driven_taskid_set = self.taskBufferIF.restartTasksForContentsUpdate_JEDI(vo, prodSourceLabel)
|
|
66
|
+
if tmpRet is None:
|
|
67
|
+
# failed
|
|
68
|
+
tmpLog.error("failed to restart")
|
|
69
|
+
else:
|
|
70
|
+
tmpLog.info(f"restarted {tmpRet} tasks")
|
|
71
|
+
for jeditaskid in msg_driven_taskid_set:
|
|
72
|
+
push_ret = self.taskBufferIF.push_task_trigger_message("jedi_contents_feeder", jeditaskid)
|
|
73
|
+
if push_ret:
|
|
74
|
+
tmpLog.debug(f"pushed trigger message to jedi_contents_feeder for jeditaskid={jeditaskid}")
|
|
75
|
+
else:
|
|
76
|
+
tmpLog.warning(f"failed to push trigger message to jedi_contents_feeder for jeditaskid={jeditaskid}")
|
|
77
|
+
# kick exhausted tasks
|
|
78
|
+
tmpLog.info(f"kick exhausted tasks for vo={vo} label={prodSourceLabel}")
|
|
79
|
+
tmpRet = self.taskBufferIF.kickExhaustedTasks_JEDI(vo, prodSourceLabel, jedi_config.watchdog.waitForExhausted)
|
|
80
|
+
if tmpRet is None:
|
|
81
|
+
# failed
|
|
82
|
+
tmpLog.error("failed to kick")
|
|
83
|
+
else:
|
|
84
|
+
tmpLog.info(f"kicked {tmpRet} tasks")
|
|
85
|
+
# finish tasks when goal is reached
|
|
86
|
+
tmpLog.info(f"finish achieved tasks for vo={vo} label={prodSourceLabel}")
|
|
87
|
+
tmpRet = self.taskBufferIF.getAchievedTasks_JEDI(vo, prodSourceLabel, jedi_config.watchdog.waitForAchieved)
|
|
88
|
+
if tmpRet is None:
|
|
89
|
+
# failed
|
|
90
|
+
tmpLog.error("failed to finish")
|
|
91
|
+
else:
|
|
92
|
+
for jediTaskID in tmpRet:
|
|
93
|
+
self.taskBufferIF.sendCommandTaskPanda(jediTaskID, "JEDI. Goal reached", True, "finish", comQualifier="soft")
|
|
94
|
+
tmpLog.info(f"finished {tmpRet} tasks")
|
|
95
|
+
# rescue unlocked tasks with picked files
|
|
96
|
+
tmpLog.info(f"rescue unlocked tasks with picked files for vo={vo} label={prodSourceLabel}")
|
|
97
|
+
tmpRet = self.taskBufferIF.rescueUnLockedTasksWithPicked_JEDI(vo, prodSourceLabel, 60, pid)
|
|
98
|
+
if tmpRet is None:
|
|
99
|
+
# failed
|
|
100
|
+
tmpLog.error("failed to rescue unlocked tasks")
|
|
101
|
+
else:
|
|
102
|
+
tmpLog.info(f"rescue unlocked {tmpRet} tasks")
|
|
103
|
+
# cache tokens
|
|
104
|
+
got_lock = self.get_process_lock("TypicalWatchDogBase.cache_tokens", timeLimit=5)
|
|
105
|
+
if not got_lock:
|
|
106
|
+
tmpLog.debug("locked by another watchdog process. Skipped to cache tokens")
|
|
107
|
+
else:
|
|
108
|
+
tmpLog.info(f"cache tokens")
|
|
109
|
+
cacher = TokenCache()
|
|
110
|
+
cacher.run()
|
|
111
|
+
|
|
112
|
+
# action to set scout job data w/o scouts
|
|
113
|
+
def doActionToSetScoutJobData(self, gTmpLog):
|
|
114
|
+
tmpRet = self.taskBufferIF.setScoutJobDataToTasks_JEDI(self.vo, self.prodSourceLabel)
|
|
115
|
+
if tmpRet is None:
|
|
116
|
+
# failed
|
|
117
|
+
gTmpLog.error("failed to set scout job data")
|
|
118
|
+
else:
|
|
119
|
+
gTmpLog.info("set scout job data successfully")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
Interaction.installSC(TypicalWatchDogBase)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import socket
|
|
3
|
+
|
|
4
|
+
from pandajedi.jedicore import Interaction
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# base class for watchdog
|
|
8
|
+
class WatchDogBase(object):
|
|
9
|
+
"""
|
|
10
|
+
Base class for watchdog
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# constructor
|
|
14
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
15
|
+
self.taskBufferIF = taskBufferIF
|
|
16
|
+
self.ddmIF = ddmIF
|
|
17
|
+
self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}-dog"
|
|
18
|
+
self.vo = None
|
|
19
|
+
self.refresh()
|
|
20
|
+
|
|
21
|
+
def get_process_lock(self, component, timeLimit=5, **kwargs):
|
|
22
|
+
"""
|
|
23
|
+
Shortcut of get process lock for watchdog action methods
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
component (str): spec of the request
|
|
27
|
+
timeLimit (int): lifetime of the lock in minutes
|
|
28
|
+
**kwargs: other arguments for taskBufferIF.lockProcess_JEDI
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
bool : True if got lock, False otherwise
|
|
32
|
+
"""
|
|
33
|
+
return self.taskBufferIF.lockProcess_JEDI(
|
|
34
|
+
vo=self.vo,
|
|
35
|
+
prodSourceLabel=kwargs.get("prodSourceLabel", "default"),
|
|
36
|
+
cloud=kwargs.get("cloud", None),
|
|
37
|
+
workqueue_id=kwargs.get("workqueue_id", None),
|
|
38
|
+
resource_name=kwargs.get("resource_name", None),
|
|
39
|
+
component=component,
|
|
40
|
+
pid=self.pid,
|
|
41
|
+
timeLimit=timeLimit,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# refresh
|
|
45
|
+
def refresh(self):
|
|
46
|
+
self.siteMapper = self.taskBufferIF.get_site_mapper()
|
|
47
|
+
|
|
48
|
+
# pre-action
|
|
49
|
+
def pre_action(self, tmpLog, vo, prodSourceLabel, pid, *args, **kwargs):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
Interaction.installSC(WatchDogBase)
|
|
File without changes
|