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,339 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import inspect
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
import typing
|
|
8
|
+
from functools import wraps
|
|
9
|
+
from types import ModuleType, UnionType
|
|
10
|
+
from typing import Union, get_args, get_origin
|
|
11
|
+
|
|
12
|
+
from pandacommon.pandalogger.LogWrapper import LogWrapper
|
|
13
|
+
|
|
14
|
+
import pandaserver.jobdispatcher.Protocol as Protocol
|
|
15
|
+
from pandaserver.config import panda_config
|
|
16
|
+
from pandaserver.dataservice.ddm import rucioAPI
|
|
17
|
+
from pandaserver.srvcore import CoreUtils
|
|
18
|
+
|
|
19
|
+
TIME_OUT = "TimeOut"
|
|
20
|
+
|
|
21
|
+
MESSAGE_SSL = "SSL secure connection is required"
|
|
22
|
+
MESSAGE_PROD_ROLE = "production or pilot role required"
|
|
23
|
+
MESSAGE_TASK_ID = "jediTaskID must be an integer"
|
|
24
|
+
MESSAGE_DATABASE = "database error in the PanDA server"
|
|
25
|
+
MESSAGE_JSON = "failed to load JSON"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_endpoint(protocol):
|
|
29
|
+
if protocol not in ["http", "https"]:
|
|
30
|
+
return False, "Protocol must be either 'http' or 'https'"
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
if protocol == "https":
|
|
34
|
+
endpoint = f"{panda_config.pserverhost}:{panda_config.pserverport}"
|
|
35
|
+
else:
|
|
36
|
+
endpoint = f"{panda_config.pserverhosthttp}:{panda_config.pserverporthttp}"
|
|
37
|
+
except Exception as e:
|
|
38
|
+
return False, str(e)
|
|
39
|
+
|
|
40
|
+
return True, endpoint
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def extract_allowed_methods(module: ModuleType) -> list:
|
|
44
|
+
"""
|
|
45
|
+
Generate the allowed methods dynamically with all function names present in the API module, excluding
|
|
46
|
+
functions imported from other modules or the init_task_buffer function
|
|
47
|
+
|
|
48
|
+
:param module: The module to extract the allowed methods from
|
|
49
|
+
:return: A list of allowed method names
|
|
50
|
+
"""
|
|
51
|
+
return [
|
|
52
|
+
name
|
|
53
|
+
for name, obj in inspect.getmembers(module, inspect.isfunction)
|
|
54
|
+
if obj.__module__ == module.__name__ and name != "init_task_buffer" and name.startswith("_") is False
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def generate_response(success, message="", data=None):
|
|
59
|
+
response = {"success": success, "message": message, "data": data}
|
|
60
|
+
return response
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# get FQANs
|
|
64
|
+
def get_fqan(req):
|
|
65
|
+
fqans = []
|
|
66
|
+
for tmp_key in req.subprocess_env:
|
|
67
|
+
tmp_value = req.subprocess_env[tmp_key]
|
|
68
|
+
# Scan VOMS attributes
|
|
69
|
+
# compact style
|
|
70
|
+
if tmp_key.startswith("GRST_CRED_") and tmp_value.startswith("VOMS"):
|
|
71
|
+
fqan = tmp_value.split()[-1]
|
|
72
|
+
fqans.append(fqan)
|
|
73
|
+
|
|
74
|
+
# old style
|
|
75
|
+
elif tmp_key.startswith("GRST_CONN_"):
|
|
76
|
+
tmp_items = tmp_value.split(":")
|
|
77
|
+
if len(tmp_items) == 2 and tmp_items[0] == "fqan":
|
|
78
|
+
fqans.append(tmp_items[-1])
|
|
79
|
+
|
|
80
|
+
return fqans
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_email_address(user, tmp_logger):
|
|
84
|
+
tmp_logger.debug(f"Getting mail address for {user}")
|
|
85
|
+
n_tries = 3
|
|
86
|
+
email = None
|
|
87
|
+
try:
|
|
88
|
+
for attempt in range(n_tries):
|
|
89
|
+
status, user_info = rucioAPI.finger(user)
|
|
90
|
+
if status:
|
|
91
|
+
email = user_info["email"]
|
|
92
|
+
tmp_logger.debug(f"User {user} got email {email}")
|
|
93
|
+
break
|
|
94
|
+
else:
|
|
95
|
+
tmp_logger.debug(f"Attempt {attempt + 1} of {n_tries} failed. Retrying...")
|
|
96
|
+
time.sleep(1)
|
|
97
|
+
except Exception:
|
|
98
|
+
error_type, error_value = sys.exc_info()[:2]
|
|
99
|
+
tmp_logger.error(f"Failed to convert email address {user} : {error_type} {error_value}")
|
|
100
|
+
|
|
101
|
+
return email
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_request_method(req):
|
|
105
|
+
# Extract the http method like GET, POST, ... from the request environment
|
|
106
|
+
environ = req.subprocess_env
|
|
107
|
+
request_method = environ.get("REQUEST_METHOD", None) # GET, POST, PUT, DELETE
|
|
108
|
+
return request_method
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# get DN
|
|
112
|
+
def get_dn(req):
|
|
113
|
+
real_dn = ""
|
|
114
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
115
|
+
# remove redundant CN
|
|
116
|
+
real_dn = CoreUtils.get_bare_dn(req.subprocess_env["SSL_CLIENT_S_DN"], keep_proxy=True)
|
|
117
|
+
return real_dn
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# check role
|
|
121
|
+
def has_production_role(req):
|
|
122
|
+
# check DN
|
|
123
|
+
user = get_dn(req)
|
|
124
|
+
for sdn in panda_config.production_dns:
|
|
125
|
+
if sdn in user:
|
|
126
|
+
return True
|
|
127
|
+
# get FQANs
|
|
128
|
+
fqans = get_fqan(req)
|
|
129
|
+
# loop over all FQANs
|
|
130
|
+
for fqan in fqans:
|
|
131
|
+
# check production role
|
|
132
|
+
for rolePat in [
|
|
133
|
+
"/atlas/usatlas/Role=production",
|
|
134
|
+
"/atlas/Role=production",
|
|
135
|
+
"^/[^/]+/Role=production",
|
|
136
|
+
]:
|
|
137
|
+
if fqan.startswith(rolePat):
|
|
138
|
+
return True
|
|
139
|
+
if re.search(rolePat, fqan):
|
|
140
|
+
return True
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def extract_production_working_groups(fqans):
|
|
145
|
+
# Extract working groups with production role from FQANs
|
|
146
|
+
wg_prod_roles = []
|
|
147
|
+
for fqan in fqans:
|
|
148
|
+
# Match FQANs with 'Role=production' and extract the working group
|
|
149
|
+
match = re.search(r"/atlas/([^/]+)/Role=production", fqan)
|
|
150
|
+
if match:
|
|
151
|
+
working_group = match.group(1)
|
|
152
|
+
# Exclude 'usatlas' and ensure uniqueness
|
|
153
|
+
if working_group and working_group not in ["usatlas"] + wg_prod_roles:
|
|
154
|
+
wg_prod_roles.extend([working_group, f"gr_{working_group}"]) # Add group and prefixed variant
|
|
155
|
+
|
|
156
|
+
return wg_prod_roles
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def extract_primary_production_working_group(fqans):
|
|
160
|
+
working_group = None
|
|
161
|
+
for fqan in fqans:
|
|
162
|
+
match = re.search("/[^/]+/([^/]+)/Role=production", fqan)
|
|
163
|
+
if match:
|
|
164
|
+
# ignore usatlas since it is used as atlas prod role
|
|
165
|
+
tmp_working_group = match.group(1)
|
|
166
|
+
if tmp_working_group not in ["", "usatlas"]:
|
|
167
|
+
working_group = tmp_working_group.split("-")[-1].lower()
|
|
168
|
+
|
|
169
|
+
return working_group
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# security check
|
|
173
|
+
def is_secure(req, logger=None):
|
|
174
|
+
# check security
|
|
175
|
+
if not Protocol.isSecure(req):
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
# disable limited proxy
|
|
179
|
+
if "/CN=limited proxy" in req.subprocess_env["SSL_CLIENT_S_DN"]:
|
|
180
|
+
if logger:
|
|
181
|
+
logger.warning(f"access via limited proxy : {req.subprocess_env['SSL_CLIENT_S_DN']}")
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def normalize_type(t):
|
|
188
|
+
mapping = {
|
|
189
|
+
typing.List: list,
|
|
190
|
+
typing.Dict: dict,
|
|
191
|
+
typing.Set: set,
|
|
192
|
+
typing.Tuple: tuple,
|
|
193
|
+
}
|
|
194
|
+
return mapping.get(t, t)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def request_validation(logger, secure=True, production=False, request_method=None):
|
|
198
|
+
def decorator(func):
|
|
199
|
+
@wraps(func)
|
|
200
|
+
def wrapper(req, *args, **kwargs):
|
|
201
|
+
# Generate a logger with the underlying function name
|
|
202
|
+
tmp_logger = LogWrapper(logger, func.__name__)
|
|
203
|
+
tmp_logger_context = LogWrapper(logger, f"{func.__name__} args:{args} kwargs:{kwargs}")
|
|
204
|
+
|
|
205
|
+
# expected and received request methods
|
|
206
|
+
expected_request_method = request_method
|
|
207
|
+
received_request_method = get_request_method(req)
|
|
208
|
+
|
|
209
|
+
# check SSL if required
|
|
210
|
+
if secure and not is_secure(req, tmp_logger):
|
|
211
|
+
tmp_logger.error(f"{MESSAGE_SSL}")
|
|
212
|
+
return generate_response(False, message=MESSAGE_SSL)
|
|
213
|
+
|
|
214
|
+
# check production role if required
|
|
215
|
+
if production and not has_production_role(req):
|
|
216
|
+
tmp_logger.error(f"{MESSAGE_PROD_ROLE}")
|
|
217
|
+
return generate_response(False, message=MESSAGE_PROD_ROLE)
|
|
218
|
+
|
|
219
|
+
# check method if required
|
|
220
|
+
if expected_request_method and expected_request_method != received_request_method:
|
|
221
|
+
message = f"expecting {expected_request_method}, received {req.subprocess_env.get('REQUEST_METHOD', None)}"
|
|
222
|
+
tmp_logger.error(f"{message}")
|
|
223
|
+
return generate_response(False, message=message)
|
|
224
|
+
|
|
225
|
+
# Get function signature and type hints
|
|
226
|
+
sig = inspect.signature(func)
|
|
227
|
+
args_tmp = (req,) + args
|
|
228
|
+
try:
|
|
229
|
+
bound_args = sig.bind(*args_tmp, **kwargs)
|
|
230
|
+
except TypeError as e:
|
|
231
|
+
message = f"Argument error: {str(e)}"
|
|
232
|
+
tmp_logger_context.error(message)
|
|
233
|
+
return generate_response(False, message=message)
|
|
234
|
+
bound_args.apply_defaults()
|
|
235
|
+
|
|
236
|
+
for param_name, param_value in bound_args.arguments.items():
|
|
237
|
+
# tmp_logger.debug(f"Got parameter '{param_name}' with value '{param_value}' and type '{type(param_value)}'")
|
|
238
|
+
|
|
239
|
+
# Skip the first argument (req)
|
|
240
|
+
if param_name == "req":
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
# Skip if no type hint
|
|
244
|
+
expected_type = sig.parameters[param_name].annotation
|
|
245
|
+
if expected_type is inspect.Parameter.empty:
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
# Skip if value is the default value
|
|
249
|
+
default_value = sig.parameters[param_name].default
|
|
250
|
+
if default_value == param_value:
|
|
251
|
+
continue
|
|
252
|
+
|
|
253
|
+
# Handle generics like List[int]
|
|
254
|
+
origin = get_origin(expected_type)
|
|
255
|
+
args = get_args(expected_type)
|
|
256
|
+
|
|
257
|
+
# GET methods are URL encoded. Parameters will lose the type and come as string. We need to cast them to the expected type
|
|
258
|
+
if received_request_method == "GET":
|
|
259
|
+
try:
|
|
260
|
+
tmp_logger.debug(f"Casting '{param_name}' to type {expected_type.__name__}.")
|
|
261
|
+
tmp_logger.debug(type(param_value))
|
|
262
|
+
if param_value == "None" and default_value is None:
|
|
263
|
+
param_value = None
|
|
264
|
+
# Don't cast if the type is already a string
|
|
265
|
+
elif expected_type is str:
|
|
266
|
+
pass
|
|
267
|
+
# Booleans need to be handled separately, since bool("False") == True
|
|
268
|
+
elif expected_type is bool:
|
|
269
|
+
param_value = param_value.lower() in ("true", "1")
|
|
270
|
+
# Convert to float first, then to int. This is a courtesy for cases passing decimal numbers.
|
|
271
|
+
elif expected_type is int:
|
|
272
|
+
param_value = int(float(param_value))
|
|
273
|
+
elif origin is list and args:
|
|
274
|
+
element_type = args[0] # Get the type inside List[<type>]
|
|
275
|
+
|
|
276
|
+
# If only one element, convert it to a list
|
|
277
|
+
if isinstance(param_value, str):
|
|
278
|
+
param_value = [param_value]
|
|
279
|
+
|
|
280
|
+
# Convert the elements of the list to the expected type
|
|
281
|
+
if element_type is int:
|
|
282
|
+
param_value = [int(float(i)) for i in param_value] # Convert list items to int
|
|
283
|
+
elif element_type is float:
|
|
284
|
+
param_value = [float(i) for i in param_value] # Convert list items to float
|
|
285
|
+
elif element_type is bool:
|
|
286
|
+
param_value = [i.lower() in ("true", "1") for i in param_value] # Convert list items to bool
|
|
287
|
+
else:
|
|
288
|
+
# Normalize type, e.g. typing.Dict -> dict
|
|
289
|
+
expected_type = normalize_type(expected_type)
|
|
290
|
+
if not isinstance(param_value, expected_type):
|
|
291
|
+
param_value = ast.literal_eval(param_value)
|
|
292
|
+
if not isinstance(param_value, expected_type):
|
|
293
|
+
raise TypeError(f"Expected {expected_type}, received {type(param_value)}")
|
|
294
|
+
bound_args.arguments[param_name] = param_value # Ensure the cast value is used
|
|
295
|
+
except (ValueError, TypeError):
|
|
296
|
+
message = f"Type error: '{param_name}' with value '{param_value}' could not be casted to type {expected_type.__name__} from {type(param_value).__name__}."
|
|
297
|
+
tmp_logger_context.error(message)
|
|
298
|
+
return generate_response(False, message=message)
|
|
299
|
+
|
|
300
|
+
# Check type
|
|
301
|
+
if origin and (origin is not Union and origin is not UnionType): # Handle generics (e.g., List[int])
|
|
302
|
+
if not isinstance(param_value, origin):
|
|
303
|
+
message = f"Type error: '{param_name}' must be of type {origin.__name__}, got {type(param_value).__name__}."
|
|
304
|
+
tmp_logger_context.error(message)
|
|
305
|
+
return generate_response(False, message=message)
|
|
306
|
+
|
|
307
|
+
if args: # Check inner types for lists, dicts, etc.
|
|
308
|
+
if origin is list and not all(isinstance(i, args[0]) for i in param_value):
|
|
309
|
+
message = f"Type error: All elements in '{param_name}' must be {args[0].__name__}."
|
|
310
|
+
tmp_logger_context.error(message)
|
|
311
|
+
return generate_response(False, message=message)
|
|
312
|
+
elif not isinstance(param_value, expected_type) and not (param_value is None and param_value == default_value):
|
|
313
|
+
message = f"Type error: '{param_name}' must be of type {expected_type.__name__}, got {type(param_value).__name__}."
|
|
314
|
+
tmp_logger_context.error(message)
|
|
315
|
+
return generate_response(False, message=message)
|
|
316
|
+
|
|
317
|
+
return func(*bound_args.args, **bound_args.kwargs)
|
|
318
|
+
|
|
319
|
+
return wrapper
|
|
320
|
+
|
|
321
|
+
return decorator
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# a wrapper to install timeout into a method
|
|
325
|
+
class TimedMethod:
|
|
326
|
+
def __init__(self, method, timeout):
|
|
327
|
+
self.method = method
|
|
328
|
+
self.timeout = timeout
|
|
329
|
+
self.result = TIME_OUT
|
|
330
|
+
|
|
331
|
+
# method emulation
|
|
332
|
+
def __call__(self, *var, **kwargs):
|
|
333
|
+
self.result = self.method(*var, **kwargs)
|
|
334
|
+
|
|
335
|
+
# run
|
|
336
|
+
def run(self, *var, **kwargs):
|
|
337
|
+
thr = threading.Thread(target=self, args=var, kwargs=kwargs)
|
|
338
|
+
thr.start()
|
|
339
|
+
thr.join()
|