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,895 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import traceback
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
from pandacommon.pandalogger.LogWrapper import LogWrapper
|
|
9
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
10
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
11
|
+
|
|
12
|
+
from pandaserver.api.v1.common import (
|
|
13
|
+
TimedMethod,
|
|
14
|
+
generate_response,
|
|
15
|
+
get_dn,
|
|
16
|
+
has_production_role,
|
|
17
|
+
request_validation,
|
|
18
|
+
)
|
|
19
|
+
from pandaserver.brokerage.SiteMapper import SiteMapper
|
|
20
|
+
from pandaserver.config import panda_config
|
|
21
|
+
from pandaserver.dataservice.adder_gen import AdderGen
|
|
22
|
+
from pandaserver.jobdispatcher import Protocol
|
|
23
|
+
from pandaserver.srvcore import CoreUtils
|
|
24
|
+
from pandaserver.srvcore.CoreUtils import normalize_cpu_model
|
|
25
|
+
from pandaserver.srvcore.panda_request import PandaRequest
|
|
26
|
+
from pandaserver.taskbuffer.TaskBuffer import TaskBuffer
|
|
27
|
+
|
|
28
|
+
_logger = PandaLogger().getLogger("api_pilot")
|
|
29
|
+
pilot_logger = PandaLogger().getLogger("PilotRequests")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
global_task_buffer = None
|
|
33
|
+
global_site_mapper_cache = None
|
|
34
|
+
|
|
35
|
+
VALID_JOB_STATES = ["running", "failed", "finished", "holding", "starting", "transferring"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def init_task_buffer(task_buffer: TaskBuffer) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Initialize the task buffer. This method needs to be called before any other method in this module.
|
|
41
|
+
"""
|
|
42
|
+
global global_task_buffer
|
|
43
|
+
global_task_buffer = task_buffer
|
|
44
|
+
|
|
45
|
+
global global_site_mapper_cache
|
|
46
|
+
global_site_mapper_cache = CoreUtils.CachedObject("site_mapper", 60 * 10, _get_site_mapper, _logger)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _get_site_mapper():
|
|
50
|
+
return True, SiteMapper(global_task_buffer)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@request_validation(_logger, secure=True, request_method="POST")
|
|
54
|
+
def acquire_jobs(
|
|
55
|
+
req: PandaRequest,
|
|
56
|
+
site_name: str,
|
|
57
|
+
timeout: int = 60,
|
|
58
|
+
memory: int = None,
|
|
59
|
+
disk_space: int = None,
|
|
60
|
+
prod_source_label: str = None,
|
|
61
|
+
node: str = None,
|
|
62
|
+
computing_element: str = None,
|
|
63
|
+
prod_user_id: str = None,
|
|
64
|
+
get_proxy_key: str = None,
|
|
65
|
+
task_id: int = None,
|
|
66
|
+
n_jobs: int = 1,
|
|
67
|
+
background: bool = None,
|
|
68
|
+
resource_type: str = None,
|
|
69
|
+
harvester_id: str = None,
|
|
70
|
+
worker_id: int = None,
|
|
71
|
+
scheduler_id: str = None,
|
|
72
|
+
job_type: str = None,
|
|
73
|
+
via_topic: bool = None,
|
|
74
|
+
remaining_time=None,
|
|
75
|
+
) -> dict:
|
|
76
|
+
"""
|
|
77
|
+
Acquire jobs
|
|
78
|
+
|
|
79
|
+
Acquire jobs for the pilot. The jobs are reserved, the job status is updated and the jobs are returned. Requires a secure connection.
|
|
80
|
+
|
|
81
|
+
API details:
|
|
82
|
+
HTTP Method: POST
|
|
83
|
+
Path: /v1/pilot/acquire_jobs
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
req(PandaRequest): Internally generated request object containing the environment variables.
|
|
87
|
+
site_name(str): The PanDA queue name
|
|
88
|
+
timeout(int, optional): Request timeout in seconds. Optional and defaults to 60.
|
|
89
|
+
memory(int, optional): Memory limit for the job. Optional and defaults to `None`.
|
|
90
|
+
disk_space(int, optional): Disk space limit for the job. Optional and defaults to `None`.
|
|
91
|
+
prod_source_label(str, optional): Prodsourcelabel, e.g. `user`, `managed`, `unified`... Optional and defaults to `None`.
|
|
92
|
+
node(str, optional): Identifier of the worker node/slot. Optional and defaults to `None`.
|
|
93
|
+
computing_element(str, optional): Computing element. Optional and defaults to `None`.
|
|
94
|
+
prod_user_id(str, optional): User ID of the job. Optional and defaults to `None`.
|
|
95
|
+
get_proxy_key(bool, optional): Flag to request a proxy key.Optional and defaults to `None`.
|
|
96
|
+
task_id(int, optional): JEDI task ID of the job. Optional and defaults to `None`.
|
|
97
|
+
n_jobs(int, optional): Number of jobs for bulk requests. Optional and defaults to `None`.
|
|
98
|
+
background(bool, optional): Background flag. Optional and defaults to `None`.
|
|
99
|
+
resource_type(str, optional): Resource type of the job, e.g. `SCORE`, `MCORE`,.... Optional and defaults to `None`.
|
|
100
|
+
harvester_id(str, optional): Harvester ID, used to update the worker entry in the DB. Optional and defaults to `None`.
|
|
101
|
+
worker_id(int, optional): Worker ID, used to update the worker entry in the DB. Optional and defaults to `None`.
|
|
102
|
+
scheduler_id(str, optional): Scheduler, e.g. harvester ID. Optional and defaults to `None`.
|
|
103
|
+
job_type(str, optional): Job type, e.g. `user`, `unified`, ... This is necessary on top of the `prodsourcelabel`
|
|
104
|
+
to disambiguate the cases of test jobs that can be production or analysis. Optional and defaults to `None`.
|
|
105
|
+
via_topic(bool, optional): Topic for message broker. Optional and defaults to `None`.
|
|
106
|
+
remaining_time(int, optional): Remaining walltime. Optional and defaults to `None`.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
dict: The system response `{"success": success, "message": message, "data": data}`. The data is a list of job dictionaries.
|
|
110
|
+
When failed, the message contains the error message.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
tmp_logger = LogWrapper(_logger, f"acquire_jobs {naive_utcnow().isoformat('/')}")
|
|
114
|
+
tmp_logger.debug(f"Start for {site_name}")
|
|
115
|
+
|
|
116
|
+
# get DN and FQANs
|
|
117
|
+
real_dn = get_dn(req)
|
|
118
|
+
|
|
119
|
+
# check production role
|
|
120
|
+
is_production_manager = has_production_role(req)
|
|
121
|
+
|
|
122
|
+
# production jobs should only be retrieved with production role
|
|
123
|
+
if not is_production_manager and prod_source_label not in ["user"]:
|
|
124
|
+
tmp_logger.warning("invalid role")
|
|
125
|
+
tmp_msg = "no production/pilot role in VOMS FQANs or non pilot owner"
|
|
126
|
+
return generate_response(False, message=tmp_msg)
|
|
127
|
+
|
|
128
|
+
# set DN for non-production user, the user can only get their own jobs
|
|
129
|
+
if not is_production_manager:
|
|
130
|
+
prod_user_id = real_dn
|
|
131
|
+
|
|
132
|
+
# allow get_proxy_key for production role
|
|
133
|
+
if get_proxy_key and is_production_manager:
|
|
134
|
+
get_proxy_key = True
|
|
135
|
+
else:
|
|
136
|
+
get_proxy_key = False
|
|
137
|
+
|
|
138
|
+
# convert memory
|
|
139
|
+
try:
|
|
140
|
+
memory = max(0, memory)
|
|
141
|
+
except (ValueError, TypeError):
|
|
142
|
+
memory = 0
|
|
143
|
+
|
|
144
|
+
# convert disk_space
|
|
145
|
+
try:
|
|
146
|
+
disk_space = max(0, disk_space)
|
|
147
|
+
except (ValueError, TypeError):
|
|
148
|
+
disk_space = 0
|
|
149
|
+
|
|
150
|
+
# convert remaining time
|
|
151
|
+
try:
|
|
152
|
+
remaining_time = max(0, remaining_time)
|
|
153
|
+
except (ValueError, TypeError):
|
|
154
|
+
remaining_time = 0
|
|
155
|
+
|
|
156
|
+
tmp_logger.debug(
|
|
157
|
+
f"{site_name}, n_jobs={n_jobs}, memory={memory}, disk={disk_space}, source_label={prod_source_label}, "
|
|
158
|
+
f"node={node}, ce={computing_element}, user={prod_user_id}, proxy={get_proxy_key}, "
|
|
159
|
+
f"task_id={task_id}, DN={real_dn}, role={is_production_manager}, "
|
|
160
|
+
f"bg={background}, rt={resource_type}, harvester_id={harvester_id}, worker_id={worker_id}, "
|
|
161
|
+
f"scheduler_id={scheduler_id}, job_type={job_type}, via_topic={via_topic} remaining_time={remaining_time}"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# log the acquire_jobs as it's used for site activity metrics
|
|
165
|
+
for i in range(n_jobs):
|
|
166
|
+
slot_suffix = f"_{i}" if n_jobs > 1 else ""
|
|
167
|
+
pilot_logger.info(f"method=getJob,site={site_name},node={node}{slot_suffix},type={prod_source_label}")
|
|
168
|
+
|
|
169
|
+
t_start = time.time()
|
|
170
|
+
|
|
171
|
+
global_site_mapper_cache.update()
|
|
172
|
+
is_grandly_unified = global_site_mapper_cache.cachedObj.getSite(site_name).is_grandly_unified()
|
|
173
|
+
in_test_status = global_site_mapper_cache.cachedObj.getSite(site_name).status == "test"
|
|
174
|
+
|
|
175
|
+
# change label
|
|
176
|
+
if in_test_status and prod_source_label in ["user", "managed", "unified"]:
|
|
177
|
+
new_label = "test"
|
|
178
|
+
tmp_logger.debug(f"prod_source_label changed from {prod_source_label} to {new_label}")
|
|
179
|
+
prod_source_label = new_label
|
|
180
|
+
|
|
181
|
+
# wrapper function for timeout
|
|
182
|
+
timed_method = TimedMethod(global_task_buffer.getJobs, timeout)
|
|
183
|
+
timed_method.run(
|
|
184
|
+
n_jobs,
|
|
185
|
+
site_name,
|
|
186
|
+
prod_source_label,
|
|
187
|
+
memory,
|
|
188
|
+
disk_space,
|
|
189
|
+
node,
|
|
190
|
+
timeout,
|
|
191
|
+
computing_element,
|
|
192
|
+
prod_user_id,
|
|
193
|
+
task_id,
|
|
194
|
+
background,
|
|
195
|
+
resource_type,
|
|
196
|
+
harvester_id,
|
|
197
|
+
worker_id,
|
|
198
|
+
scheduler_id,
|
|
199
|
+
job_type,
|
|
200
|
+
is_grandly_unified,
|
|
201
|
+
via_topic,
|
|
202
|
+
remaining_time,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Time-out
|
|
206
|
+
if timed_method.result == Protocol.TimeOutToken:
|
|
207
|
+
message = "Timed out"
|
|
208
|
+
tmp_logger.debug(message)
|
|
209
|
+
return generate_response(False, message=message)
|
|
210
|
+
|
|
211
|
+
# Try to get the jobs
|
|
212
|
+
jobs = []
|
|
213
|
+
if isinstance(timed_method.result, list):
|
|
214
|
+
result = timed_method.result
|
|
215
|
+
secrets_map = result.pop()
|
|
216
|
+
proxy_key = result[-1]
|
|
217
|
+
n_sent = result[-2]
|
|
218
|
+
jobs = result[:-2]
|
|
219
|
+
|
|
220
|
+
if not jobs:
|
|
221
|
+
message = "No jobs in PanDA"
|
|
222
|
+
tmp_logger.debug(message)
|
|
223
|
+
pilot_logger.info(f"method=noJob,site={site_name},node={node},type={prod_source_label}")
|
|
224
|
+
return generate_response(False, message=message)
|
|
225
|
+
|
|
226
|
+
# add each job to the list
|
|
227
|
+
response_list = []
|
|
228
|
+
for tmp_job in jobs:
|
|
229
|
+
try:
|
|
230
|
+
# The response is nothing but a dictionary with the job information
|
|
231
|
+
response = Protocol.Response(Protocol.SC_Success)
|
|
232
|
+
response.appendJob(tmp_job, global_site_mapper_cache)
|
|
233
|
+
except Exception as e:
|
|
234
|
+
tmp_msg = f"failed to get jobs with {str(e)}"
|
|
235
|
+
tmp_logger.error(f"{tmp_msg}\n{traceback.format_exc()}")
|
|
236
|
+
raise
|
|
237
|
+
|
|
238
|
+
# append n_sent
|
|
239
|
+
response.appendNode("nSent", n_sent)
|
|
240
|
+
|
|
241
|
+
# set proxy key
|
|
242
|
+
if get_proxy_key:
|
|
243
|
+
response.setProxyKey(proxy_key)
|
|
244
|
+
|
|
245
|
+
# set user secrets
|
|
246
|
+
if tmp_job.use_secrets() and tmp_job.prodUserName in secrets_map and secrets_map[tmp_job.prodUserName]:
|
|
247
|
+
response.appendNode("secrets", secrets_map[tmp_job.prodUserName])
|
|
248
|
+
|
|
249
|
+
# set pilot secrets
|
|
250
|
+
pilot_secrets = secrets_map.get(panda_config.pilot_secrets, None)
|
|
251
|
+
if pilot_secrets:
|
|
252
|
+
response.appendNode("pilotSecrets", secrets_map[panda_config.pilot_secrets])
|
|
253
|
+
|
|
254
|
+
response_list.append(response.data)
|
|
255
|
+
|
|
256
|
+
# if the jobs were requested in bulk, we make a bulk response
|
|
257
|
+
if n_jobs is not None:
|
|
258
|
+
try:
|
|
259
|
+
response = Protocol.Response(Protocol.SC_Success)
|
|
260
|
+
response.appendNode("jobs", response_list)
|
|
261
|
+
except Exception as e:
|
|
262
|
+
tmp_msg = f"Failed to make response with {str(e)}"
|
|
263
|
+
tmp_logger.error(f"{tmp_msg}\n{traceback.format_exc()}")
|
|
264
|
+
raise
|
|
265
|
+
|
|
266
|
+
tmp_logger.debug(f"Done for {site_name} {node}")
|
|
267
|
+
|
|
268
|
+
t_end = time.time()
|
|
269
|
+
t_delta = t_end - t_start
|
|
270
|
+
tmp_logger.info(f"site_name={site_name} took timing={t_delta}s in_test={in_test_status}")
|
|
271
|
+
return generate_response(True, data=response.data)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@request_validation(_logger, secure=True, request_method="GET")
|
|
275
|
+
def get_job_status(req: PandaRequest, job_ids: List[int], timeout: int = 60) -> dict:
|
|
276
|
+
"""
|
|
277
|
+
Get job status
|
|
278
|
+
|
|
279
|
+
Gets the status for a list of jobs. Requires a secure connection.
|
|
280
|
+
|
|
281
|
+
API details:
|
|
282
|
+
HTTP Method: GET
|
|
283
|
+
Path: /v1/pilot/get_job_status
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
req(PandaRequest): Internally generated request object containing the environment variables.
|
|
287
|
+
job_ids(list): list of job IDs.
|
|
288
|
+
timeout(int, optional): The timeout value. Defaults to 60.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
dict: The system response `{"success": success, "message": message, "data": data}`. The data is a list of job status dictionaries, in the format
|
|
292
|
+
```
|
|
293
|
+
[{"job_id": <job_id_requested>, "status": "not found", "attempt_number": 0}), {"job_id": <job_id>, "status": <status>, "attempt_number": <attempt_nr>}]
|
|
294
|
+
```
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
tmp_logger = LogWrapper(_logger, f"get_job_status {job_ids}")
|
|
298
|
+
tmp_logger.debug("Start")
|
|
299
|
+
|
|
300
|
+
# peek jobs
|
|
301
|
+
timed_method = TimedMethod(global_task_buffer.peekJobs, timeout)
|
|
302
|
+
timed_method.run(job_ids, fromDefined=False, fromActive=True, fromArchived=True, forAnal=False, use_json=False)
|
|
303
|
+
|
|
304
|
+
# make response
|
|
305
|
+
if timed_method.result == Protocol.TimeOutToken:
|
|
306
|
+
message = "Timed out"
|
|
307
|
+
tmp_logger.debug(message)
|
|
308
|
+
return generate_response(False, message=message)
|
|
309
|
+
|
|
310
|
+
if not isinstance(timed_method.result, list):
|
|
311
|
+
message = "Database error"
|
|
312
|
+
tmp_logger.debug(message)
|
|
313
|
+
return generate_response(False, message=message)
|
|
314
|
+
|
|
315
|
+
job_status_list = []
|
|
316
|
+
for job, job_id_requested in zip(timed_method.result, job_ids):
|
|
317
|
+
if not job:
|
|
318
|
+
job_status_list.append({"job_id": job_id_requested, "status": "not found", "attempt_number": 0})
|
|
319
|
+
else:
|
|
320
|
+
job_status_list.append({"job_id": job.PandaID, "status": job.jobStatus, "attempt_number": job.attemptNr})
|
|
321
|
+
|
|
322
|
+
tmp_logger.debug(f"Done: job_status_list={job_status_list}")
|
|
323
|
+
|
|
324
|
+
return generate_response(True, data=job_status_list)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@request_validation(_logger, secure=True, production=True, request_method="POST")
|
|
328
|
+
def update_job(
|
|
329
|
+
req: PandaRequest,
|
|
330
|
+
job_id: int,
|
|
331
|
+
job_status: str,
|
|
332
|
+
job_output_report: str = "",
|
|
333
|
+
node: str = None,
|
|
334
|
+
cpu_consumption_time: int = None,
|
|
335
|
+
cpu_consumption_unit: str = None,
|
|
336
|
+
scheduler_id: str = None,
|
|
337
|
+
pilot_id: str = None,
|
|
338
|
+
site_name: str = None,
|
|
339
|
+
pilot_log: str = "",
|
|
340
|
+
meta_data: str = "",
|
|
341
|
+
cpu_conversion_factor: float | int = None,
|
|
342
|
+
trans_exit_code: int = None,
|
|
343
|
+
pilot_error_code: int = None,
|
|
344
|
+
pilot_error_diag: str = None,
|
|
345
|
+
exe_error_code: int = None,
|
|
346
|
+
exe_error_diag: str = None,
|
|
347
|
+
pilot_timing: str = None,
|
|
348
|
+
start_time: str = None,
|
|
349
|
+
end_time: str = None,
|
|
350
|
+
n_events: int = None,
|
|
351
|
+
n_input_files: int = None,
|
|
352
|
+
batch_id: str = None,
|
|
353
|
+
attempt_nr: int = None,
|
|
354
|
+
job_metrics: str = None,
|
|
355
|
+
stdout: str = "",
|
|
356
|
+
job_sub_status: str = None,
|
|
357
|
+
core_count: int = None,
|
|
358
|
+
max_rss: int | float = None,
|
|
359
|
+
max_vmem: int | float = None,
|
|
360
|
+
max_swap: int | float = None,
|
|
361
|
+
max_pss: int | float = None,
|
|
362
|
+
avg_rss: int | float = None,
|
|
363
|
+
avg_vmem: int | float = None,
|
|
364
|
+
avg_swap: int | float = None,
|
|
365
|
+
avg_pss: int | float = None,
|
|
366
|
+
tot_rchar: int | float = None,
|
|
367
|
+
tot_wchar: int | float = None,
|
|
368
|
+
tot_rbytes: int | float = None,
|
|
369
|
+
tot_wbytes: int | float = None,
|
|
370
|
+
rate_rchar: int | float = None,
|
|
371
|
+
rate_wchar: int | float = None,
|
|
372
|
+
rate_rbytes: int | float = None,
|
|
373
|
+
rate_wbytes: int | float = None,
|
|
374
|
+
corrupted_files: str = None,
|
|
375
|
+
mean_core_count: int = None,
|
|
376
|
+
cpu_architecture_level: str = None,
|
|
377
|
+
grid: str = None,
|
|
378
|
+
source_site: str = None,
|
|
379
|
+
destination_site: str = None,
|
|
380
|
+
timeout: int = 60,
|
|
381
|
+
):
|
|
382
|
+
"""
|
|
383
|
+
Update job
|
|
384
|
+
|
|
385
|
+
Updates the details for a job, stores the metadata and excerpts from the pilot log. Requires a secure connection and production role.
|
|
386
|
+
|
|
387
|
+
API details:
|
|
388
|
+
HTTP Method: POST
|
|
389
|
+
Path: /v1/pilot/update_job
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
req(PandaRequest): internally generated request object containing the env variables
|
|
393
|
+
job_id (int): PanDA job ID
|
|
394
|
+
job_status(str, optional): Job status
|
|
395
|
+
job_sub_status(str, optional): Job sub status. Optional, defaults to `None`
|
|
396
|
+
start_time(str, optional): Job start time in format `"%Y-%m-%d %H:%M:%S"`. Optional, defaults to `None`
|
|
397
|
+
end_time(str, optional): Job end time in format `"%Y-%m-%d %H:%M:%S"`. Optional, defaults to `None`
|
|
398
|
+
pilot_timing(str, optional): String with pilot timings. Optional, defaults to `None`
|
|
399
|
+
site_name(str, optional): PanDA queue name. Optional, defaults to `None`
|
|
400
|
+
node(str, optional): Identifier for worker node/slot. Optional, defaults to `None`
|
|
401
|
+
scheduler_id(str, optional): Scheduler ID, such as harvester instance. Optional, defaults to `None`
|
|
402
|
+
pilot_id(str, optional): Pilot ID. Optional, defaults to `None`
|
|
403
|
+
batch_id(str, optional): Batch ID. Optional, defaults to `None`
|
|
404
|
+
trans_exit_code(int, optional): Transformation exit code. Optional, defaults to `None`
|
|
405
|
+
pilot_error_code(int, optional): Pilot error code. Optional, defaults to `None`
|
|
406
|
+
pilot_error_diag(str, optional): Pilot error message. Optional, defaults to `None`
|
|
407
|
+
exe_error_code(int, optional): Execution error code. Optional, defaults to `None`
|
|
408
|
+
exe_error_diag(str, optional): Execution error message. Optional, defaults to `None`
|
|
409
|
+
n_events(int, optional): Number of events. Optional, defaults to `None`
|
|
410
|
+
n_input_files(int, optional): Number of input files. Optional, defaults to `None`
|
|
411
|
+
attempt_nr(int, optional): Job attempt number. Optional, defaults to `None`
|
|
412
|
+
cpu_consumption_time(int, optional): CPU consumption time. Optional, defaults to `None`
|
|
413
|
+
cpu_consumption_unit(str, optional): CPU consumption unit, being used for updating some CPU details. Optional, defaults to `None`
|
|
414
|
+
cpu_conversion_factor(float, optional): CPU conversion factor. Optional defaults to `None`
|
|
415
|
+
core_count(int, optional): Number of cores of the job. Optional, defaults to `None`
|
|
416
|
+
mean_core_count(int, optional): Mean core count. Optional, defaults to `None`
|
|
417
|
+
max_rss(int, optional): Measured max RSS memory. Optional, defaults to `None`
|
|
418
|
+
max_vmem(int, optional): Measured max Virtual memory. Optional, defaults to `None`
|
|
419
|
+
max_swap(int, optional): Measured max swap memory. Optional, defaults to `None`
|
|
420
|
+
max_pss(int, optional): Measured max PSS memory. Optional, defaults to `None`
|
|
421
|
+
avg_rss(int, optional): Measured average RSS. Optional, defaults to `None`
|
|
422
|
+
avg_vmem(int, optional): Measured average Virtual memory.Optional, defaults to `None`
|
|
423
|
+
avg_swap(int, optional): Measured average swap memory. Optional, defaults to `None`
|
|
424
|
+
avg_pss(int, optional): Measured average PSS. Optional, defaults to `None`
|
|
425
|
+
tot_rchar(int, optional): Measured total read characters. Optional, defaults to `None`
|
|
426
|
+
tot_wchar(int, optional): Measured total written characters. Optional, defaults to `None`
|
|
427
|
+
tot_rbytes(int, optional): Measured total read bytes. Optional, defaults to `None`
|
|
428
|
+
tot_wbytes(int, optional): Measured total written bytes. Optional, defaults to `None`
|
|
429
|
+
rate_rchar(int, optional): Measured rate for read characters. Optional, defaults to `None`
|
|
430
|
+
rate_wchar(int, optional): Measured rate for written characters. Optional, defaults to `None`
|
|
431
|
+
rate_rbytes(int, optional): Measured rate for read bytes. Optional, defaults to `None`
|
|
432
|
+
rate_wbytes(int, optional): Measured rate for written bytes. Optional, defaults to `None`
|
|
433
|
+
corrupted_files(str, optional): List of corrupted files in comma separated format. Optional, defaults to `None`
|
|
434
|
+
cpu_architecture_level(str, optional): CPU architecture level (e.g. `x86_64-v3`). Optional, defaults to `None`
|
|
435
|
+
grid(str, optional): Grid type. Optional, defaults to `None`
|
|
436
|
+
source_site(str, optional): Source site name. Optional, defaults to `None`
|
|
437
|
+
destination_site(str, optional): Destination site name. Optional, defaults to `None`
|
|
438
|
+
job_metrics(str, optional): Job metrics. Optional, defaults to `None`
|
|
439
|
+
job_output_report(str, optional): Job output report. Optional, defaults to `""`
|
|
440
|
+
pilot_log(str, optional): Pilot log excerpt. Optional, defaults to `""`
|
|
441
|
+
meta_data(str, optional): Job metadata. Optional, defaults to `""`
|
|
442
|
+
stdout(str, optional): Standard output. Optional, defaults to `""`
|
|
443
|
+
timeout(int, optional): Timeout for the operation in seconds. Optional, defaults to 60
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
dict: The system response `{"success": success, "message": message, "data": data}`. Data will contain a dictionary with the pilot secrets and the command to the pilot.
|
|
447
|
+
```
|
|
448
|
+
{"pilotSecrets": <pilot_secrets>, "command": <command>}
|
|
449
|
+
```
|
|
450
|
+
"""
|
|
451
|
+
tmp_logger = LogWrapper(_logger, f"update_job PandaID={job_id} PID={os.getpid()}")
|
|
452
|
+
tmp_logger.debug("Start")
|
|
453
|
+
|
|
454
|
+
_logger.debug(
|
|
455
|
+
f"update_job({job_id}, {job_status}, {trans_exit_code}, {pilot_error_code}, {pilot_error_diag}, {node},"
|
|
456
|
+
f"cpu_consumption_time={cpu_consumption_time}, {cpu_consumption_unit}, {cpu_architecture_level}, "
|
|
457
|
+
f"{scheduler_id}, {pilot_id}, {site_name}, {n_events}, {n_input_files}, {cpu_conversion_factor}, "
|
|
458
|
+
f"{exe_error_code}, {exe_error_diag}, {pilot_timing}, {start_time}, {end_time}, {batch_id}, "
|
|
459
|
+
f"attempt_nr:{attempt_nr}, job_sub_status:{job_sub_status}, core_count:{core_count}, "
|
|
460
|
+
f"max_rss={max_rss}, max_vmem={max_vmem}, max_swap={max_swap}, "
|
|
461
|
+
f"max_pss={max_pss}, avg_rss={avg_rss}, avg_vmem={avg_vmem}, avg_swap={avg_swap}, avg_pss={avg_pss}, "
|
|
462
|
+
f"tot_rchar={tot_rchar}, tot_wchar={tot_wchar}, tot_rbytes={tot_rbytes}, tot_wbytes={tot_wbytes}, rate_rchar={rate_rchar}, "
|
|
463
|
+
f"rate_wchar={rate_wchar}, rate_rbytes={rate_rbytes}, rate_wbytes={rate_wbytes}, mean_core_count={mean_core_count}, "
|
|
464
|
+
f"grid={grid}, source_site={source_site}, destination_site={destination_site}, "
|
|
465
|
+
f"corrupted_files={corrupted_files}\n==job_output_report==\n{job_output_report}\n==LOG==\n{pilot_log[:1024]}\n==Meta==\n{meta_data[:1024]}\n"
|
|
466
|
+
f"==Metrics==\n{job_metrics}\n==stdout==\n{stdout})"
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
pilot_logger.debug(f"method=updateJob,site={site_name},node={node},type=None")
|
|
470
|
+
|
|
471
|
+
# aborting message
|
|
472
|
+
if job_id == "NULL":
|
|
473
|
+
response = Protocol.Response(Protocol.SC_Invalid)
|
|
474
|
+
return generate_response(success=False, message="job_id is NULL", data=response.data)
|
|
475
|
+
|
|
476
|
+
# check the job status is valid
|
|
477
|
+
if job_status not in VALID_JOB_STATES:
|
|
478
|
+
message = f"Invalid job status: {job_status}"
|
|
479
|
+
tmp_logger.warning(message)
|
|
480
|
+
response = Protocol.Response(Protocol.SC_Invalid)
|
|
481
|
+
return generate_response(success=False, message=message, data=response.data)
|
|
482
|
+
|
|
483
|
+
# create the job parameter map
|
|
484
|
+
param = {}
|
|
485
|
+
fields = [
|
|
486
|
+
("cpuConsumptionTime", cpu_consumption_time, int),
|
|
487
|
+
("cpuConsumptionUnit", cpu_consumption_unit, str),
|
|
488
|
+
("cpu_architecture_level", cpu_architecture_level, lambda x: str(x)[:20]),
|
|
489
|
+
("modificationHost", node, lambda x: str(x)[:128]),
|
|
490
|
+
("transExitCode", trans_exit_code, int),
|
|
491
|
+
("pilotErrorCode", pilot_error_code, int),
|
|
492
|
+
("pilotErrorDiag", pilot_error_diag, lambda x: str(x)[:500]),
|
|
493
|
+
("jobMetrics", job_metrics, lambda x: str(x)[:500]),
|
|
494
|
+
("schedulerID", scheduler_id, str),
|
|
495
|
+
("pilotID", pilot_id, lambda x: str(x)[:200]),
|
|
496
|
+
("batchID", batch_id, lambda x: str(x)[:80]),
|
|
497
|
+
("exeErrorCode", exe_error_code, int),
|
|
498
|
+
("exeErrorDiag", exe_error_diag, lambda x: str(x)[:500]),
|
|
499
|
+
("cpuConversion", cpu_conversion_factor, float),
|
|
500
|
+
("pilotTiming", pilot_timing, str),
|
|
501
|
+
("nEvents", n_events, int),
|
|
502
|
+
("nInputFiles", n_input_files, int),
|
|
503
|
+
("jobSubStatus", job_sub_status, str),
|
|
504
|
+
("actualCoreCount", core_count, int),
|
|
505
|
+
("meanCoreCount", mean_core_count, float),
|
|
506
|
+
("maxRSS", max_rss, int),
|
|
507
|
+
("maxVMEM", max_vmem, int),
|
|
508
|
+
("maxSWAP", max_swap, int),
|
|
509
|
+
("maxPSS", max_pss, int),
|
|
510
|
+
("avgRSS", avg_rss, lambda x: int(float(x))),
|
|
511
|
+
("avgVMEM", avg_vmem, lambda x: int(float(x))),
|
|
512
|
+
("avgSWAP", avg_swap, lambda x: int(float(x))),
|
|
513
|
+
("avgPSS", avg_pss, lambda x: int(float(x))),
|
|
514
|
+
("corruptedFiles", corrupted_files, str),
|
|
515
|
+
("grid", grid, str),
|
|
516
|
+
("sourceSite", source_site, str),
|
|
517
|
+
("destinationSite", destination_site, str),
|
|
518
|
+
]
|
|
519
|
+
|
|
520
|
+
# Iterate through fields, apply transformations and add to `param`
|
|
521
|
+
for key, value, cast in fields:
|
|
522
|
+
if value not in [None, ""]:
|
|
523
|
+
try:
|
|
524
|
+
param[key] = cast(value)
|
|
525
|
+
except Exception:
|
|
526
|
+
tmp_logger.error(f"Invalid {key}={value} for updateJob")
|
|
527
|
+
|
|
528
|
+
# Special handling for file size metrics
|
|
529
|
+
file_metrics = [
|
|
530
|
+
("totRCHAR", tot_rchar),
|
|
531
|
+
("totWCHAR", tot_wchar),
|
|
532
|
+
("totRBYTES", tot_rbytes),
|
|
533
|
+
("totWBYTES", tot_wbytes),
|
|
534
|
+
("rateRCHAR", rate_rchar),
|
|
535
|
+
("rateWCHAR", rate_wchar),
|
|
536
|
+
("rateRBYTES", rate_rbytes),
|
|
537
|
+
("rateWBYTES", rate_wbytes),
|
|
538
|
+
]
|
|
539
|
+
|
|
540
|
+
for key, value in file_metrics:
|
|
541
|
+
if value is not None:
|
|
542
|
+
try:
|
|
543
|
+
value = int(value) / 1024 # Convert to kB
|
|
544
|
+
param[key] = min(10**10 - 1, value) # Limit to 10 digits
|
|
545
|
+
except Exception:
|
|
546
|
+
tmp_logger.error(f"Invalid {key}={value} for updateJob")
|
|
547
|
+
|
|
548
|
+
# Convert timestamps
|
|
549
|
+
for key, value in [("startTime", start_time), ("endTime", end_time)]:
|
|
550
|
+
if value is not None:
|
|
551
|
+
try:
|
|
552
|
+
param[key] = datetime.datetime(*time.strptime(value, "%Y-%m-%d %H:%M:%S")[:6])
|
|
553
|
+
except Exception:
|
|
554
|
+
tmp_logger.error(f"Invalid {key}={value} for updateJob")
|
|
555
|
+
|
|
556
|
+
# Handle attempt_nr separately
|
|
557
|
+
if attempt_nr is not None:
|
|
558
|
+
try:
|
|
559
|
+
attempt_nr = int(attempt_nr)
|
|
560
|
+
except Exception:
|
|
561
|
+
attempt_nr = None
|
|
562
|
+
|
|
563
|
+
tmp_logger.debug("executing")
|
|
564
|
+
|
|
565
|
+
# store the pilot log
|
|
566
|
+
if pilot_log != "":
|
|
567
|
+
tmp_logger.debug("Saving pilot log")
|
|
568
|
+
try:
|
|
569
|
+
global_task_buffer.storePilotLog(int(job_id), pilot_log)
|
|
570
|
+
tmp_logger.debug("Saving pilot log DONE")
|
|
571
|
+
except Exception:
|
|
572
|
+
tmp_logger.debug("Saving pilot log FAILED")
|
|
573
|
+
|
|
574
|
+
# add meta_data
|
|
575
|
+
if meta_data != "":
|
|
576
|
+
ret = global_task_buffer.addMetadata([job_id], [meta_data], [job_status])
|
|
577
|
+
if len(ret) > 0 and not ret[0]:
|
|
578
|
+
message = "Failed to add meta_data"
|
|
579
|
+
tmp_logger.debug(message)
|
|
580
|
+
return generate_response(True, message, data={"StatusCode": Protocol.SC_Failed, "ErrorDiag": message})
|
|
581
|
+
|
|
582
|
+
# add stdout
|
|
583
|
+
if stdout != "":
|
|
584
|
+
global_task_buffer.addStdOut(job_id, stdout)
|
|
585
|
+
|
|
586
|
+
# update the job
|
|
587
|
+
tmp_status = job_status
|
|
588
|
+
update_state_change = False
|
|
589
|
+
if job_status in ("failed", "finished"):
|
|
590
|
+
tmp_status = "holding"
|
|
591
|
+
update_state_change = True # update stateChangeTime to prevent Watcher from finding this job
|
|
592
|
+
param["jobDispatcherErrorDiag"] = None
|
|
593
|
+
elif job_status in ("holding", "transferring"):
|
|
594
|
+
param["jobDispatcherErrorDiag"] = f"set to {job_status} by the pilot at {naive_utcnow().strftime('%Y-%m-%d %H:%M:%S')}"
|
|
595
|
+
|
|
596
|
+
# update the job status in the database
|
|
597
|
+
timeout = None if job_status == "holding" else timeout
|
|
598
|
+
timed_method = TimedMethod(global_task_buffer.updateJobStatus, timeout)
|
|
599
|
+
timed_method.run(job_id, tmp_status, param, update_state_change, attempt_nr)
|
|
600
|
+
|
|
601
|
+
# time-out
|
|
602
|
+
if timed_method.result == Protocol.TimeOutToken:
|
|
603
|
+
message = "Timed out"
|
|
604
|
+
tmp_logger.error(message)
|
|
605
|
+
response = Protocol.Response(Protocol.SC_TimeOut)
|
|
606
|
+
return generate_response(True, message=message, data=response.data)
|
|
607
|
+
|
|
608
|
+
# no result
|
|
609
|
+
if not timed_method.result:
|
|
610
|
+
message = "Database error"
|
|
611
|
+
tmp_logger.error(message)
|
|
612
|
+
response = Protocol.Response(Protocol.SC_Failed)
|
|
613
|
+
return generate_response(True, message=message, data=response.data)
|
|
614
|
+
|
|
615
|
+
# generate the response with the result
|
|
616
|
+
data = {"StatusCode": Protocol.SC_Success}
|
|
617
|
+
result = timed_method.result
|
|
618
|
+
|
|
619
|
+
# set the secrets
|
|
620
|
+
secrets = result.get("secrets") if isinstance(result, dict) else None
|
|
621
|
+
if secrets:
|
|
622
|
+
data["pilotSecrets"] = secrets
|
|
623
|
+
|
|
624
|
+
# set the command to the pilot
|
|
625
|
+
command = result.get("command") if isinstance(result, dict) else result
|
|
626
|
+
data["command"] = command if isinstance(command, str) else None
|
|
627
|
+
|
|
628
|
+
# add output to dataset for failed/finished jobs with correct results
|
|
629
|
+
if job_status in ("failed", "finished") and result not in ("badattemptnr", "alreadydone"):
|
|
630
|
+
adder_gen = AdderGen(global_task_buffer, job_id, job_status, attempt_nr)
|
|
631
|
+
adder_gen.dump_file_report(job_output_report, attempt_nr)
|
|
632
|
+
del adder_gen
|
|
633
|
+
|
|
634
|
+
tmp_logger.debug(f"Done. data={data}")
|
|
635
|
+
return generate_response(True, data=data)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
@request_validation(_logger, secure=True, production=True, request_method="POST")
|
|
639
|
+
def update_jobs_bulk(req, job_list: List, harvester_id: str = None):
|
|
640
|
+
"""
|
|
641
|
+
Update jobs in bulk
|
|
642
|
+
|
|
643
|
+
Bulk method to update the details for jobs, store the metadata and excerpt from the pilot log. Internally, this method loops over
|
|
644
|
+
the jobs and calls `update_job` for each job. Requires a secure connection and production role.
|
|
645
|
+
|
|
646
|
+
API details:
|
|
647
|
+
HTTP Method: POST
|
|
648
|
+
Path: /v1/pilot/update_jobs_bulk
|
|
649
|
+
|
|
650
|
+
Args:
|
|
651
|
+
req(PandaRequest): internally generated request object containing the env variables
|
|
652
|
+
job_list(list): list of job dictionaries to update. The mandatory and optional keys for each job dictionary are the same as the arguments for `update_job`.
|
|
653
|
+
harvester_id (str, optional): Harvester ID. Optional, defaults to `None`.
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
dict: The system response `{"success": success, "message": message, "data": data}`. Data will contain a dictionary with the pilot secrets and the command to the pilot.
|
|
657
|
+
```
|
|
658
|
+
{"pilotSecrets": <pilot_secrets>, "command": <command>}
|
|
659
|
+
```
|
|
660
|
+
"""
|
|
661
|
+
tmp_logger = LogWrapper(_logger, f"update_jobs_bulk harvester_id={harvester_id}")
|
|
662
|
+
tmp_logger.debug("Start")
|
|
663
|
+
t_start = naive_utcnow()
|
|
664
|
+
|
|
665
|
+
success = False
|
|
666
|
+
message = ""
|
|
667
|
+
data = []
|
|
668
|
+
|
|
669
|
+
try:
|
|
670
|
+
for job_dict in job_list:
|
|
671
|
+
job_id = job_dict["job_id"]
|
|
672
|
+
del job_dict["job_id"]
|
|
673
|
+
|
|
674
|
+
status = job_dict["job_status"]
|
|
675
|
+
del job_dict["job_status"]
|
|
676
|
+
|
|
677
|
+
if "meta_data" in job_dict:
|
|
678
|
+
job_dict["meta_data"] = str(job_dict["meta_data"])
|
|
679
|
+
|
|
680
|
+
tmp_ret = update_job(req, job_id, status, **job_dict)
|
|
681
|
+
data.append(tmp_ret)
|
|
682
|
+
success = True
|
|
683
|
+
except Exception:
|
|
684
|
+
err_type, err_value = sys.exc_info()[:2]
|
|
685
|
+
message = f"failed with {err_type.__name__} {err_value}"
|
|
686
|
+
data = []
|
|
687
|
+
tmp_logger.error(f"{message}\n{traceback.format_exc()}")
|
|
688
|
+
|
|
689
|
+
t_delta = naive_utcnow() - t_start
|
|
690
|
+
tmp_logger.debug(f"Done. Took {t_delta.seconds}.{t_delta.microseconds // 1000:03d} sec")
|
|
691
|
+
return generate_response(success, message, data)
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
@request_validation(_logger, secure=True, production=True, request_method="POST")
|
|
695
|
+
def update_worker_status(req: PandaRequest, worker_id, harvester_id, status, timeout=60, node_id=None):
|
|
696
|
+
"""
|
|
697
|
+
Update worker status
|
|
698
|
+
|
|
699
|
+
Updates the status of a worker with the information seen by the pilot. Requires a secure connection and production role.
|
|
700
|
+
|
|
701
|
+
API details:
|
|
702
|
+
HTTP Method: POST
|
|
703
|
+
Path: /v1/pilot/update_worker_status
|
|
704
|
+
|
|
705
|
+
Args:
|
|
706
|
+
req(PandaRequest): Internally generated request object containing the environment variables.
|
|
707
|
+
worker_id (str): The worker ID.
|
|
708
|
+
harvester_id (str): The harvester ID.
|
|
709
|
+
status (str): The status of the worker. Must be either 'started' or 'finished'.
|
|
710
|
+
timeout (int, optional): The timeout value. Defaults to 60.
|
|
711
|
+
node_id (str, optional): The node ID. Defaults to None.
|
|
712
|
+
|
|
713
|
+
Returns:
|
|
714
|
+
str: The result of the status update or an error message.
|
|
715
|
+
"""
|
|
716
|
+
tmp_logger = LogWrapper(
|
|
717
|
+
_logger,
|
|
718
|
+
f"updateWorkerPilotStatus worker_id={worker_id} harvester_id={harvester_id} status={status} node_id={node_id} PID={os.getpid()}",
|
|
719
|
+
)
|
|
720
|
+
tmp_logger.debug("Start")
|
|
721
|
+
|
|
722
|
+
# validate the state passed by the pilot
|
|
723
|
+
valid_worker_states = ("started", "finished")
|
|
724
|
+
if status not in valid_worker_states:
|
|
725
|
+
message = f"Invalid worker state. The worker state has to be in {valid_worker_states}"
|
|
726
|
+
tmp_logger.debug(message)
|
|
727
|
+
return generate_response(False, message)
|
|
728
|
+
|
|
729
|
+
timed_method = TimedMethod(global_task_buffer.updateWorkerPilotStatus, timeout)
|
|
730
|
+
timed_method.run(worker_id, harvester_id, status, node_id)
|
|
731
|
+
|
|
732
|
+
# generate the response
|
|
733
|
+
if not timed_method.result: # failure
|
|
734
|
+
message = "Failed to update worker status"
|
|
735
|
+
tmp_logger.error(message)
|
|
736
|
+
return generate_response(False, message)
|
|
737
|
+
|
|
738
|
+
if timed_method.result == Protocol.TimeOutToken: # timeout
|
|
739
|
+
message = "Updating worker status timed out"
|
|
740
|
+
tmp_logger.error(message)
|
|
741
|
+
return generate_response(False, message)
|
|
742
|
+
|
|
743
|
+
tmp_logger.debug(f"Done")
|
|
744
|
+
return generate_response(True)
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
@request_validation(_logger, secure=True, production=True, request_method="POST")
|
|
748
|
+
def update_worker_node(
|
|
749
|
+
req: PandaRequest,
|
|
750
|
+
site: str,
|
|
751
|
+
host_name: str,
|
|
752
|
+
cpu_model: str,
|
|
753
|
+
panda_queue: str = None,
|
|
754
|
+
n_logical_cpus: int = None,
|
|
755
|
+
n_sockets: int = None,
|
|
756
|
+
cores_per_socket: int = None,
|
|
757
|
+
threads_per_core: int = None,
|
|
758
|
+
cpu_architecture: str = None,
|
|
759
|
+
cpu_architecture_level: str = None,
|
|
760
|
+
clock_speed: float = None,
|
|
761
|
+
total_memory: int = None,
|
|
762
|
+
total_local_disk: int = None,
|
|
763
|
+
timeout: int = 60,
|
|
764
|
+
):
|
|
765
|
+
"""
|
|
766
|
+
Update worker node
|
|
767
|
+
|
|
768
|
+
Updates a worker node in the worker node map. When already found, it updates the `last_seen` time. When not found, it adds the worker node. Requires a secure connection and production role.
|
|
769
|
+
|
|
770
|
+
API details:
|
|
771
|
+
HTTP Method: POST
|
|
772
|
+
Path: /v1/pilot/update_worker_node
|
|
773
|
+
|
|
774
|
+
Args:
|
|
775
|
+
req(PandaRequest): Internally generated request object containing the environment variables.
|
|
776
|
+
site(str): Site name (e.g. ATLAS site name, not PanDA queue).
|
|
777
|
+
host_name(str): Host name. In the case of reporting in format `slot@worker_node.example.com`, the slot ID will be parsed out.
|
|
778
|
+
cpu_model(str): CPU model, e.g. `AMD EPYC 7351`.
|
|
779
|
+
panda_queue(str, optional): PanDA queue the worker node is associated to. Optional, defaults to `None`.
|
|
780
|
+
n_logical_cpus(int, optional): Number of logical CPUs: n_sockets * cores_per_socket * threads_per_core.
|
|
781
|
+
When SMT is enabled, this is the number of threads. Otherwise it is the number of cores. Optional, defaults to `None`.
|
|
782
|
+
n_sockets(int, optional): Number of sockets. Optional, defaults to `None`.
|
|
783
|
+
cores_per_socket(int, optional): Number of cores per socket. Optional, defaults to `None`.
|
|
784
|
+
threads_per_core(int, optional): Number of threads per core. When SMT is disabled, this is 1. Otherwise a number > 1. Optional, defaults to `None`.
|
|
785
|
+
cpu_architecture(str, optional): CPU architecture, e.g. `x86_64`. Optional, defaults to `None`.
|
|
786
|
+
cpu_architecture_level(str, optional): CPU architecture level, e.g. `x86-64-v3`. Optional, defaults to `None`.
|
|
787
|
+
clock_speed(float, optional): Clock speed in MHz. Optional, defaults to `None`.
|
|
788
|
+
total_memory(int, optional): Total memory in MB. Optional, defaults to `None`.
|
|
789
|
+
total_local_disk(int, optional): Total disk space in GB. Optional, defaults to `None`.
|
|
790
|
+
timeout(int, optional): The timeout value. Defaults to 60.
|
|
791
|
+
|
|
792
|
+
Returns:
|
|
793
|
+
dict: The system response `{"success": success, "message": message, "data": data}`. True for success, False for failure, and an error message.
|
|
794
|
+
"""
|
|
795
|
+
tmp_logger = LogWrapper(_logger, f"update_worker_node site={site} panda_queue={panda_queue} host_name={host_name} cpu_model={cpu_model}")
|
|
796
|
+
tmp_logger.debug("Start")
|
|
797
|
+
|
|
798
|
+
cpu_model_normalized = normalize_cpu_model(cpu_model)
|
|
799
|
+
|
|
800
|
+
timed_method = TimedMethod(global_task_buffer.update_worker_node, timeout)
|
|
801
|
+
timed_method.run(
|
|
802
|
+
site,
|
|
803
|
+
panda_queue,
|
|
804
|
+
host_name,
|
|
805
|
+
cpu_model,
|
|
806
|
+
cpu_model_normalized,
|
|
807
|
+
n_logical_cpus,
|
|
808
|
+
n_sockets,
|
|
809
|
+
cores_per_socket,
|
|
810
|
+
threads_per_core,
|
|
811
|
+
cpu_architecture,
|
|
812
|
+
cpu_architecture_level,
|
|
813
|
+
clock_speed,
|
|
814
|
+
total_memory,
|
|
815
|
+
total_local_disk,
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
if timed_method.result == Protocol.TimeOutToken: # timeout
|
|
819
|
+
message = "Updating worker node timed out"
|
|
820
|
+
tmp_logger.error(message)
|
|
821
|
+
return generate_response(False, message)
|
|
822
|
+
|
|
823
|
+
success, message = timed_method.result
|
|
824
|
+
|
|
825
|
+
tmp_logger.debug(f"Done")
|
|
826
|
+
return generate_response(success, message)
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
@request_validation(_logger, secure=True, production=True, request_method="POST")
|
|
830
|
+
def update_worker_node_gpu(
|
|
831
|
+
req: PandaRequest,
|
|
832
|
+
site: str,
|
|
833
|
+
host_name: str,
|
|
834
|
+
vendor: str,
|
|
835
|
+
model: str,
|
|
836
|
+
count: int,
|
|
837
|
+
vram: int = None,
|
|
838
|
+
architecture: str = None,
|
|
839
|
+
framework: str = None,
|
|
840
|
+
framework_version: str = None,
|
|
841
|
+
driver_version: str = None,
|
|
842
|
+
timeout: int = 60,
|
|
843
|
+
):
|
|
844
|
+
"""
|
|
845
|
+
Update GPUs for a worker node
|
|
846
|
+
|
|
847
|
+
Updates the GPUs associated to a worker node in the worker node map. When already found, it updates the `last_seen` time. Requires a secure connection and production role.
|
|
848
|
+
|
|
849
|
+
API details:
|
|
850
|
+
HTTP Method: POST
|
|
851
|
+
Path: /v1/pilot/update_worker_node_gpu
|
|
852
|
+
|
|
853
|
+
Args:
|
|
854
|
+
req(PandaRequest): Internally generated request object containing the environment variables.
|
|
855
|
+
site(str): Site name (e.g. ATLAS site name, not PanDA queue).
|
|
856
|
+
host_name(str): Host name. In the case of reporting in format `slot@worker_node.example.com`, the slot ID will be parsed out.
|
|
857
|
+
vendor(str): GPU vendor, e.g. `NVIDIA`.
|
|
858
|
+
model(str): GPU model, e.g. `A100 80GB`.
|
|
859
|
+
count(int): Number of GPUs of this type in the worker node.
|
|
860
|
+
vram(int, optional): VRAM memory in MB. Defaults to `None`.
|
|
861
|
+
architecture(str, optional): GPU architecture, e.g. `Tesla`, `Ampere`... Defaults to `None`.
|
|
862
|
+
framework(str, optional): Driver framework available, e.g. `CUDA`. Defaults to `None`.
|
|
863
|
+
framework_version(str, optional): Version of the driver framework, e.g. `12.2`. Defaults to `None`.
|
|
864
|
+
driver_version(str, optional): Version of the driver, e.g. `575.51.03`. Defaults to `None`
|
|
865
|
+
timeout(int, optional): The timeout value. Defaults to `60`.
|
|
866
|
+
|
|
867
|
+
Returns:
|
|
868
|
+
dict: The system response `{"success": success, "message": message, "data": data}`. True for success, False for failure, and an error message.
|
|
869
|
+
"""
|
|
870
|
+
tmp_logger = LogWrapper(_logger, f"update_worker_node_gpu site={site} host_name={host_name} vendor={vendor} model={model}")
|
|
871
|
+
tmp_logger.debug("Start")
|
|
872
|
+
|
|
873
|
+
timed_method = TimedMethod(global_task_buffer.update_worker_node_gpu, timeout)
|
|
874
|
+
timed_method.run(
|
|
875
|
+
site,
|
|
876
|
+
host_name,
|
|
877
|
+
vendor,
|
|
878
|
+
model,
|
|
879
|
+
count,
|
|
880
|
+
vram,
|
|
881
|
+
architecture,
|
|
882
|
+
framework,
|
|
883
|
+
framework_version,
|
|
884
|
+
driver_version,
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
if timed_method.result == Protocol.TimeOutToken: # timeout
|
|
888
|
+
message = "Updating worker node GPU timed out"
|
|
889
|
+
tmp_logger.error(message)
|
|
890
|
+
return generate_response(False, message)
|
|
891
|
+
|
|
892
|
+
success, message = timed_method.result
|
|
893
|
+
|
|
894
|
+
tmp_logger.debug(f"Done")
|
|
895
|
+
return generate_response(success, message)
|