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,552 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import time
|
|
4
|
+
import traceback
|
|
5
|
+
from re import error as ReError
|
|
6
|
+
|
|
7
|
+
from pandacommon.pandalogger.LogWrapper import LogWrapper
|
|
8
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
9
|
+
|
|
10
|
+
_logger = PandaLogger().getLogger("RetrialModule")
|
|
11
|
+
|
|
12
|
+
NO_RETRY = "no_retry"
|
|
13
|
+
INCREASE_MEM = "increase_memory"
|
|
14
|
+
LIMIT_RETRY = "limit_retry"
|
|
15
|
+
INCREASE_CPU = "increase_cputime"
|
|
16
|
+
INCREASE_MEM_XTIMES = "increase_memory_xtimes"
|
|
17
|
+
REDUCE_INPUT_PER_JOB = "reduce_input_per_job"
|
|
18
|
+
|
|
19
|
+
SYSTEM_ERROR_CLASS = "system"
|
|
20
|
+
NO_ERROR_CLASS = "unknown"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def timeit(method):
|
|
24
|
+
"""
|
|
25
|
+
Decorator function to time the execution time of any given method. Use as decorator.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def timed(*args, **kwargs):
|
|
29
|
+
tmp_log = LogWrapper(_logger, f"timed {method.__name__!r} ({args!r}, {kwargs!r})")
|
|
30
|
+
tmp_log.debug(f"Start")
|
|
31
|
+
|
|
32
|
+
ts = time.time()
|
|
33
|
+
result = method(*args, **kwargs)
|
|
34
|
+
te = time.time()
|
|
35
|
+
|
|
36
|
+
tmp_log.debug(f"Took {te - ts:.2f} sec")
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
return timed
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def safe_match(pattern, message):
|
|
43
|
+
"""
|
|
44
|
+
Wrapper around re.search with simple exception handling
|
|
45
|
+
"""
|
|
46
|
+
tmp_log = LogWrapper(_logger, f"safe_match")
|
|
47
|
+
|
|
48
|
+
matches = False
|
|
49
|
+
try:
|
|
50
|
+
matches = re.match(pattern, message, flags=re.DOTALL)
|
|
51
|
+
except ReError:
|
|
52
|
+
tmp_log.error(f"Regexp matching excepted. \nPattern: {pattern} \nString: {message}")
|
|
53
|
+
finally:
|
|
54
|
+
return matches
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def conditions_apply(
|
|
58
|
+
errordiag_job,
|
|
59
|
+
architecture_job,
|
|
60
|
+
release_job,
|
|
61
|
+
wqid_job,
|
|
62
|
+
errordiag_rule,
|
|
63
|
+
architecture_rule,
|
|
64
|
+
release_rule,
|
|
65
|
+
wqid_rule,
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
Checks that the error regexp, architecture, release and work queue of rule and job match,
|
|
69
|
+
only in case the attributes are defined for the rule
|
|
70
|
+
"""
|
|
71
|
+
tmp_log = LogWrapper(_logger, f"conditions_apply")
|
|
72
|
+
tmp_log.debug(f"Start {locals()}")
|
|
73
|
+
if (
|
|
74
|
+
(errordiag_rule and not safe_match(errordiag_rule, errordiag_job))
|
|
75
|
+
or (architecture_rule and architecture_rule != architecture_job)
|
|
76
|
+
or (release_rule and release_rule != release_job)
|
|
77
|
+
or (wqid_rule and wqid_rule != wqid_job)
|
|
78
|
+
):
|
|
79
|
+
tmp_log.debug("Leaving: False")
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
_logger.debug("Leaving: True")
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def compare_strictness(rule1, rule2):
|
|
87
|
+
"""
|
|
88
|
+
Return 1 if rule1 is stricter, 0 if equal, -1 if rule2 is stricter
|
|
89
|
+
"""
|
|
90
|
+
tmp_log = LogWrapper(_logger, f"compare_strictness")
|
|
91
|
+
tmp_log.debug("Start")
|
|
92
|
+
rule1_weight = 0
|
|
93
|
+
if rule1["architecture"]:
|
|
94
|
+
rule1_weight += 1
|
|
95
|
+
if rule1["release"]:
|
|
96
|
+
rule1_weight += 1
|
|
97
|
+
if rule1["wqid"]:
|
|
98
|
+
rule1_weight += 1
|
|
99
|
+
|
|
100
|
+
rule2_weight = 0
|
|
101
|
+
if rule2["architecture"]:
|
|
102
|
+
rule2_weight += 1
|
|
103
|
+
if rule2["release"]:
|
|
104
|
+
rule2_weight += 1
|
|
105
|
+
if rule2["wqid"]:
|
|
106
|
+
rule2_weight += 1
|
|
107
|
+
|
|
108
|
+
if rule1_weight > rule2_weight:
|
|
109
|
+
return 1
|
|
110
|
+
elif rule1_weight < rule2_weight:
|
|
111
|
+
return -1
|
|
112
|
+
else:
|
|
113
|
+
return 0
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def preprocess_rules(rules, error_diag_job, release_job, architecture_job, wqid_job):
|
|
117
|
+
"""
|
|
118
|
+
Do some preliminary validation of the applicable rules.
|
|
119
|
+
- Duplicate rules, (action=limit_retry, maxAttempt=5) vs (action=limit_retry, maxAttempt=7, release=X):
|
|
120
|
+
resolve to the most specific rule, in our example (action=limit_retry, maxAttempt=7, release=X)
|
|
121
|
+
- Inconsistent rules, e.g. (action=limit_retry, maxAttempt=5) vs (action=limit_retry, maxAttempt=7):
|
|
122
|
+
resolve into the strictest rule, in our example (limit_retry = 5)
|
|
123
|
+
- Bad intended rules, e.g. (action=limit_retry, maxAttempt=5) vs (action=limit_retry, maxAttempt=7, release=X):
|
|
124
|
+
"""
|
|
125
|
+
tmp_log = LogWrapper(_logger, f"preprocess_rules")
|
|
126
|
+
tmp_log.debug("Start")
|
|
127
|
+
filtered_rules = []
|
|
128
|
+
limit_retry_rule = {}
|
|
129
|
+
try:
|
|
130
|
+
# See if there is a INCREASE_MEM rule.
|
|
131
|
+
# The effect of INCREASE_MEM rules is the same, so take the first one that appears
|
|
132
|
+
for rule in rules:
|
|
133
|
+
if rule["action"] != INCREASE_MEM or not conditions_apply(
|
|
134
|
+
error_diag_job,
|
|
135
|
+
architecture_job,
|
|
136
|
+
release_job,
|
|
137
|
+
wqid_job,
|
|
138
|
+
rule["error_diag"],
|
|
139
|
+
rule["architecture"],
|
|
140
|
+
rule["release"],
|
|
141
|
+
rule["wqid"],
|
|
142
|
+
):
|
|
143
|
+
continue
|
|
144
|
+
else:
|
|
145
|
+
filtered_rules.append(rule)
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
# See if there is a INCREASE_MEM_XTIMES rule.
|
|
149
|
+
# The effect of INCREASE_MEM_XTIMES rules is the same, so take the first one that appears
|
|
150
|
+
for rule in rules:
|
|
151
|
+
if rule["action"] != INCREASE_MEM_XTIMES or not conditions_apply(
|
|
152
|
+
error_diag_job,
|
|
153
|
+
architecture_job,
|
|
154
|
+
release_job,
|
|
155
|
+
wqid_job,
|
|
156
|
+
rule["error_diag"],
|
|
157
|
+
rule["architecture"],
|
|
158
|
+
rule["release"],
|
|
159
|
+
rule["wqid"],
|
|
160
|
+
):
|
|
161
|
+
continue
|
|
162
|
+
else:
|
|
163
|
+
filtered_rules.append(rule)
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
# See if there is a INCREASE_CPU rule. The effect of INCREASE_CPU rules is the same, so take the first one that appears
|
|
167
|
+
for rule in rules:
|
|
168
|
+
if rule["action"] != INCREASE_CPU or not conditions_apply(
|
|
169
|
+
error_diag_job,
|
|
170
|
+
architecture_job,
|
|
171
|
+
release_job,
|
|
172
|
+
wqid_job,
|
|
173
|
+
rule["error_diag"],
|
|
174
|
+
rule["architecture"],
|
|
175
|
+
rule["release"],
|
|
176
|
+
rule["wqid"],
|
|
177
|
+
):
|
|
178
|
+
continue
|
|
179
|
+
else:
|
|
180
|
+
filtered_rules.append(rule)
|
|
181
|
+
break
|
|
182
|
+
|
|
183
|
+
# See if there is a REDUCE_INPUT_PER_JOB rule.
|
|
184
|
+
for rule in rules:
|
|
185
|
+
if rule["action"] != REDUCE_INPUT_PER_JOB or not conditions_apply(
|
|
186
|
+
error_diag_job,
|
|
187
|
+
architecture_job,
|
|
188
|
+
release_job,
|
|
189
|
+
wqid_job,
|
|
190
|
+
rule["error_diag"],
|
|
191
|
+
rule["architecture"],
|
|
192
|
+
rule["release"],
|
|
193
|
+
rule["wqid"],
|
|
194
|
+
):
|
|
195
|
+
continue
|
|
196
|
+
else:
|
|
197
|
+
filtered_rules.append(rule)
|
|
198
|
+
break
|
|
199
|
+
|
|
200
|
+
# See if there is a LIMIT_RETRY or NO_RETRY rule (they are handled together in this context). Take the narrowest rule, in case of draw take the strictest conditions
|
|
201
|
+
for rule in rules:
|
|
202
|
+
if rule["action"] not in (LIMIT_RETRY, NO_RETRY) or not conditions_apply(
|
|
203
|
+
error_diag_job,
|
|
204
|
+
architecture_job,
|
|
205
|
+
release_job,
|
|
206
|
+
wqid_job,
|
|
207
|
+
rule["error_diag"],
|
|
208
|
+
rule["architecture"],
|
|
209
|
+
rule["release"],
|
|
210
|
+
rule["wqid"],
|
|
211
|
+
):
|
|
212
|
+
continue
|
|
213
|
+
elif not limit_retry_rule:
|
|
214
|
+
limit_retry_rule = rule
|
|
215
|
+
else:
|
|
216
|
+
comparison = compare_strictness(rule, limit_retry_rule)
|
|
217
|
+
if comparison == 1:
|
|
218
|
+
limit_retry_rule = rule
|
|
219
|
+
elif comparison == 0:
|
|
220
|
+
limit_retry_rule["params"]["maxAttempt"] = min(
|
|
221
|
+
limit_retry_rule["params"]["maxAttempt"],
|
|
222
|
+
rule["params"]["maxAttempt"],
|
|
223
|
+
)
|
|
224
|
+
elif comparison == -1:
|
|
225
|
+
pass
|
|
226
|
+
except KeyError:
|
|
227
|
+
tmp_log.error(f"Rules are not properly defined. Rules: {rules}")
|
|
228
|
+
|
|
229
|
+
if limit_retry_rule:
|
|
230
|
+
filtered_rules.append(limit_retry_rule)
|
|
231
|
+
|
|
232
|
+
tmp_log.debug("Done")
|
|
233
|
+
return filtered_rules
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@timeit
|
|
237
|
+
def apply_retrial_rules(task_buffer, job, errors, attemptNr):
|
|
238
|
+
"""
|
|
239
|
+
Get rules from DB and applies them to a failed job. Actions can be:
|
|
240
|
+
- flag the job so it is not retried again (error code is a final state and retrying will not help)
|
|
241
|
+
- limit the number of retries
|
|
242
|
+
- increase the memory of a job if it failed because of insufficient memory
|
|
243
|
+
"""
|
|
244
|
+
job_id = job.PandaID
|
|
245
|
+
|
|
246
|
+
_logger.debug(f"Entered apply_retrial_rules for PandaID={job_id}, errors={errors}, attemptNr={attemptNr}")
|
|
247
|
+
|
|
248
|
+
retrial_rules = task_buffer.getRetrialRules()
|
|
249
|
+
_logger.debug("Back from getRetrialRules")
|
|
250
|
+
if not retrial_rules:
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
acted_on_job = False
|
|
255
|
+
for error in errors:
|
|
256
|
+
# in case of multiple errors for a job (e.g. pilot error + exe error) we will only apply one action
|
|
257
|
+
if acted_on_job:
|
|
258
|
+
break
|
|
259
|
+
|
|
260
|
+
error_source = error["source"]
|
|
261
|
+
error_code = error["error_code"]
|
|
262
|
+
error_diag = error["error_diag"]
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
error_code = int(error_code)
|
|
266
|
+
except ValueError:
|
|
267
|
+
if error_code != "NULL":
|
|
268
|
+
_logger.error(f"Error code ({error_code}) can not be casted to int")
|
|
269
|
+
continue
|
|
270
|
+
try:
|
|
271
|
+
rule = retrial_rules[error_source][error_code]
|
|
272
|
+
except KeyError as e:
|
|
273
|
+
_logger.debug(f"Retry rule does not apply for jobID {job_id}, attemptNr {attemptNr}, failed with {errors}. (Exception {e})")
|
|
274
|
+
continue
|
|
275
|
+
|
|
276
|
+
applicable_rules = preprocess_rules(rule, error_diag, job.AtlasRelease, job.cmtConfig, job.workQueue_ID)
|
|
277
|
+
_logger.debug(f"Applicable rules for PandaID={job_id}: {applicable_rules}")
|
|
278
|
+
for rule in applicable_rules:
|
|
279
|
+
try:
|
|
280
|
+
error_id = rule["error_id"]
|
|
281
|
+
error_diag_rule = rule["error_diag"]
|
|
282
|
+
action = rule["action"]
|
|
283
|
+
parameters = rule["params"]
|
|
284
|
+
architecture = rule["architecture"] # cmtconfig
|
|
285
|
+
release = rule["release"] # transHome
|
|
286
|
+
wqid = rule["wqid"] # work queue ID
|
|
287
|
+
active = rule["active"] # If False, don't apply rule, only log
|
|
288
|
+
|
|
289
|
+
_logger.debug(
|
|
290
|
+
"error_diag_rule {0}, action {1}, parameters {2}, architecture {3}, release {4}, wqid {5}, active {6}".format(
|
|
291
|
+
error_diag_rule,
|
|
292
|
+
action,
|
|
293
|
+
parameters,
|
|
294
|
+
architecture,
|
|
295
|
+
release,
|
|
296
|
+
wqid,
|
|
297
|
+
active,
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
_logger.debug(f"Processing rule {rule} for jobID {job_id}, error_source {error_source}, error_code {error_code}, attemptNr {attemptNr}")
|
|
302
|
+
if not conditions_apply(
|
|
303
|
+
error_diag,
|
|
304
|
+
job.cmtConfig,
|
|
305
|
+
job.AtlasRelease,
|
|
306
|
+
job.workQueue_ID,
|
|
307
|
+
error_diag_rule,
|
|
308
|
+
architecture,
|
|
309
|
+
release,
|
|
310
|
+
wqid,
|
|
311
|
+
):
|
|
312
|
+
_logger.debug(
|
|
313
|
+
f"Skipped rule {rule}. cmtConfig ({architecture} : {job.cmtConfig}) or Release ({release} : {job.AtlasRelease}) did NOT match"
|
|
314
|
+
)
|
|
315
|
+
continue
|
|
316
|
+
|
|
317
|
+
if action == NO_RETRY:
|
|
318
|
+
if active:
|
|
319
|
+
task_buffer.setNoRetry(job_id, job.jediTaskID, job.Files)
|
|
320
|
+
message = (
|
|
321
|
+
f"action=setNoRetry for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
|
|
322
|
+
f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
|
|
323
|
+
f"Error/action active={active} error_id={error_id} )"
|
|
324
|
+
)
|
|
325
|
+
acted_on_job = True
|
|
326
|
+
_logger.info(message)
|
|
327
|
+
|
|
328
|
+
elif action == LIMIT_RETRY:
|
|
329
|
+
try:
|
|
330
|
+
if active:
|
|
331
|
+
task_buffer.setMaxAttempt(
|
|
332
|
+
job_id,
|
|
333
|
+
job.jediTaskID,
|
|
334
|
+
job.Files,
|
|
335
|
+
int(parameters["maxAttempt"]),
|
|
336
|
+
)
|
|
337
|
+
message = (
|
|
338
|
+
f"action=setMaxAttempt for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} maxAttempt={int(parameters['maxAttempt'])} "
|
|
339
|
+
f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
|
|
340
|
+
f"Error/action active={active} error_id={error_id} )"
|
|
341
|
+
)
|
|
342
|
+
acted_on_job = True
|
|
343
|
+
_logger.info(message)
|
|
344
|
+
except (KeyError, ValueError):
|
|
345
|
+
_logger.error(f"Inconsistent definition of limit_retry rule - maxAttempt not defined. parameters: {parameters}")
|
|
346
|
+
|
|
347
|
+
elif action == INCREASE_MEM:
|
|
348
|
+
try:
|
|
349
|
+
if active:
|
|
350
|
+
task_buffer.increaseRamLimitJobJEDI(job, job.minRamCount, job.jediTaskID)
|
|
351
|
+
message = (
|
|
352
|
+
f"action=increaseRAMLimit for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
|
|
353
|
+
f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
|
|
354
|
+
f"Error/action active={active} error_id={error_id} )"
|
|
355
|
+
)
|
|
356
|
+
acted_on_job = True
|
|
357
|
+
_logger.info(message)
|
|
358
|
+
except Exception:
|
|
359
|
+
error_type, error_value = sys.exc_info()[:2]
|
|
360
|
+
_logger.error(f"Failed to increase RAM limit : {error_type} {error_value}")
|
|
361
|
+
|
|
362
|
+
elif action == INCREASE_MEM_XTIMES:
|
|
363
|
+
try:
|
|
364
|
+
if active:
|
|
365
|
+
task_buffer.increaseRamLimitJobJEDI_xtimes(job, job.minRamCount, job.jediTaskID, attemptNr)
|
|
366
|
+
message = (
|
|
367
|
+
f"action=increaseRAMLimit_xtimes for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
|
|
368
|
+
f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
|
|
369
|
+
f"Error/action active={active} error_id={error_id} )"
|
|
370
|
+
)
|
|
371
|
+
acted_on_job = True
|
|
372
|
+
_logger.info(message)
|
|
373
|
+
except Exception:
|
|
374
|
+
error_type, error_value = sys.exc_info()[:2]
|
|
375
|
+
_logger.error(f"Failed to increase RAM xtimes limit : {error_type} {error_value}")
|
|
376
|
+
|
|
377
|
+
elif action == INCREASE_CPU:
|
|
378
|
+
new_cpu_time = None
|
|
379
|
+
try:
|
|
380
|
+
# update the task CPU time based on the failed job
|
|
381
|
+
if active:
|
|
382
|
+
new_cpu_time, new_cpu_time_unit = task_buffer.initialize_cpu_time_task(
|
|
383
|
+
job_id, job.jediTaskID, job.computingSite, job.Files, active
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
message = (
|
|
387
|
+
f"action=increaseCpuTime triggered CPU time increase based on failed job PandaID={job_id} "
|
|
388
|
+
f"jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} (active={active} ), new_cpu_time={new_cpu_time} new_cpu_time_unit={new_cpu_time_unit}. "
|
|
389
|
+
f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
|
|
390
|
+
f"Error/action active={active} error_id={error_id} )"
|
|
391
|
+
)
|
|
392
|
+
_logger.info(message)
|
|
393
|
+
except Exception:
|
|
394
|
+
error_type, error_value = sys.exc_info()[:2]
|
|
395
|
+
_logger.error(f"Failed to increase CPU-Time based on failed jobs: {error_type} {error_value}")
|
|
396
|
+
|
|
397
|
+
# if the CPU time was not increased based on one failed job, we request recalculation of task parameters
|
|
398
|
+
if not new_cpu_time:
|
|
399
|
+
try:
|
|
400
|
+
# request recalculation of task parameters and see if it applied
|
|
401
|
+
applied = False
|
|
402
|
+
|
|
403
|
+
if active:
|
|
404
|
+
rowcount = task_buffer.requestTaskParameterRecalculation(job.jediTaskID)
|
|
405
|
+
else:
|
|
406
|
+
rowcount = 0
|
|
407
|
+
|
|
408
|
+
if rowcount:
|
|
409
|
+
applied = True
|
|
410
|
+
|
|
411
|
+
message = (
|
|
412
|
+
f"action=increaseCpuTime requested recalculation of task parameters for PandaID={job_id} "
|
|
413
|
+
f"jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} (active={active} ), applied={applied}. "
|
|
414
|
+
f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_diag_rule}. "
|
|
415
|
+
f"Error/action active={active} error_id={error_id} )"
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
acted_on_job = True
|
|
419
|
+
_logger.info(message)
|
|
420
|
+
except Exception:
|
|
421
|
+
error_type, error_value = sys.exc_info()[:2]
|
|
422
|
+
_logger.error(f"Failed to increase CPU-Time : {error_type} {error_value}")
|
|
423
|
+
|
|
424
|
+
elif action == REDUCE_INPUT_PER_JOB:
|
|
425
|
+
try:
|
|
426
|
+
applied = False
|
|
427
|
+
if active:
|
|
428
|
+
applied = task_buffer.reduce_input_per_job(
|
|
429
|
+
job.PandaID, job.jediTaskID, job.attemptNr, parameters.get("excluded_rules"), parameters.get("steps")
|
|
430
|
+
)
|
|
431
|
+
message = (
|
|
432
|
+
f"action=reduceInputPerJob for PandaID={job_id} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} applied={applied} "
|
|
433
|
+
f"( ErrorSource={error_source} ErrorCode={error_code} ErrorDiag: {error_code}. "
|
|
434
|
+
f"Error/action active={active} error_id={error_id} )"
|
|
435
|
+
)
|
|
436
|
+
acted_on_job = True
|
|
437
|
+
_logger.info(message)
|
|
438
|
+
except Exception as e:
|
|
439
|
+
_logger.error(f"Failed to reduce input per job : {e} {traceback.format_exc()}")
|
|
440
|
+
_logger.error(traceback.format_exc())
|
|
441
|
+
|
|
442
|
+
_logger.debug(f"Finished rule {rule} for PandaID={job_id} error_source={error_source} error_code={error_code} attemptNr={attemptNr}")
|
|
443
|
+
|
|
444
|
+
except KeyError:
|
|
445
|
+
_logger.error(f"Rule was missing some field(s). Rule: {rule}")
|
|
446
|
+
|
|
447
|
+
except KeyError as e:
|
|
448
|
+
_logger.debug(f"No retrial rules to apply for jobID {job_id}, attemptNr {attemptNr}, failed with {errors}. (Exception {e})")
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def get_job_error_details(job_spec):
|
|
452
|
+
# Possible error types
|
|
453
|
+
error_sources = ["pilotError", "exeError", "supError", "ddmError", "brokerageError", "jobDispatcherError", "taskBufferError"]
|
|
454
|
+
job_id = job_spec.PandaID
|
|
455
|
+
job_errors = []
|
|
456
|
+
|
|
457
|
+
tmp_log = LogWrapper(_logger, f"get_job_error_details PandaID={job_id}")
|
|
458
|
+
|
|
459
|
+
tmp_log.debug(f"Starting for status {job_spec.jobStatus}")
|
|
460
|
+
|
|
461
|
+
# Get the error codes and messages that are set for the job
|
|
462
|
+
for source in error_sources:
|
|
463
|
+
error_code = getattr(job_spec, source + "Code", None) # 1099
|
|
464
|
+
error_diag = getattr(job_spec, source + "Diag", None) # "Test error message"
|
|
465
|
+
error_source = source + "Code" # pilotErrorCode
|
|
466
|
+
if error_code:
|
|
467
|
+
job_errors.append((error_code, error_diag, error_source))
|
|
468
|
+
|
|
469
|
+
if job_errors:
|
|
470
|
+
tmp_log.debug(f"Job has following error codes: {job_errors}")
|
|
471
|
+
else:
|
|
472
|
+
tmp_log.debug("Job has no error codes")
|
|
473
|
+
|
|
474
|
+
return job_errors
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def classify_error(task_buffer, job_id, job_errors):
|
|
478
|
+
# Get the confirmed error classification rules
|
|
479
|
+
tmp_log = LogWrapper(_logger, f"classify_error PandaID={job_id}")
|
|
480
|
+
|
|
481
|
+
# Query the error classification rules from the database
|
|
482
|
+
sql = "SELECT id, error_source, error_code, error_diag, error_class, active FROM ATLAS_PANDA.ERROR_CLASSIFICATION"
|
|
483
|
+
var_map = []
|
|
484
|
+
status, rules = task_buffer.querySQLS(sql, var_map)
|
|
485
|
+
if not rules:
|
|
486
|
+
tmp_log.debug(f"No error classification rules defined in the database")
|
|
487
|
+
return None
|
|
488
|
+
|
|
489
|
+
# Iterate job errors and rules to find a match
|
|
490
|
+
for job_error in job_errors:
|
|
491
|
+
err_code, err_diag, err_source = job_error
|
|
492
|
+
for rule in rules:
|
|
493
|
+
rule_id, rule_source, rule_code, rule_diag, rule_class, rule_active = rule
|
|
494
|
+
|
|
495
|
+
if (
|
|
496
|
+
(rule_source and err_source is not None and rule_source == err_source)
|
|
497
|
+
and (rule_code and err_code is not None and rule_code == err_code)
|
|
498
|
+
and (rule_diag and err_diag is not None and safe_match(rule_diag, err_diag))
|
|
499
|
+
):
|
|
500
|
+
active = rule_active == "Y"
|
|
501
|
+
tmp_log.debug(f"Job classified with rule {rule_id}: ({err_source}, {err_code}, {err_diag}) as {rule_class} (active: {active})")
|
|
502
|
+
return rule_id, rule_source, rule_code, rule_diag, rule_class, active
|
|
503
|
+
|
|
504
|
+
tmp_log.debug(f"No matching rule found")
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
@timeit
|
|
509
|
+
def apply_error_classification_logic(task_buffer, job):
|
|
510
|
+
tmp_log = LogWrapper(_logger, f"apply_error_classification_logic")
|
|
511
|
+
|
|
512
|
+
# Find the error source and getting the code, diag, and source
|
|
513
|
+
job_errors = get_job_error_details(job)
|
|
514
|
+
|
|
515
|
+
# Classify the error
|
|
516
|
+
ret = classify_error(task_buffer, job.PandaID, job_errors)
|
|
517
|
+
if not ret:
|
|
518
|
+
return
|
|
519
|
+
|
|
520
|
+
# Unpack the classification
|
|
521
|
+
rule_id, rule_source, rule_code, rule_diag, rule_class, active = ret
|
|
522
|
+
|
|
523
|
+
# System errors should not count towards the user's max attempt. We increase the max attempt, since we can't repeat attempt numbers
|
|
524
|
+
if rule_class == SYSTEM_ERROR_CLASS:
|
|
525
|
+
# Structure the message for logstash parsing and monitoring.
|
|
526
|
+
# We are using a large offset in the rule IDs in the database to avoid overlapping IDs with the retry module
|
|
527
|
+
message = (
|
|
528
|
+
f"action=increase_max_failure for PandaID={job.PandaID} jediTaskID={job.jediTaskID} prodSourceLabel={job.prodSourceLabel} "
|
|
529
|
+
f"( ErrorSource={rule_source} ErrorCode={rule_code} ErrorDiag: {rule_diag}. "
|
|
530
|
+
f"Error/action active={active} error_id={rule_id} )"
|
|
531
|
+
)
|
|
532
|
+
tmp_log.info(message)
|
|
533
|
+
|
|
534
|
+
# Apply the rule only for active errors
|
|
535
|
+
if active:
|
|
536
|
+
task_buffer.increase_max_failure(job.PandaID, job.jediTaskID, job.Files)
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def job_failure_postprocessing(task_buffer, job_id, errors, attempt_number):
|
|
540
|
+
"""
|
|
541
|
+
Entry point for job failure post-processing. This includes applying the retry rules and error classification logic.
|
|
542
|
+
"""
|
|
543
|
+
# get the job spec from the ID
|
|
544
|
+
job = task_buffer.peekJobs([job_id], fromDefined=False, fromArchived=True, fromWaiting=False)[0]
|
|
545
|
+
if not job:
|
|
546
|
+
return
|
|
547
|
+
|
|
548
|
+
# Run the retry module on the job
|
|
549
|
+
apply_retrial_rules(task_buffer, job, errors, attempt_number)
|
|
550
|
+
|
|
551
|
+
# Apply any logic related to error classification
|
|
552
|
+
apply_error_classification_logic(task_buffer, job)
|