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,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EventLookupClientEI is a class for looking up events in the EventIndex.
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import tempfile
|
|
7
|
+
|
|
8
|
+
from typing import List, Tuple
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EventLookupClientEI:
|
|
12
|
+
"""
|
|
13
|
+
EventLookupClientEI is a class for looking up events in the EventIndex.
|
|
14
|
+
"""
|
|
15
|
+
def do_lookup(self, event_run_list: List[Tuple[int, int]], stream: str = None, tokens: str = None, ami_tag: str = None) -> Tuple[List[str], str, str, str]:
|
|
16
|
+
"""
|
|
17
|
+
Performs a lookup in the EventIndex for the given parameters.
|
|
18
|
+
|
|
19
|
+
Parameters:
|
|
20
|
+
event_run_list (List[Tuple[int, int]]): The list of run events.
|
|
21
|
+
stream (str): The name of the stream.
|
|
22
|
+
tokens (str): The tokens.
|
|
23
|
+
ami_tag (str): The AMI tag.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Tuple[List[str], str, str, str]: A tuple containing the list of GUIDs, the command, the output, and the error.
|
|
27
|
+
"""
|
|
28
|
+
command = os.path.join(
|
|
29
|
+
os.getenv(
|
|
30
|
+
"EIDIR",
|
|
31
|
+
"/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase/x86_64/EIClient/current",
|
|
32
|
+
),
|
|
33
|
+
"bin",
|
|
34
|
+
"event-lookup",
|
|
35
|
+
)
|
|
36
|
+
with tempfile.NamedTemporaryFile(mode="w+t") as tmp_event_file:
|
|
37
|
+
command += f" -F {tmp_event_file.name} "
|
|
38
|
+
for run_event in event_run_list:
|
|
39
|
+
# creating a preformatted string. The :08d and :09d parts are formatting these integers to be eight and nine digits long
|
|
40
|
+
tmp_string = f"{int(run_event[0]):08d} {int(run_event[1]):09d}\n"
|
|
41
|
+
tmp_event_file.write(tmp_string)
|
|
42
|
+
tmp_event_file.flush()
|
|
43
|
+
if stream not in [None, ""]:
|
|
44
|
+
command += f"-s {stream} "
|
|
45
|
+
if ami_tag not in [None, ""]:
|
|
46
|
+
command += f"-a {ami_tag} "
|
|
47
|
+
command += "-c plain "
|
|
48
|
+
with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True,
|
|
49
|
+
universal_newlines=True) as execute_process:
|
|
50
|
+
tmp_output, tmp_error = execute_process.communicate()
|
|
51
|
+
guids = {}
|
|
52
|
+
if tokens == "":
|
|
53
|
+
tokens = None
|
|
54
|
+
try:
|
|
55
|
+
# The output is expected to be a string with multiple lines, each line containing information about a single event.
|
|
56
|
+
# Each line is split into items, the first two items are interpreted as integers representing a run number and an event number.
|
|
57
|
+
# These two numbers are combined into a tuple, run_event, which is used as a key in the guids dictionary.
|
|
58
|
+
# The fourth item in the line is appended to the string "Stream" to form tmp_token, and the third item is assigned to tmp_guid.
|
|
59
|
+
# An example input (tmp_output) is:
|
|
60
|
+
# 123 456 789abc StreamXYZ
|
|
61
|
+
# An example output (guids dictionary) is:
|
|
62
|
+
# (123, 456): {"789abc"}
|
|
63
|
+
for tmp_line in tmp_output.split("\n"):
|
|
64
|
+
tmp_items = tmp_line.split()
|
|
65
|
+
run_event = (int(tmp_items[0]), int(tmp_items[1]))
|
|
66
|
+
guids.setdefault(run_event, set())
|
|
67
|
+
# check type
|
|
68
|
+
tmp_token = "Stream" + tmp_items[3]
|
|
69
|
+
tmp_guid = tmp_items[2]
|
|
70
|
+
if not tokens or tokens == tmp_token:
|
|
71
|
+
guids[run_event].add(tmp_guid)
|
|
72
|
+
if not guids:
|
|
73
|
+
# add dummy
|
|
74
|
+
guids[None] = None
|
|
75
|
+
except Exception:
|
|
76
|
+
pass
|
|
77
|
+
return guids, command, tmp_output, tmp_error
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""
|
|
2
|
+
add data to dataset
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import fcntl
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
import traceback
|
|
11
|
+
|
|
12
|
+
from pandacommon.pandalogger.LogWrapper import LogWrapper
|
|
13
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
14
|
+
|
|
15
|
+
from pandaserver.dataservice import dyn_data_distributer
|
|
16
|
+
from pandaserver.srvcore import CoreUtils
|
|
17
|
+
from pandaserver.userinterface import Client
|
|
18
|
+
|
|
19
|
+
# logger
|
|
20
|
+
_logger = PandaLogger().getLogger("event_picker")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class EventPicker:
|
|
24
|
+
"""
|
|
25
|
+
A class used to add data to a dataset.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# constructor
|
|
29
|
+
def __init__(self, taskBuffer, siteMapper, evpFileName: str, ignoreError: bool):
|
|
30
|
+
"""
|
|
31
|
+
Constructs all the necessary attributes for the EventPicker object.
|
|
32
|
+
|
|
33
|
+
Parameters:
|
|
34
|
+
taskBuffer : TaskBuffer
|
|
35
|
+
The task buffer that contains the jobs.
|
|
36
|
+
siteMapper : SiteMapper
|
|
37
|
+
The site mapper.
|
|
38
|
+
evpFileName : str
|
|
39
|
+
The name of the event picking file.
|
|
40
|
+
ignoreError : bool
|
|
41
|
+
Whether to ignore errors.
|
|
42
|
+
"""
|
|
43
|
+
self.task_buffer = taskBuffer
|
|
44
|
+
self.site_mapper = siteMapper
|
|
45
|
+
self.ignore_error = ignoreError
|
|
46
|
+
self.event_picking_file_name = evpFileName
|
|
47
|
+
# logger
|
|
48
|
+
self.logger = LogWrapper(_logger)
|
|
49
|
+
dataset_lifetime = self.task_buffer.getConfigValue("event_picker", "EVP_DATASET_LIFETIME")
|
|
50
|
+
if not dataset_lifetime:
|
|
51
|
+
dataset_lifetime = 28
|
|
52
|
+
self.pd2p = dyn_data_distributer.DynDataDistributer([], self.site_mapper, token=" ", dataset_lifetime=dataset_lifetime)
|
|
53
|
+
self.user_dataset_name = ""
|
|
54
|
+
self.creation_time = ""
|
|
55
|
+
self.params = ""
|
|
56
|
+
self.locked_by = ""
|
|
57
|
+
self.event_picking_file = None
|
|
58
|
+
self.user_task_name = ""
|
|
59
|
+
self.user_dn = ""
|
|
60
|
+
# JEDI
|
|
61
|
+
self.jedi_task_id = None
|
|
62
|
+
|
|
63
|
+
# end with error
|
|
64
|
+
def end_with_error(self, message: str):
|
|
65
|
+
"""
|
|
66
|
+
Ends the event picker with an error.
|
|
67
|
+
|
|
68
|
+
This method is called when an error occurs during the event picking process. It logs the error message,
|
|
69
|
+
unlocks and closes the event picking file, and removes it if the error is not to be ignored. It then uploads
|
|
70
|
+
the log and updates the task status in the task buffer.
|
|
71
|
+
|
|
72
|
+
Parameters:
|
|
73
|
+
message (str): The error message to be logged.
|
|
74
|
+
"""
|
|
75
|
+
self.logger.error(message)
|
|
76
|
+
# unlock evp file
|
|
77
|
+
try:
|
|
78
|
+
fcntl.flock(self.event_picking_file.fileno(), fcntl.LOCK_UN)
|
|
79
|
+
self.event_picking_file.close()
|
|
80
|
+
if not self.ignore_error:
|
|
81
|
+
# remove evp file
|
|
82
|
+
os.remove(self.event_picking_file_name)
|
|
83
|
+
except Exception:
|
|
84
|
+
pass
|
|
85
|
+
# upload log
|
|
86
|
+
if self.jedi_task_id is not None:
|
|
87
|
+
out_log = self.upload_log()
|
|
88
|
+
self.task_buffer.updateTaskErrorDialogJEDI(self.jedi_task_id, f"event picking failed. {out_log}")
|
|
89
|
+
# update task
|
|
90
|
+
if not self.ignore_error:
|
|
91
|
+
self.task_buffer.updateTaskModTimeJEDI(self.jedi_task_id, "tobroken")
|
|
92
|
+
self.logger.debug(out_log)
|
|
93
|
+
self.logger.debug(f"end {self.event_picking_file_name}")
|
|
94
|
+
|
|
95
|
+
# upload log
|
|
96
|
+
def upload_log(self) -> str:
|
|
97
|
+
"""
|
|
98
|
+
Uploads the log.
|
|
99
|
+
|
|
100
|
+
This method uploads the log of the EventPicker. It first checks if the jediTaskID is not None.
|
|
101
|
+
If it is None, it returns a message indicating that the jediTaskID could not be found.
|
|
102
|
+
Otherwise, it dumps the logger content to a string and attempts to upload it using the Client's upload_log method.
|
|
103
|
+
If the upload is not successful, it returns a message indicating the failure.
|
|
104
|
+
If the upload is successful and the output starts with "http", it returns a hyperlink to the log.
|
|
105
|
+
Otherwise, it returns the output of the upload_log method.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
str: The result of the log upload. This can be a message indicating an error, a hyperlink to the log, or the output of the upload_log method.
|
|
109
|
+
"""
|
|
110
|
+
if self.jedi_task_id is None:
|
|
111
|
+
return "cannot find jediTaskID"
|
|
112
|
+
str_msg = self.logger.dumpToString()
|
|
113
|
+
status, output = Client.uploadLog(str_msg, self.jedi_task_id)
|
|
114
|
+
if status != 0:
|
|
115
|
+
return f"failed to upload log with {status}."
|
|
116
|
+
if output.startswith("http"):
|
|
117
|
+
return f'<a href="{output}">log</a>'
|
|
118
|
+
return output
|
|
119
|
+
|
|
120
|
+
def get_options_from_file(self) -> dict:
|
|
121
|
+
"""
|
|
122
|
+
Gets options from the event picking file.
|
|
123
|
+
|
|
124
|
+
This method reads the event picking file and extracts options from it. The options are stored in a dictionary and returned.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
dict: A dictionary containing the options extracted from the event picking file.
|
|
128
|
+
"""
|
|
129
|
+
options = {
|
|
130
|
+
"runEvent": [],
|
|
131
|
+
"eventPickDataType": "",
|
|
132
|
+
"eventPickStreamName": "",
|
|
133
|
+
"eventPickDS": [],
|
|
134
|
+
"eventPickAmiTag": "",
|
|
135
|
+
"eventPickNumSites": 1,
|
|
136
|
+
"inputFileList": [],
|
|
137
|
+
"tagDS": [],
|
|
138
|
+
"tagQuery": "",
|
|
139
|
+
"tagStreamRef": "",
|
|
140
|
+
"runEvtGuidMap": {},
|
|
141
|
+
"ei_api": "",
|
|
142
|
+
"userName": "",
|
|
143
|
+
"userTaskName": "",
|
|
144
|
+
"userDatasetName": "",
|
|
145
|
+
"lockedBy": "",
|
|
146
|
+
"creationTime": "",
|
|
147
|
+
"params": "",
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for tmp_line in self.event_picking_file:
|
|
151
|
+
# regular expression to parse lines where a key and value are separated by an equal sign
|
|
152
|
+
tmp_match = re.search("^([^=]+)=(.+)$", tmp_line)
|
|
153
|
+
if tmp_match is not None:
|
|
154
|
+
key, value = tmp_match.groups()
|
|
155
|
+
if key in options:
|
|
156
|
+
if key == "runEvent":
|
|
157
|
+
tmp_run_event = value.split(",")
|
|
158
|
+
if len(tmp_run_event) == 2:
|
|
159
|
+
options[key].append(tmp_run_event)
|
|
160
|
+
elif key in ["eventPickDS", "inputFileList", "tagDS"]:
|
|
161
|
+
options[key] = value.split(",")
|
|
162
|
+
if key == "inputFileList":
|
|
163
|
+
options[key] = [item for item in options[key] if item != ""]
|
|
164
|
+
elif key == "eventPickNumSites":
|
|
165
|
+
options[key] = int(value)
|
|
166
|
+
elif key == "runEvtGuidMap":
|
|
167
|
+
options[key] = eval(value)
|
|
168
|
+
else:
|
|
169
|
+
options[key] = value
|
|
170
|
+
if key == "tagStreamRef" and not options[key].endswith("_ref"):
|
|
171
|
+
options[key] += "_ref"
|
|
172
|
+
return options
|
|
173
|
+
|
|
174
|
+
def get_jedi_task_id(self, options: dict) -> int:
|
|
175
|
+
"""
|
|
176
|
+
Gets the jediTaskID.
|
|
177
|
+
|
|
178
|
+
This method gets the jediTaskID from the task buffer using the user's DN and task name.
|
|
179
|
+
|
|
180
|
+
Parameters:
|
|
181
|
+
options (dict): A dictionary containing the options extracted from the event picking file.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
int: The jediTaskID.
|
|
185
|
+
"""
|
|
186
|
+
self.user_dn = options["userName"]
|
|
187
|
+
self.user_task_name = options["userTaskName"]
|
|
188
|
+
self.user_dataset_name = options["userDatasetName"]
|
|
189
|
+
self.locked_by = options["lockedBy"]
|
|
190
|
+
self.creation_time = options["creationTime"]
|
|
191
|
+
self.params = options["params"]
|
|
192
|
+
|
|
193
|
+
# extract task name
|
|
194
|
+
if self.user_task_name == "" and self.params != "":
|
|
195
|
+
# regular expression to parse the dataset name from a string where it is prefixed with --outDS and followed by either an equal sign or a space
|
|
196
|
+
tmp_match = re.search("--outDS(=| ) *([^ ]+)", self.params)
|
|
197
|
+
if tmp_match is not None:
|
|
198
|
+
self.user_task_name = tmp_match.group(2)
|
|
199
|
+
if not self.user_task_name.endswith("/"):
|
|
200
|
+
self.user_task_name += "/"
|
|
201
|
+
|
|
202
|
+
compact_dn = CoreUtils.clean_user_id(self.user_dn)
|
|
203
|
+
return self.task_buffer.getTaskIDwithTaskNameJEDI(compact_dn, self.user_task_name)
|
|
204
|
+
|
|
205
|
+
# main
|
|
206
|
+
def run(self) -> bool:
|
|
207
|
+
"""
|
|
208
|
+
Starts the event picker.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
bool: True if the event picker ran successfully, False otherwise.
|
|
212
|
+
"""
|
|
213
|
+
try:
|
|
214
|
+
self.logger.debug(f"start {self.event_picking_file_name}")
|
|
215
|
+
# lock event picking file
|
|
216
|
+
with open(self.event_picking_file_name) as self.event_picking_file:
|
|
217
|
+
try:
|
|
218
|
+
fcntl.flock(self.event_picking_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
219
|
+
except Exception:
|
|
220
|
+
# release
|
|
221
|
+
self.logger.debug(f"cannot lock {self.event_picking_file_name}")
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
options = self.get_options_from_file()
|
|
225
|
+
|
|
226
|
+
self.jedi_task_id = self.get_jedi_task_id(options)
|
|
227
|
+
|
|
228
|
+
tmp_ret, location_map, all_files = self.pd2p.convert_evt_run_to_datasets(
|
|
229
|
+
options["runEvent"],
|
|
230
|
+
options["eventPickDataType"],
|
|
231
|
+
options["eventPickStreamName"],
|
|
232
|
+
options["eventPickDS"],
|
|
233
|
+
options["eventPickAmiTag"],
|
|
234
|
+
options["runEvtGuidMap"],
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if not tmp_ret:
|
|
238
|
+
if "isFatal" in location_map and location_map["isFatal"] is True:
|
|
239
|
+
self.ignore_error = False
|
|
240
|
+
self.end_with_error("Failed to convert the run/event list to a dataset/file list")
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
# use only files in the list
|
|
244
|
+
if options["inputFileList"]:
|
|
245
|
+
all_files = [tmp_file for tmp_file in all_files if tmp_file["lfn"] in options["inputFileList"]]
|
|
246
|
+
|
|
247
|
+
# remove redundant CN from DN
|
|
248
|
+
tmp_dn = CoreUtils.get_id_from_dn(self.user_dn)
|
|
249
|
+
tmp_ret = self.pd2p.register_dataset_container_with_datasets(
|
|
250
|
+
self.user_dataset_name,
|
|
251
|
+
all_files,
|
|
252
|
+
location_map,
|
|
253
|
+
n_sites=options["eventPickNumSites"],
|
|
254
|
+
owner=tmp_dn,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if not tmp_ret:
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
fcntl.flock(self.event_picking_file.fileno(), fcntl.LOCK_UN)
|
|
261
|
+
os.remove(self.event_picking_file_name)
|
|
262
|
+
|
|
263
|
+
self.logger.debug(f"end {self.event_picking_file_name}")
|
|
264
|
+
return True
|
|
265
|
+
except Exception:
|
|
266
|
+
error_type, error_value = sys.exc_info()[:2]
|
|
267
|
+
self.end_with_error(f"Got exception {error_type}:{error_value} {traceback.format_exc()}")
|
|
268
|
+
return False
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
finish transferring jobs
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
import threading
|
|
11
|
+
from typing import List
|
|
12
|
+
|
|
13
|
+
from pandacommon.pandalogger.LogWrapper import LogWrapper
|
|
14
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
15
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
16
|
+
|
|
17
|
+
# logger
|
|
18
|
+
_logger = PandaLogger().getLogger("finisher")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Finisher(threading.Thread):
|
|
22
|
+
"""
|
|
23
|
+
A class used to finish transferring jobs
|
|
24
|
+
|
|
25
|
+
Attributes
|
|
26
|
+
----------
|
|
27
|
+
dataset : DatasetSpec
|
|
28
|
+
The dataset to be transferred
|
|
29
|
+
taskBuffer : TaskBuffer
|
|
30
|
+
The task buffer that contains the jobs
|
|
31
|
+
job : JobSpec, optional
|
|
32
|
+
The job to be transferred (default is None)
|
|
33
|
+
site : str, optional
|
|
34
|
+
The site where the job is to be transferred (default is None)
|
|
35
|
+
|
|
36
|
+
Methods
|
|
37
|
+
-------
|
|
38
|
+
run():
|
|
39
|
+
Starts the thread to finish transferring jobs
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# constructor
|
|
43
|
+
def __init__(self, taskBuffer, dataset, job: str = None, site: str = None):
|
|
44
|
+
"""
|
|
45
|
+
Constructs all the necessary attributes for the Finisher object.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
taskBuffer : TaskBuffer
|
|
50
|
+
The task buffer that contains the jobs
|
|
51
|
+
dataset : DatasetSpec
|
|
52
|
+
The dataset to be transferred
|
|
53
|
+
job : JobSpec, optional
|
|
54
|
+
The job to be transferred (default is None)
|
|
55
|
+
site : str, optional
|
|
56
|
+
The site where the job is to be transferred (default is None)
|
|
57
|
+
"""
|
|
58
|
+
threading.Thread.__init__(self)
|
|
59
|
+
self.dataset = dataset
|
|
60
|
+
self.task_buffer = taskBuffer
|
|
61
|
+
self.job = job
|
|
62
|
+
self.site = site
|
|
63
|
+
|
|
64
|
+
def create_json_doc(self, job, failed_files: List[str], no_out_files: List[str]):
|
|
65
|
+
"""
|
|
66
|
+
This function creates a JSON document for the jobs.
|
|
67
|
+
|
|
68
|
+
Parameters:
|
|
69
|
+
job (JobSpec): The job specification.
|
|
70
|
+
failed_files (list): List of failed files.
|
|
71
|
+
no_out_files (list): List of files with no output.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
str: The created JSON document as a string.
|
|
75
|
+
"""
|
|
76
|
+
json_dict = {}
|
|
77
|
+
for file in job.Files:
|
|
78
|
+
if file.type in ["output", "log"]:
|
|
79
|
+
# skip failed or no-output files
|
|
80
|
+
if file.lfn in failed_files + no_out_files:
|
|
81
|
+
continue
|
|
82
|
+
file_dict = {
|
|
83
|
+
"guid": file.GUID,
|
|
84
|
+
"fsize": file.fsize,
|
|
85
|
+
"full_lfn": file.lfn,
|
|
86
|
+
}
|
|
87
|
+
if file.checksum.startswith("ad:"):
|
|
88
|
+
file_dict["adler32"] = re.sub("^ad:", "", file.checksum)
|
|
89
|
+
else:
|
|
90
|
+
file_dict["md5sum"] = re.sub("^md5:", "", file.checksum)
|
|
91
|
+
json_dict[file.lfn] = file_dict
|
|
92
|
+
return json.dumps(json_dict)
|
|
93
|
+
|
|
94
|
+
def update_job_output_report(self, job, failed_files: List[str], no_out_files: List[str]):
|
|
95
|
+
"""
|
|
96
|
+
This function updates the job output report.
|
|
97
|
+
|
|
98
|
+
Parameters:
|
|
99
|
+
job (JobSpec): The job specification.
|
|
100
|
+
failed_files (list): List of failed files.
|
|
101
|
+
no_out_files (list): List of files with no output.
|
|
102
|
+
"""
|
|
103
|
+
json_data = self.create_json_doc(job, failed_files, no_out_files)
|
|
104
|
+
record_status = "finished" if not failed_files else "failed"
|
|
105
|
+
tmp_ret = self.task_buffer.updateJobOutputReport(
|
|
106
|
+
panda_id=job.PandaID,
|
|
107
|
+
attempt_nr=job.attemptNr,
|
|
108
|
+
data=json_data,
|
|
109
|
+
)
|
|
110
|
+
if not tmp_ret:
|
|
111
|
+
self.task_buffer.insertJobOutputReport(
|
|
112
|
+
panda_id=job.PandaID,
|
|
113
|
+
prod_source_label=job.prodSourceLabel,
|
|
114
|
+
job_status=record_status,
|
|
115
|
+
attempt_nr=job.attemptNr,
|
|
116
|
+
data=json_data,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def check_file_status(self, job):
|
|
120
|
+
"""
|
|
121
|
+
This function checks the status of the files for the job.
|
|
122
|
+
|
|
123
|
+
Parameters:
|
|
124
|
+
job (JobSpec): The job specification.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
tuple: A tuple containing three elements:
|
|
128
|
+
- bool: True if all files are ready, False otherwise.
|
|
129
|
+
- list: A list of failed files.
|
|
130
|
+
- list: A list of files with no output.
|
|
131
|
+
"""
|
|
132
|
+
tmp_log = LogWrapper(_logger, f"check_file_status-{naive_utcnow().isoformat('/')}")
|
|
133
|
+
failed_files = []
|
|
134
|
+
no_out_files = []
|
|
135
|
+
for file in job.Files:
|
|
136
|
+
if file.type in ("output", "log"):
|
|
137
|
+
if file.status == "failed":
|
|
138
|
+
failed_files.append(file.lfn)
|
|
139
|
+
elif file.status == "nooutput":
|
|
140
|
+
no_out_files.append(file.lfn)
|
|
141
|
+
elif file.status != "ready":
|
|
142
|
+
tmp_log.debug(f"Job: {job.PandaID} file:{file.lfn} {file.status} != ready")
|
|
143
|
+
return False, failed_files, no_out_files
|
|
144
|
+
return True, failed_files, no_out_files
|
|
145
|
+
|
|
146
|
+
# main
|
|
147
|
+
def run(self):
|
|
148
|
+
"""
|
|
149
|
+
Starts the thread to finish transferring jobs
|
|
150
|
+
"""
|
|
151
|
+
tmp_log = LogWrapper(_logger, f"run-{naive_utcnow().isoformat('/')}")
|
|
152
|
+
# start
|
|
153
|
+
try:
|
|
154
|
+
by_call_back = False
|
|
155
|
+
if self.job is not None:
|
|
156
|
+
tmp_log.debug(f"start: {self.job.PandaID}")
|
|
157
|
+
panda_ids = [self.job.PandaID]
|
|
158
|
+
jobs = [self.job]
|
|
159
|
+
else:
|
|
160
|
+
by_call_back = True
|
|
161
|
+
tmp_log.debug(f"start: {self.dataset.name}")
|
|
162
|
+
panda_ids = self.task_buffer.updateOutFilesReturnPandaIDs(self.dataset.name)
|
|
163
|
+
# set flag for T2 cleanup
|
|
164
|
+
self.dataset.status = "cleanup"
|
|
165
|
+
self.task_buffer.updateDatasets([self.dataset])
|
|
166
|
+
jobs = self.task_buffer.peekJobs(panda_ids, fromDefined=False, fromArchived=False, fromWaiting=False)
|
|
167
|
+
|
|
168
|
+
tmp_log.debug(f"IDs: {panda_ids}")
|
|
169
|
+
if len(panda_ids) != 0:
|
|
170
|
+
# loop over all jobs
|
|
171
|
+
for job in jobs:
|
|
172
|
+
if job is None or job.jobStatus != "transferring":
|
|
173
|
+
continue
|
|
174
|
+
job_ready, failed_files, no_out_files = self.check_file_status(job)
|
|
175
|
+
# finish job
|
|
176
|
+
if job_ready:
|
|
177
|
+
if by_call_back:
|
|
178
|
+
tmp_log.debug(f"Job: {job.PandaID} all files ready")
|
|
179
|
+
else:
|
|
180
|
+
tmp_log.debug(f"Job: {job.PandaID} all files checked with catalog")
|
|
181
|
+
# create JSON
|
|
182
|
+
try:
|
|
183
|
+
self.update_job_output_report(job, failed_files, no_out_files)
|
|
184
|
+
except Exception:
|
|
185
|
+
exc_type, value, _ = sys.exc_info()
|
|
186
|
+
tmp_log.error(f"Job: {job.PandaID} {exc_type} {value}")
|
|
187
|
+
tmp_log.debug(f"Job: {job.PandaID} status: {job.jobStatus}")
|
|
188
|
+
if self.job is None:
|
|
189
|
+
tmp_log.debug(f"end: {self.dataset.name}")
|
|
190
|
+
else:
|
|
191
|
+
tmp_log.debug(f"end: {self.job.PandaID}")
|
|
192
|
+
except Exception:
|
|
193
|
+
exc_type, value, _ = sys.exc_info()
|
|
194
|
+
tmp_log.error(f"run() : {exc_type} {value}")
|