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,185 @@
|
|
|
1
|
+
import gc
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from pandacommon.pandalogger import logger_utils
|
|
6
|
+
from pandacommon.pandautils.PandaUtils import try_malloc_trim
|
|
7
|
+
|
|
8
|
+
from pandajedi.jedicore.ThreadUtils import ListWithLock
|
|
9
|
+
from pandajedi.jediddm.DDMInterface import DDMInterface
|
|
10
|
+
from pandajedi.jedimsgprocessor.base_msg_processor import BaseMsgProcPlugin
|
|
11
|
+
from pandajedi.jediorder.JobGenerator import JobGeneratorThread, get_params_to_get_tasks
|
|
12
|
+
from pandajedi.jediorder.TaskSetupper import TaskSetupper
|
|
13
|
+
from pandaserver.srvcore import CoreUtils
|
|
14
|
+
|
|
15
|
+
base_logger = logger_utils.setup_logger(__name__.split(".")[-1])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Jedi Job Generator message processor plugin
|
|
19
|
+
class JediJobGeneratorMsgProcPlugin(BaseMsgProcPlugin):
|
|
20
|
+
"""
|
|
21
|
+
Message-driven Job Generator
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def initialize(self):
|
|
25
|
+
BaseMsgProcPlugin.initialize(self)
|
|
26
|
+
# DDM interface
|
|
27
|
+
self.ddmIF = DDMInterface()
|
|
28
|
+
self.ddmIF.setupInterface()
|
|
29
|
+
self.task_setupper_map = {}
|
|
30
|
+
# cache heavy metadata objects with short TTL to reduce allocation churn
|
|
31
|
+
self._cache_ttl_sec = 300
|
|
32
|
+
self._site_mapper = None
|
|
33
|
+
self._site_mapper_ts = 0
|
|
34
|
+
self._work_queue_mapper = None
|
|
35
|
+
self._work_queue_mapper_ts = 0
|
|
36
|
+
self._resource_types = None
|
|
37
|
+
self._resource_types_ts = 0
|
|
38
|
+
self._params_to_get_tasks = {}
|
|
39
|
+
# memory limit to trigger early cleanup to avoid OOM killer. This is not a hard limit, just a threshold to trigger early cleanup.
|
|
40
|
+
self._mem_usage_threshold_mb = 1500
|
|
41
|
+
# get SiteMapper
|
|
42
|
+
# siteMapper = self.tbIF.get_site_mapper()
|
|
43
|
+
# get work queue mapper
|
|
44
|
+
# workQueueMapper = self.tbIF.getWorkQueueMap()
|
|
45
|
+
# get TaskSetupper
|
|
46
|
+
# taskSetupper = TaskSetupper(self.vos, self.prodSourceLabels)
|
|
47
|
+
# taskSetupper.initializeMods(self.tbIF, self.ddmIF)
|
|
48
|
+
self.pid = self.get_pid()
|
|
49
|
+
|
|
50
|
+
def _is_cache_valid(self, ts):
|
|
51
|
+
return (time.time() - ts) < self._cache_ttl_sec
|
|
52
|
+
|
|
53
|
+
def _get_site_mapper(self):
|
|
54
|
+
if self._site_mapper is None or not self._is_cache_valid(self._site_mapper_ts):
|
|
55
|
+
self._site_mapper = self.tbIF.get_site_mapper()
|
|
56
|
+
self._site_mapper_ts = time.time()
|
|
57
|
+
return self._site_mapper
|
|
58
|
+
|
|
59
|
+
def _get_work_queue_mapper(self):
|
|
60
|
+
if self._work_queue_mapper is None or not self._is_cache_valid(self._work_queue_mapper_ts):
|
|
61
|
+
self._work_queue_mapper = self.tbIF.getWorkQueueMap()
|
|
62
|
+
self._work_queue_mapper_ts = time.time()
|
|
63
|
+
return self._work_queue_mapper
|
|
64
|
+
|
|
65
|
+
def _get_resource_types(self):
|
|
66
|
+
if self._resource_types is None or not self._is_cache_valid(self._resource_types_ts):
|
|
67
|
+
self._resource_types = self.tbIF.load_resource_types()
|
|
68
|
+
self._resource_types_ts = time.time()
|
|
69
|
+
return self._resource_types
|
|
70
|
+
|
|
71
|
+
def process(self, msg_obj):
|
|
72
|
+
tmp_log = logger_utils.make_logger(base_logger, token=self.get_pid(), method_name="process")
|
|
73
|
+
input_list = None
|
|
74
|
+
gen_thr = None
|
|
75
|
+
tmp_list = None
|
|
76
|
+
taskSpec = None
|
|
77
|
+
taskSetupper = None
|
|
78
|
+
# start
|
|
79
|
+
tmp_log.info("start")
|
|
80
|
+
tmp_log.debug(f"sub_id={msg_obj.sub_id} ; msg_id={msg_obj.msg_id}")
|
|
81
|
+
# parse json
|
|
82
|
+
try:
|
|
83
|
+
msg_dict = json.loads(msg_obj.data)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
err_str = f"failed to parse message json {msg_obj.data} , skipped. {e.__class__.__name__} : {e}"
|
|
86
|
+
tmp_log.error(err_str)
|
|
87
|
+
raise
|
|
88
|
+
# sanity check
|
|
89
|
+
try:
|
|
90
|
+
msg_type = msg_dict["msg_type"]
|
|
91
|
+
except Exception as e:
|
|
92
|
+
err_str = f"failed to parse message object dict {msg_dict} , skipped. {e.__class__.__name__} : {e}"
|
|
93
|
+
tmp_log.error(err_str)
|
|
94
|
+
raise
|
|
95
|
+
if msg_type != "jedi_job_generator":
|
|
96
|
+
# FIXME
|
|
97
|
+
err_str = f"got unknown msg_type {msg_type} , skipped"
|
|
98
|
+
tmp_log.error(err_str)
|
|
99
|
+
raise
|
|
100
|
+
# run
|
|
101
|
+
try:
|
|
102
|
+
# get task to generate jobs
|
|
103
|
+
task_id = int(msg_dict["taskid"])
|
|
104
|
+
_, taskSpec = self.tbIF.getTaskWithID_JEDI(task_id)
|
|
105
|
+
if not taskSpec:
|
|
106
|
+
tmp_log.debug(f"unknown task_id={task_id}, skipped")
|
|
107
|
+
else:
|
|
108
|
+
tmp_log.debug(f"processing task_id={task_id} workqueue_id={taskSpec.workQueue_ID} status={taskSpec.status}")
|
|
109
|
+
# get WQ
|
|
110
|
+
vo = taskSpec.vo
|
|
111
|
+
prodSourceLabel = taskSpec.prodSourceLabel
|
|
112
|
+
workQueue = self._get_work_queue_mapper().getQueueWithIDGshare(taskSpec.workQueue_ID, taskSpec.gshare)
|
|
113
|
+
# get resource types
|
|
114
|
+
resource_types = self._get_resource_types()
|
|
115
|
+
if not resource_types:
|
|
116
|
+
raise RuntimeError("failed to get resource types")
|
|
117
|
+
# nFiles from shared JobGenerator config resolver
|
|
118
|
+
tmp_params = get_params_to_get_tasks(
|
|
119
|
+
self.tbIF, self._params_to_get_tasks, vo, prodSourceLabel, workQueue.queue_name if workQueue else "", taskSpec.cloud
|
|
120
|
+
)
|
|
121
|
+
nFiles = tmp_params["nFiles"]
|
|
122
|
+
# get inputs
|
|
123
|
+
tmp_list = self.tbIF.getTasksToBeProcessed_JEDI(self.pid, None, workQueue, None, None, nFiles=nFiles, target_tasks=[task_id])
|
|
124
|
+
if tmp_list:
|
|
125
|
+
input_list = ListWithLock(tmp_list)
|
|
126
|
+
# run generator inline to avoid creating an extra worker thread.
|
|
127
|
+
siteMapper = self._get_site_mapper()
|
|
128
|
+
setupper_key = (vo, prodSourceLabel)
|
|
129
|
+
taskSetupper = self.task_setupper_map.get(setupper_key)
|
|
130
|
+
if taskSetupper is None:
|
|
131
|
+
taskSetupper = TaskSetupper(vo, prodSourceLabel)
|
|
132
|
+
taskSetupper.initializeMods(self.tbIF, self.ddmIF)
|
|
133
|
+
self.task_setupper_map[setupper_key] = taskSetupper
|
|
134
|
+
# log memory usage before running job generator to help identify if this is a source of memory bloat.
|
|
135
|
+
gen_thr = JobGeneratorThread(
|
|
136
|
+
input_list,
|
|
137
|
+
None,
|
|
138
|
+
self.tbIF,
|
|
139
|
+
self.ddmIF,
|
|
140
|
+
siteMapper,
|
|
141
|
+
True,
|
|
142
|
+
taskSetupper,
|
|
143
|
+
self.pid,
|
|
144
|
+
workQueue,
|
|
145
|
+
"pjmsg",
|
|
146
|
+
None,
|
|
147
|
+
None,
|
|
148
|
+
None,
|
|
149
|
+
False,
|
|
150
|
+
resource_types,
|
|
151
|
+
)
|
|
152
|
+
gen_thr.run()
|
|
153
|
+
tmp_log.info(f"task_id={task_id} generated jobs")
|
|
154
|
+
else:
|
|
155
|
+
tmp_log.debug(f"task_id={task_id} is not considered to be processed, skipped")
|
|
156
|
+
except Exception as e:
|
|
157
|
+
err_str = f"failed to run, skipped. {e.__class__.__name__} : {e}"
|
|
158
|
+
tmp_log.error(err_str)
|
|
159
|
+
raise
|
|
160
|
+
finally:
|
|
161
|
+
# Explicitly clear large per-run maps to release references as early as possible.
|
|
162
|
+
if gen_thr is not None:
|
|
163
|
+
try:
|
|
164
|
+
gen_thr.buildSpecMap.clear()
|
|
165
|
+
gen_thr.finished_lib_specs_map.clear()
|
|
166
|
+
gen_thr.active_lib_specs_map.clear()
|
|
167
|
+
gen_thr.inputList = None
|
|
168
|
+
except Exception:
|
|
169
|
+
pass
|
|
170
|
+
gen_thr = None
|
|
171
|
+
input_list = None
|
|
172
|
+
tmp_list = None
|
|
173
|
+
taskSpec = None
|
|
174
|
+
taskSetupper = None
|
|
175
|
+
# If memory usage is above the threshold, trigger early cleanup to avoid OOM killer.
|
|
176
|
+
mem_usage = CoreUtils.getMemoryUsage()
|
|
177
|
+
if mem_usage is None:
|
|
178
|
+
tmp_log.warning("failed to get memory usage, skipped memory cleanup")
|
|
179
|
+
elif mem_usage > self._mem_usage_threshold_mb:
|
|
180
|
+
gc.collect()
|
|
181
|
+
try_malloc_trim(tmp_log)
|
|
182
|
+
new_mem_usage = CoreUtils.getMemoryUsage()
|
|
183
|
+
tmp_log.debug(f"trimmed memory usage from {mem_usage} MB to {new_mem_usage} MB")
|
|
184
|
+
# done
|
|
185
|
+
tmp_log.info("done")
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from pandacommon.pandalogger import logger_utils
|
|
4
|
+
|
|
5
|
+
from pandajedi.jediconfig import jedi_config
|
|
6
|
+
from pandajedi.jedicore.FactoryBase import FactoryBase
|
|
7
|
+
from pandajedi.jediddm.DDMInterface import DDMInterface
|
|
8
|
+
from pandajedi.jedimsgprocessor.base_msg_processor import BaseMsgProcPlugin
|
|
9
|
+
from pandajedi.jediorder.PostProcessor import PostProcessorThread
|
|
10
|
+
from pandaserver.srvcore.CoreUtils import convert_config_params, parse_init_params
|
|
11
|
+
|
|
12
|
+
base_logger = logger_utils.setup_logger(__name__.split(".")[-1])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Jedi Post-Processor message processor plugin
|
|
16
|
+
class JediPostProcessorMsgProcPlugin(BaseMsgProcPlugin):
|
|
17
|
+
"""
|
|
18
|
+
Message-driven Post-Processor
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def initialize(self):
|
|
22
|
+
BaseMsgProcPlugin.initialize(self)
|
|
23
|
+
# DDM interface
|
|
24
|
+
ddmIF = DDMInterface()
|
|
25
|
+
ddmIF.setupInterface()
|
|
26
|
+
# factory bases and post processor thread object for all vos and prodsourcelabels
|
|
27
|
+
self.post_processor_thread_dict = dict()
|
|
28
|
+
for itemStr in jedi_config.postprocessor.procConfig.split(";"):
|
|
29
|
+
items = convert_config_params(itemStr)
|
|
30
|
+
vos = parse_init_params(items[0])
|
|
31
|
+
prodsourcelabels = parse_init_params(items[1])
|
|
32
|
+
tmp_factory_base_obj = FactoryBase(vos=vos, sourceLabels=prodsourcelabels, logger=base_logger, modConfig=jedi_config.postprocessor.modConfig)
|
|
33
|
+
tmp_post_processor_thread_obj = PostProcessorThread(
|
|
34
|
+
taskList=None, threadPool=None, taskbufferIF=self.tbIF, ddmIF=ddmIF, implFactory=tmp_factory_base_obj
|
|
35
|
+
)
|
|
36
|
+
for vo in vos:
|
|
37
|
+
for prodsourcelabel in prodsourcelabels:
|
|
38
|
+
self.post_processor_thread_dict[(vo, prodsourcelabel)] = tmp_post_processor_thread_obj
|
|
39
|
+
|
|
40
|
+
def process(self, msg_obj):
|
|
41
|
+
tmp_log = logger_utils.make_logger(base_logger, token=self.get_pid(), method_name="process")
|
|
42
|
+
# start
|
|
43
|
+
tmp_log.info("start")
|
|
44
|
+
tmp_log.debug(f"sub_id={msg_obj.sub_id} ; msg_id={msg_obj.msg_id}")
|
|
45
|
+
# parse json
|
|
46
|
+
try:
|
|
47
|
+
msg_dict = json.loads(msg_obj.data)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
err_str = f"failed to parse message json {msg_obj.data} , skipped. {e.__class__.__name__} : {e}"
|
|
50
|
+
tmp_log.error(err_str)
|
|
51
|
+
raise
|
|
52
|
+
# sanity check
|
|
53
|
+
try:
|
|
54
|
+
msg_type = msg_dict["msg_type"]
|
|
55
|
+
except Exception as e:
|
|
56
|
+
err_str = f"failed to parse message object dict {msg_dict} , skipped. {e.__class__.__name__} : {e}"
|
|
57
|
+
tmp_log.error(err_str)
|
|
58
|
+
raise
|
|
59
|
+
if msg_type != "jedi_post_processor":
|
|
60
|
+
# FIXME
|
|
61
|
+
err_str = f"got unknown msg_type {msg_type} , skipped "
|
|
62
|
+
tmp_log.error(err_str)
|
|
63
|
+
raise
|
|
64
|
+
# run
|
|
65
|
+
try:
|
|
66
|
+
task_id = msg_dict["taskid"]
|
|
67
|
+
vo = msg_dict["task_vo"]
|
|
68
|
+
prodsourcelabel = msg_dict["task_prodsourcelabel"]
|
|
69
|
+
ret_list = self.tbIF.prepareTasksToBeFinished_JEDI(vo, prodSourceLabel, jedi_config.postprocessor.nTasks, self.get_pid())
|
|
70
|
+
task_list = self.tbIF.getTasksToBeFinished_JEDI(vo, prodSourceLabel, self.get_pid(), jedi_config.postprocessor.nTasks, target_tasks=ret_list)
|
|
71
|
+
if task_list and task_id in [task_spec.jediTaskID for task_spec in task_list]:
|
|
72
|
+
tmp_post_processor_thread_obj = self.post_processor_thread_dict[(vo, prodsourcelabel)]
|
|
73
|
+
tmp_post_processor_thread_obj.post_process_tasks(task_list)
|
|
74
|
+
tmp_log.info(f"post processed tasks {task_list} including {task_id}")
|
|
75
|
+
else:
|
|
76
|
+
tmp_log.debug(f"did not get task {task_id}; skip ")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
err_str = f"failed to run, skipped. {e.__class__.__name__} : {e}"
|
|
79
|
+
tmp_log.error(err_str)
|
|
80
|
+
raise
|
|
81
|
+
# done
|
|
82
|
+
tmp_log.info("done")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from pandacommon.kafkapublisher.KafkaPublisher import KafkaPublisher
|
|
4
|
+
from pandacommon.pandalogger import logger_utils
|
|
5
|
+
|
|
6
|
+
from pandajedi.jedimsgprocessor.base_msg_processor import BaseMsgProcPlugin
|
|
7
|
+
|
|
8
|
+
base_logger = logger_utils.setup_logger(__name__.split(".")[-1])
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Kafka message processing plugin
|
|
12
|
+
class KafkaMsgProcPlugin(BaseMsgProcPlugin):
|
|
13
|
+
def initialize(self):
|
|
14
|
+
"""
|
|
15
|
+
initialize plugin instance, run once before loop in thread
|
|
16
|
+
"""
|
|
17
|
+
self.publisher = KafkaPublisher()
|
|
18
|
+
|
|
19
|
+
def process(self, msg_obj, decoded_data=None):
|
|
20
|
+
tmp_log = logger_utils.make_logger(base_logger, token=self.get_pid(), method_name="process")
|
|
21
|
+
|
|
22
|
+
# start
|
|
23
|
+
tmp_log.info("start")
|
|
24
|
+
tmp_log.debug(f"sub_id={msg_obj.sub_id} ; msg_id={msg_obj.msg_id}")
|
|
25
|
+
|
|
26
|
+
# Parse and access the message content from msg_obj.data
|
|
27
|
+
if decoded_data is None:
|
|
28
|
+
# json decode
|
|
29
|
+
try:
|
|
30
|
+
message_content = json.loads(msg_obj.data)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
err_str = f"failed to parse message json {msg_obj.data} , skipped. {e.__class__.__name__} : {e}"
|
|
33
|
+
tmp_log.error(err_str)
|
|
34
|
+
raise
|
|
35
|
+
else:
|
|
36
|
+
message_content = decoded_data
|
|
37
|
+
|
|
38
|
+
# Publish the message to Kafka
|
|
39
|
+
self.publisher.publish_message(message_content)
|
|
40
|
+
tmp_log.debug(f"sent {message_content}")
|
|
41
|
+
tmp_log.info("done")
|
|
42
|
+
|
|
43
|
+
def terminate(self):
|
|
44
|
+
self.publisher.close()
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
from pandacommon.pandalogger import LogWrapper, logger_utils
|
|
6
|
+
|
|
7
|
+
from pandajedi.jedimsgprocessor.base_msg_processor import BaseMsgProcPlugin
|
|
8
|
+
from pandaserver.dataservice.ddm_handler import DDMHandler
|
|
9
|
+
|
|
10
|
+
base_logger = logger_utils.setup_logger(__name__.split(".")[-1])
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# panda dataset callback message processing plugin
|
|
14
|
+
class PandaCallbackMsgProcPlugin(BaseMsgProcPlugin):
|
|
15
|
+
|
|
16
|
+
def __init__(self, **params):
|
|
17
|
+
super().__init__(**params)
|
|
18
|
+
self.activities_with_file_callback = []
|
|
19
|
+
self.component_action_map = []
|
|
20
|
+
self.site_mapper = None
|
|
21
|
+
self.verbose = False
|
|
22
|
+
|
|
23
|
+
def initialize(self, **params):
|
|
24
|
+
BaseMsgProcPlugin.initialize(self, **params)
|
|
25
|
+
# activity list to use file callback
|
|
26
|
+
self.activities_with_file_callback = self.params.get("activities_with_file_callback", [])
|
|
27
|
+
# component action map
|
|
28
|
+
# [{"event_type": "<event_type>", "component": "<component_name>", "criteria": {"key": <value>,}, "to_id": "<how_to_get_task_id>}, ...]
|
|
29
|
+
self.component_action_map = self.params.get("component_action_map", [])
|
|
30
|
+
# site mapper
|
|
31
|
+
self.site_mapper = self.tbIF.get_site_mapper()
|
|
32
|
+
# verbose logging
|
|
33
|
+
self.verbose = self.params.get("verbose", False)
|
|
34
|
+
|
|
35
|
+
def process(self, msg_obj):
|
|
36
|
+
tmp_log = logger_utils.make_logger(base_logger, token=self.get_pid(), method_name="process")
|
|
37
|
+
# start
|
|
38
|
+
# tmp_log.info('start')
|
|
39
|
+
# tmp_log.debug('sub_id={0} ; msg_id={1}'.format(msg_obj.sub_id, msg_obj.msg_id))
|
|
40
|
+
# parse yaml
|
|
41
|
+
try:
|
|
42
|
+
message_dict = yaml.safe_load(msg_obj.data)
|
|
43
|
+
except Exception as e:
|
|
44
|
+
err_str = f"failed to parse message yaml {msg_obj.data} , skipped. {e.__class__.__name__} : {e}"
|
|
45
|
+
tmp_log.error(err_str)
|
|
46
|
+
raise
|
|
47
|
+
# run
|
|
48
|
+
try:
|
|
49
|
+
to_continue = True
|
|
50
|
+
dsn = "UNKNOWN"
|
|
51
|
+
# check event type
|
|
52
|
+
if not isinstance(message_dict, dict):
|
|
53
|
+
err_str = f"skip due to invalid message format:{type(message_dict).__name__}. msg:{str(message_dict)}"
|
|
54
|
+
tmp_log.warning(err_str)
|
|
55
|
+
return
|
|
56
|
+
event_type = message_dict["event_type"]
|
|
57
|
+
message_ids = f"sub_id={msg_obj.sub_id} ; msg_id={msg_obj.msg_id}"
|
|
58
|
+
if event_type in ["datasetlock_ok"]:
|
|
59
|
+
self.process_dataset_callback(event_type, message_ids, message_dict, tmp_log)
|
|
60
|
+
elif self.activities_with_file_callback and event_type in ["transfer-done"]:
|
|
61
|
+
self.process_file_callback(event_type, message_ids, message_dict, tmp_log)
|
|
62
|
+
else:
|
|
63
|
+
if self.verbose:
|
|
64
|
+
tmp_log.debug(f"skip event_type={event_type}")
|
|
65
|
+
# trigger component actions
|
|
66
|
+
if self.component_action_map:
|
|
67
|
+
self.trigger_component_action(event_type, message_ids, message_dict, tmp_log)
|
|
68
|
+
# end
|
|
69
|
+
except Exception as e:
|
|
70
|
+
err_str = f"failed to run, skipped. {e.__class__.__name__} : {e}\n{traceback.format_exc()}"
|
|
71
|
+
tmp_log.error(err_str)
|
|
72
|
+
raise
|
|
73
|
+
|
|
74
|
+
def process_dataset_callback(self, event_type: str, message_ids: str, message_dict: dict, tmp_log: LogWrapper.LogWrapper) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Process a dataset callback
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
event_type: message event type
|
|
80
|
+
message_ids: subscription and message IDs
|
|
81
|
+
message_dict: message dictionary
|
|
82
|
+
tmp_log: logger instance
|
|
83
|
+
"""
|
|
84
|
+
message_payload = message_dict["payload"]
|
|
85
|
+
# only for _dis or _sub
|
|
86
|
+
dsn = message_payload["name"]
|
|
87
|
+
if (re.search(r"_dis\d+$", dsn) is None) and (re.search(r"_sub\d+$", dsn) is None):
|
|
88
|
+
return
|
|
89
|
+
tmp_log.debug(message_ids)
|
|
90
|
+
tmp_log.debug(f"{event_type} start")
|
|
91
|
+
# take action
|
|
92
|
+
scope = message_payload["scope"]
|
|
93
|
+
site = message_payload["rse"]
|
|
94
|
+
tmp_log.debug(f"{dsn} site={site} type={event_type}")
|
|
95
|
+
thr = DDMHandler(task_buffer=self.tbIF, vuid=None, site=site, dataset=dsn, scope=scope)
|
|
96
|
+
# just call run rather than start+join, to run it in main thread instead of spawning new thread
|
|
97
|
+
thr.run()
|
|
98
|
+
del thr
|
|
99
|
+
tmp_log.debug(f"done {dsn}")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
def process_file_callback(self, event_type: str, message_ids: str, message_dict: dict, tmp_log: LogWrapper.LogWrapper) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Process a file callback
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
event_type: message event type
|
|
108
|
+
message_ids: subscription and message IDs
|
|
109
|
+
message_dict: message dictionary
|
|
110
|
+
tmp_log: logger instance
|
|
111
|
+
"""
|
|
112
|
+
message_payload = message_dict["payload"]
|
|
113
|
+
# only for activities with file callback
|
|
114
|
+
activity = message_payload["activity"]
|
|
115
|
+
if activity not in self.activities_with_file_callback:
|
|
116
|
+
return
|
|
117
|
+
# update file status and get corresponding PandaIDs
|
|
118
|
+
filename = message_payload["name"]
|
|
119
|
+
endpoint = message_payload["dst-rse"]
|
|
120
|
+
tmp_log.debug(message_ids)
|
|
121
|
+
tmp_log.debug(f"{event_type} start for lfn={filename} activity={activity}")
|
|
122
|
+
sites = self.site_mapper.get_sites_for_endpoint(endpoint, "input")
|
|
123
|
+
panda_ids = self.tbIF.update_input_files_at_sites_and_get_panda_ids(filename, sites)
|
|
124
|
+
jobs = self.tbIF.peekJobs(panda_ids, fromActive=False, fromArchived=False, fromWaiting=False)
|
|
125
|
+
# activate jobs
|
|
126
|
+
self.tbIF.activateJobs(jobs)
|
|
127
|
+
tmp_log.debug(f"done")
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
def trigger_component_action(self, event_type: str, message_ids: str, message_dict: dict, tmp_log: LogWrapper.LogWrapper) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Trigger component action based on the event type
|
|
133
|
+
Args:
|
|
134
|
+
event_type: message event type
|
|
135
|
+
message_ids: subscription and message IDs
|
|
136
|
+
message_dict: message dictionary
|
|
137
|
+
tmp_log: logger instance
|
|
138
|
+
"""
|
|
139
|
+
message_payload = message_dict["payload"]
|
|
140
|
+
for action_item in self.component_action_map:
|
|
141
|
+
# check event type
|
|
142
|
+
if action_item["event_type"] != event_type:
|
|
143
|
+
continue
|
|
144
|
+
# check criteria
|
|
145
|
+
criteria_matched = True
|
|
146
|
+
for key, value in action_item.get("criteria", {}).items():
|
|
147
|
+
dict_value = message_payload.get(key)
|
|
148
|
+
if dict_value is None:
|
|
149
|
+
criteria_matched = False
|
|
150
|
+
break
|
|
151
|
+
if dict_value != value and not re.match(value, str(dict_value)):
|
|
152
|
+
criteria_matched = False
|
|
153
|
+
break
|
|
154
|
+
if not criteria_matched:
|
|
155
|
+
continue
|
|
156
|
+
component_name = action_item["component"]
|
|
157
|
+
to_id = action_item["to_id"]
|
|
158
|
+
# extract task ID based on to_id
|
|
159
|
+
jedi_task_ids = None
|
|
160
|
+
if to_id == "from_input_dataset":
|
|
161
|
+
dataset_name = message_payload["name"].split(":")[-1]
|
|
162
|
+
jedi_task_ids = self.tbIF.get_task_ids_with_dataset_attributes({"datasetName": dataset_name, "type": "input"})
|
|
163
|
+
else:
|
|
164
|
+
tmp_log.warning(f"unknown to_id={to_id} for action_item={action_item} ; skipped")
|
|
165
|
+
continue
|
|
166
|
+
if jedi_task_ids is None:
|
|
167
|
+
tmp_log.warning(f"failed to extract jediTaskID for action_item={action_item} ; skipped")
|
|
168
|
+
continue
|
|
169
|
+
if not jedi_task_ids:
|
|
170
|
+
tmp_log.debug(f"no jediTaskID found for action_item={action_item} ; skipped")
|
|
171
|
+
continue
|
|
172
|
+
# loop over task IDs
|
|
173
|
+
for jedi_task_id in jedi_task_ids:
|
|
174
|
+
# release task just in case
|
|
175
|
+
self.tbIF.release_task_on_hold(jedi_task_id)
|
|
176
|
+
# push trigger message
|
|
177
|
+
push_ret = self.tbIF.push_task_trigger_message(component_name, jedi_task_ids)
|
|
178
|
+
if push_ret:
|
|
179
|
+
tmp_log.debug(f"pushed trigger message to {component_name} for jediTaskID={jedi_task_ids}")
|
|
180
|
+
else:
|
|
181
|
+
tmp_log.warning(f"failed to push trigger to {component_name} for jediTaskID={jedi_task_ids}")
|
|
182
|
+
return
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import socket
|
|
4
|
+
|
|
5
|
+
from pandacommon.pandalogger import logger_utils
|
|
6
|
+
|
|
7
|
+
from pandajedi.jedicore.ThreadUtils import ListWithLock, ThreadPool
|
|
8
|
+
from pandajedi.jediddm.DDMInterface import DDMInterface
|
|
9
|
+
from pandajedi.jedimsgprocessor.base_msg_processor import BaseMsgProcPlugin
|
|
10
|
+
from pandajedi.jediorder.JobGenerator import JobGeneratorThread
|
|
11
|
+
from pandajedi.jediorder.TaskSetupper import TaskSetupper
|
|
12
|
+
|
|
13
|
+
base_logger = logger_utils.setup_logger(__name__.split(".")[-1])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# plugin to process messages from Panda to JEDI
|
|
17
|
+
class PandaToJediMsgProcPlugin(BaseMsgProcPlugin):
|
|
18
|
+
def initialize(self):
|
|
19
|
+
BaseMsgProcPlugin.initialize(self)
|
|
20
|
+
self.ddmIF = DDMInterface()
|
|
21
|
+
self.ddmIF.setupInterface()
|
|
22
|
+
self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}_{os.getpgrp()}-pjmsg"
|
|
23
|
+
|
|
24
|
+
def process(self, msg_obj, decoded_data=None):
|
|
25
|
+
tmp_log = logger_utils.make_logger(base_logger, token=self.get_pid(), method_name="process")
|
|
26
|
+
# start
|
|
27
|
+
tmp_log.info("start")
|
|
28
|
+
# parse
|
|
29
|
+
if decoded_data is None:
|
|
30
|
+
# json decode
|
|
31
|
+
try:
|
|
32
|
+
msg_dict = json.loads(msg_obj.data)
|
|
33
|
+
except Exception as e:
|
|
34
|
+
err_str = f"failed to parse message json {msg_obj.data} , skipped. {e.__class__.__name__} : {e}"
|
|
35
|
+
tmp_log.error(err_str)
|
|
36
|
+
raise
|
|
37
|
+
else:
|
|
38
|
+
msg_dict = decoded_data
|
|
39
|
+
# run
|
|
40
|
+
try:
|
|
41
|
+
tmp_log.debug(f"got message {msg_dict}")
|
|
42
|
+
if msg_dict["msg_type"] == "generate_job":
|
|
43
|
+
# get task to generate jobs
|
|
44
|
+
jediTaskID = int(msg_dict["taskid"])
|
|
45
|
+
s, taskSpec = self.tbIF.getTaskWithID_JEDI(jediTaskID)
|
|
46
|
+
if not taskSpec:
|
|
47
|
+
tmp_log.debug(f"unknown task {jediTaskID}")
|
|
48
|
+
else:
|
|
49
|
+
# get WQ
|
|
50
|
+
vo = taskSpec.vo
|
|
51
|
+
prodSourceLabel = taskSpec.prodSourceLabel
|
|
52
|
+
workQueue = self.tbIF.getWorkQueueMap().getQueueWithIDGshare(taskSpec.workQueue_ID, taskSpec.gshare)
|
|
53
|
+
# get inputs
|
|
54
|
+
tmpList = self.tbIF.getTasksToBeProcessed_JEDI(self.pid, None, workQueue, None, None, nFiles=1000, target_tasks=[jediTaskID])
|
|
55
|
+
if tmpList:
|
|
56
|
+
inputList = ListWithLock(tmpList)
|
|
57
|
+
# create thread
|
|
58
|
+
threadPool = ThreadPool()
|
|
59
|
+
siteMapper = self.tbIF.get_site_mapper()
|
|
60
|
+
taskSetupper = TaskSetupper(vo, prodSourceLabel)
|
|
61
|
+
taskSetupper.initializeMods(self.tbIF, self.ddmIF)
|
|
62
|
+
gen = JobGeneratorThread(
|
|
63
|
+
inputList, threadPool, self.tbIF, self.ddmIF, siteMapper, True, taskSetupper, self.pid, workQueue, "pjmsg", None, None, None, False
|
|
64
|
+
)
|
|
65
|
+
gen.start()
|
|
66
|
+
gen.join()
|
|
67
|
+
else:
|
|
68
|
+
tmp_log.debug(f"unknown message type : {msg_dict['msg_type']}")
|
|
69
|
+
except Exception as e:
|
|
70
|
+
err_str = f"failed to run, skipped. {e.__class__.__name__} : {e}"
|
|
71
|
+
tmp_log.error(err_str)
|
|
72
|
+
raise
|
|
73
|
+
# done
|
|
74
|
+
tmp_log.info("done")
|