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,189 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import socket
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
# logger
|
|
10
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
11
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
12
|
+
|
|
13
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
14
|
+
from pandajedi.jedicore.ThreadUtils import ListWithLock, ThreadPool, WorkerThread
|
|
15
|
+
from pandaserver.taskbuffer.DataCarousel import (
|
|
16
|
+
DataCarouselInterface,
|
|
17
|
+
DataCarouselRequestSpec,
|
|
18
|
+
DataCarouselRequestStatus,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from .WatchDogBase import WatchDogBase
|
|
22
|
+
|
|
23
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
24
|
+
|
|
25
|
+
# ==============================================================
|
|
26
|
+
|
|
27
|
+
# lock time limits in minutes
|
|
28
|
+
StageDCRequests_LOCK_TIME_MINUTES = 5
|
|
29
|
+
CheckDCRequests_LOCK_TIME_MINUTES = 5
|
|
30
|
+
KeepRulesAlive_LOCK_TIME_MINUTES = 60
|
|
31
|
+
CleanDCRequests_LOCK_TIME_MINUTES = 1440
|
|
32
|
+
RescuePendingTasks_LOCK_TIME_MINUTES = 120
|
|
33
|
+
|
|
34
|
+
# ==============================================================
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AtlasDataCarouselWatchDog(WatchDogBase):
|
|
38
|
+
"""
|
|
39
|
+
Data Carousel watchdog for ATLAS
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# constructor
|
|
43
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
44
|
+
WatchDogBase.__init__(self, taskBufferIF, ddmIF)
|
|
45
|
+
self.vo = "atlas"
|
|
46
|
+
self.ddmIF = ddmIF.getInterface(self.vo)
|
|
47
|
+
self.data_carousel_interface = DataCarouselInterface(taskBufferIF, self.ddmIF)
|
|
48
|
+
|
|
49
|
+
def doStageDCRequests(self):
|
|
50
|
+
"""
|
|
51
|
+
Action to get queued DC requests and start staging
|
|
52
|
+
"""
|
|
53
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doStageDCRequests")
|
|
54
|
+
tmpLog.debug("start")
|
|
55
|
+
try:
|
|
56
|
+
# timer start
|
|
57
|
+
t0 = time.monotonic()
|
|
58
|
+
lock_time_sec = StageDCRequests_LOCK_TIME_MINUTES * 60
|
|
59
|
+
# watchdog lock
|
|
60
|
+
got_lock = self.get_process_lock("AtlasDataCarousDog.doStageDCReq", timeLimit=StageDCRequests_LOCK_TIME_MINUTES)
|
|
61
|
+
if not got_lock:
|
|
62
|
+
tmpLog.debug("locked by another watchdog process. Skipped")
|
|
63
|
+
return
|
|
64
|
+
tmpLog.debug("got watchdog lock")
|
|
65
|
+
# get DC requests to stage
|
|
66
|
+
to_stage_list = self.data_carousel_interface.get_requests_to_stage()
|
|
67
|
+
# stage the requests
|
|
68
|
+
for dc_req_spec, extra_params in to_stage_list:
|
|
69
|
+
self.data_carousel_interface.stage_request(dc_req_spec, extra_params=extra_params)
|
|
70
|
+
tmpLog.debug(f"stage request_id={dc_req_spec.request_id} dataset={dc_req_spec.dataset}")
|
|
71
|
+
# check timer
|
|
72
|
+
t1 = time.monotonic()
|
|
73
|
+
if t1 - t0 > max(lock_time_sec - 60, lock_time_sec * 0.8):
|
|
74
|
+
tmpLog.debug("approaching lock time limit. Stop staging more requests")
|
|
75
|
+
break
|
|
76
|
+
# done
|
|
77
|
+
tmpLog.debug("done")
|
|
78
|
+
except Exception:
|
|
79
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
80
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
81
|
+
|
|
82
|
+
def doCheckDCRequests(self):
|
|
83
|
+
"""
|
|
84
|
+
Action to check active DC requests
|
|
85
|
+
"""
|
|
86
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doCheckDCRequests")
|
|
87
|
+
tmpLog.debug("start")
|
|
88
|
+
try:
|
|
89
|
+
# watchdog lock
|
|
90
|
+
got_lock = self.get_process_lock("AtlasDataCarousDog.doCheckDCRequests", timeLimit=CheckDCRequests_LOCK_TIME_MINUTES)
|
|
91
|
+
if not got_lock:
|
|
92
|
+
tmpLog.debug("locked by another watchdog process. Skipped")
|
|
93
|
+
return
|
|
94
|
+
tmpLog.debug("got watchdog lock")
|
|
95
|
+
# check for staging
|
|
96
|
+
self.data_carousel_interface.check_staging_requests()
|
|
97
|
+
# resume tasks with requests in staging
|
|
98
|
+
self.data_carousel_interface.resume_tasks_from_staging()
|
|
99
|
+
# done
|
|
100
|
+
tmpLog.debug(f"done")
|
|
101
|
+
except Exception:
|
|
102
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
103
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
104
|
+
|
|
105
|
+
def doKeepRulesAlive(self):
|
|
106
|
+
"""
|
|
107
|
+
Action to keep DDM staging rules alive when tasks running
|
|
108
|
+
"""
|
|
109
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doKeepRulesAlive")
|
|
110
|
+
tmpLog.debug("start")
|
|
111
|
+
try:
|
|
112
|
+
# watchdog lock
|
|
113
|
+
got_lock = self.get_process_lock("AtlasDataCarousDog.doKeepRulesAlive", timeLimit=KeepRulesAlive_LOCK_TIME_MINUTES)
|
|
114
|
+
if not got_lock:
|
|
115
|
+
tmpLog.debug("locked by another watchdog process. Skipped")
|
|
116
|
+
return
|
|
117
|
+
tmpLog.debug("got watchdog lock")
|
|
118
|
+
# get requests of active tasks
|
|
119
|
+
self.data_carousel_interface.keep_alive_ddm_rules()
|
|
120
|
+
# done
|
|
121
|
+
tmpLog.debug(f"done")
|
|
122
|
+
except Exception:
|
|
123
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
124
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
125
|
+
|
|
126
|
+
def doCleanDCRequests(self):
|
|
127
|
+
"""
|
|
128
|
+
Action to clean up old DC requests in DB table
|
|
129
|
+
"""
|
|
130
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doCleanDCRequests")
|
|
131
|
+
tmpLog.debug("start")
|
|
132
|
+
try:
|
|
133
|
+
# watchdog lock
|
|
134
|
+
got_lock = self.get_process_lock("AtlasDataCarousDog.doCleanDCReq", timeLimit=CleanDCRequests_LOCK_TIME_MINUTES)
|
|
135
|
+
if not got_lock:
|
|
136
|
+
tmpLog.debug("locked by another watchdog process. Skipped")
|
|
137
|
+
return
|
|
138
|
+
tmpLog.debug("got watchdog lock")
|
|
139
|
+
# clean up
|
|
140
|
+
self.data_carousel_interface.clean_up_requests()
|
|
141
|
+
# done
|
|
142
|
+
tmpLog.debug("done")
|
|
143
|
+
except Exception:
|
|
144
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
145
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
146
|
+
|
|
147
|
+
def doRescuePendingTasks(self):
|
|
148
|
+
"""
|
|
149
|
+
Action to rescue pending tasks stuck due to never updated staged files about previously done DC requests
|
|
150
|
+
"""
|
|
151
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doRescuePendingTasks")
|
|
152
|
+
tmpLog.debug("start")
|
|
153
|
+
try:
|
|
154
|
+
# watchdog lock
|
|
155
|
+
got_lock = self.get_process_lock("AtlasDataCarousDog.doRescuePendingTasks", timeLimit=RescuePendingTasks_LOCK_TIME_MINUTES)
|
|
156
|
+
if not got_lock:
|
|
157
|
+
tmpLog.debug("locked by another watchdog process. Skipped")
|
|
158
|
+
return
|
|
159
|
+
tmpLog.debug("got watchdog lock")
|
|
160
|
+
# rescue pending tasks
|
|
161
|
+
self.data_carousel_interface.rescue_pending_tasks_with_done_requests()
|
|
162
|
+
# done
|
|
163
|
+
tmpLog.debug(f"done")
|
|
164
|
+
except Exception:
|
|
165
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
166
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
167
|
+
|
|
168
|
+
# main
|
|
169
|
+
def doAction(self):
|
|
170
|
+
try:
|
|
171
|
+
# get logger
|
|
172
|
+
origTmpLog = MsgWrapper(logger)
|
|
173
|
+
origTmpLog.debug("start")
|
|
174
|
+
# clean up old DC requests
|
|
175
|
+
self.doCleanDCRequests()
|
|
176
|
+
# keep staging rules alive
|
|
177
|
+
self.doKeepRulesAlive()
|
|
178
|
+
# check staging requests
|
|
179
|
+
self.doCheckDCRequests()
|
|
180
|
+
# stage queued requests
|
|
181
|
+
self.doStageDCRequests()
|
|
182
|
+
# rescue pending tasks
|
|
183
|
+
# self.doRescuePendingTasks()
|
|
184
|
+
except Exception:
|
|
185
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
186
|
+
origTmpLog.error(f"failed with {errtype} {errvalue}")
|
|
187
|
+
# return
|
|
188
|
+
origTmpLog.debug("done")
|
|
189
|
+
return self.SC_SUCCEEDED
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import socket
|
|
5
|
+
import sys
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
9
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
10
|
+
|
|
11
|
+
from pandajedi.jedicore.MsgWrapper import MsgWrapper
|
|
12
|
+
from pandajedi.jedicore.ThreadUtils import ListWithLock, ThreadPool, WorkerThread
|
|
13
|
+
|
|
14
|
+
from .WatchDogBase import WatchDogBase
|
|
15
|
+
|
|
16
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# data locality updater for ATLAS
|
|
20
|
+
class AtlasDataLocalityUpdaterWatchDog(WatchDogBase):
|
|
21
|
+
# constructor
|
|
22
|
+
def __init__(self, taskBufferIF, ddmIF):
|
|
23
|
+
WatchDogBase.__init__(self, taskBufferIF, ddmIF)
|
|
24
|
+
self.pid = f"{socket.getfqdn().split('.')[0]}-{os.getpid()}-dog"
|
|
25
|
+
self.vo = "atlas"
|
|
26
|
+
self.ddmIF = ddmIF.getInterface(self.vo)
|
|
27
|
+
|
|
28
|
+
# get list-with-lock of datasets to update
|
|
29
|
+
def get_datasets_list(self):
|
|
30
|
+
datasets_list = self.taskBufferIF.get_tasks_inputdatasets_JEDI(self.vo)
|
|
31
|
+
datasets_list = ListWithLock(datasets_list)
|
|
32
|
+
# return
|
|
33
|
+
return datasets_list
|
|
34
|
+
|
|
35
|
+
# update data locality records to DB table
|
|
36
|
+
def doUpdateDataLocality(self):
|
|
37
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doUpdateDataLocality")
|
|
38
|
+
tmpLog.debug("start")
|
|
39
|
+
try:
|
|
40
|
+
# lock
|
|
41
|
+
got_lock = self.taskBufferIF.lockProcess_JEDI(
|
|
42
|
+
vo=self.vo,
|
|
43
|
+
prodSourceLabel="default",
|
|
44
|
+
cloud=None,
|
|
45
|
+
workqueue_id=None,
|
|
46
|
+
resource_name=None,
|
|
47
|
+
component="AtlasDataLocaUpdDog.doUpdateDataLoca",
|
|
48
|
+
pid=self.pid,
|
|
49
|
+
timeLimit=240,
|
|
50
|
+
)
|
|
51
|
+
if not got_lock:
|
|
52
|
+
tmpLog.debug("locked by another process. Skipped")
|
|
53
|
+
return
|
|
54
|
+
tmpLog.debug("got lock")
|
|
55
|
+
# get list of datasets
|
|
56
|
+
datasets_list = self.get_datasets_list()
|
|
57
|
+
tmpLog.debug(f"got {len(datasets_list)} datasets to update")
|
|
58
|
+
# make thread pool
|
|
59
|
+
thread_pool = ThreadPool()
|
|
60
|
+
# make workers
|
|
61
|
+
n_workers = 8
|
|
62
|
+
for _ in range(n_workers):
|
|
63
|
+
thr = DataLocalityUpdaterThread(
|
|
64
|
+
taskDsList=datasets_list, threadPool=thread_pool, taskbufferIF=self.taskBufferIF, ddmIF=self.ddmIF, pid=self.pid, loggerObj=tmpLog
|
|
65
|
+
)
|
|
66
|
+
thr.start()
|
|
67
|
+
tmpLog.debug(f"started {n_workers} updater workers")
|
|
68
|
+
# join
|
|
69
|
+
thread_pool.join()
|
|
70
|
+
# done
|
|
71
|
+
tmpLog.debug("done")
|
|
72
|
+
except Exception:
|
|
73
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
74
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
75
|
+
|
|
76
|
+
# clean up old data locality records in DB table
|
|
77
|
+
def doCleanDataLocality(self):
|
|
78
|
+
tmpLog = MsgWrapper(logger, " #ATM #KV doCleanDataLocality")
|
|
79
|
+
tmpLog.debug("start")
|
|
80
|
+
try:
|
|
81
|
+
# lock
|
|
82
|
+
got_lock = self.taskBufferIF.lockProcess_JEDI(
|
|
83
|
+
vo=self.vo,
|
|
84
|
+
prodSourceLabel="default",
|
|
85
|
+
cloud=None,
|
|
86
|
+
workqueue_id=None,
|
|
87
|
+
resource_name=None,
|
|
88
|
+
component="AtlasDataLocaUpdDog.doCleanDataLoca",
|
|
89
|
+
pid=self.pid,
|
|
90
|
+
timeLimit=1440,
|
|
91
|
+
)
|
|
92
|
+
if not got_lock:
|
|
93
|
+
tmpLog.debug("locked by another process. Skipped")
|
|
94
|
+
return
|
|
95
|
+
tmpLog.debug("got lock")
|
|
96
|
+
# lifetime of records
|
|
97
|
+
record_lifetime_hours = 24
|
|
98
|
+
# run
|
|
99
|
+
now_timestamp = naive_utcnow()
|
|
100
|
+
before_timestamp = now_timestamp - datetime.timedelta(hours=record_lifetime_hours)
|
|
101
|
+
n_rows = self.taskBufferIF.deleteOutdatedDatasetLocality_JEDI(before_timestamp)
|
|
102
|
+
tmpLog.info(f"cleaned up {n_rows} records")
|
|
103
|
+
# done
|
|
104
|
+
tmpLog.debug("done")
|
|
105
|
+
except Exception:
|
|
106
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
107
|
+
tmpLog.error(f"failed with {errtype} {errvalue} {traceback.format_exc()}")
|
|
108
|
+
|
|
109
|
+
# main
|
|
110
|
+
def doAction(self):
|
|
111
|
+
try:
|
|
112
|
+
# get logger
|
|
113
|
+
origTmpLog = MsgWrapper(logger)
|
|
114
|
+
origTmpLog.debug("start")
|
|
115
|
+
# clean up data locality
|
|
116
|
+
self.doCleanDataLocality()
|
|
117
|
+
# update data locality
|
|
118
|
+
self.doUpdateDataLocality()
|
|
119
|
+
except Exception:
|
|
120
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
121
|
+
origTmpLog.error(f"failed with {errtype} {errvalue}")
|
|
122
|
+
# return
|
|
123
|
+
origTmpLog.debug("done")
|
|
124
|
+
return self.SC_SUCCEEDED
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# thread for data locality update
|
|
128
|
+
class DataLocalityUpdaterThread(WorkerThread):
|
|
129
|
+
# constructor
|
|
130
|
+
def __init__(self, taskDsList, threadPool, taskbufferIF, ddmIF, pid, loggerObj):
|
|
131
|
+
# initialize worker with no semaphore
|
|
132
|
+
WorkerThread.__init__(self, None, threadPool, loggerObj)
|
|
133
|
+
# attributes
|
|
134
|
+
self.taskDsList = taskDsList
|
|
135
|
+
self.taskBufferIF = taskbufferIF
|
|
136
|
+
self.ddmIF = ddmIF
|
|
137
|
+
self.msgType = "datalocalityupdate"
|
|
138
|
+
self.pid = pid
|
|
139
|
+
self.logger = loggerObj
|
|
140
|
+
|
|
141
|
+
# main
|
|
142
|
+
def runImpl(self):
|
|
143
|
+
# initialize
|
|
144
|
+
n_updated_ds = 0
|
|
145
|
+
n_skipped_ds = 0
|
|
146
|
+
n_updated_replicas = 0
|
|
147
|
+
n_skipped_replicas = 0
|
|
148
|
+
while True:
|
|
149
|
+
try:
|
|
150
|
+
# get part of datasets
|
|
151
|
+
nDatasets = 5
|
|
152
|
+
taskDsList = self.taskDsList.get(nDatasets)
|
|
153
|
+
if len(taskDsList) == 0:
|
|
154
|
+
# no more datasets, quit
|
|
155
|
+
self.logger.debug(
|
|
156
|
+
f"{self.name} terminating since no more items; updated {n_updated_ds} datasets and {n_updated_replicas} replicas; skipped {n_skipped_ds} datasets and {n_skipped_replicas} replicas"
|
|
157
|
+
)
|
|
158
|
+
return
|
|
159
|
+
# loop over these datasets
|
|
160
|
+
for item in taskDsList:
|
|
161
|
+
if item is None:
|
|
162
|
+
n_skipped_ds += 1
|
|
163
|
+
continue
|
|
164
|
+
jedi_task_id, dataset_id, dataset_name = item
|
|
165
|
+
_, task_spec = self.taskBufferIF.getTaskWithID_JEDI(jedi_task_id)
|
|
166
|
+
dataset_replicas_map = self.ddmIF.listDatasetReplicas(dataset_name)
|
|
167
|
+
is_distributed_ds = self.ddmIF.isDistributedDataset(dataset_name)
|
|
168
|
+
# get rules when using data carousel
|
|
169
|
+
rule_rse_list = []
|
|
170
|
+
rule_rse_types = []
|
|
171
|
+
if task_spec.inputPreStaging():
|
|
172
|
+
# collect rse expressions from rules
|
|
173
|
+
_, tmp_rules = self.ddmIF.get_rules_state(dataset_name)
|
|
174
|
+
rule_rse_list = [r["rse_expression"] for r in tmp_rules.values()]
|
|
175
|
+
rule_rse_types = []
|
|
176
|
+
# extract rse types from rse expressions
|
|
177
|
+
for tmp_rse in rule_rse_list:
|
|
178
|
+
m = re.search(r"type=([^)]+)", tmp_rse)
|
|
179
|
+
if m:
|
|
180
|
+
rule_rse_types.append(m.group(1))
|
|
181
|
+
# loop over all replicas
|
|
182
|
+
for tmp_rse, tmp_stat_list in dataset_replicas_map.items():
|
|
183
|
+
# pre-checks
|
|
184
|
+
if is_distributed_ds:
|
|
185
|
+
# no checks for distributed datasets
|
|
186
|
+
pass
|
|
187
|
+
elif task_spec.inputPreStaging():
|
|
188
|
+
# use only replicas with rules when using data carousel
|
|
189
|
+
if tmp_rse not in rule_rse_list:
|
|
190
|
+
# check rse type
|
|
191
|
+
to_skip = True
|
|
192
|
+
for rse_type in rule_rse_types:
|
|
193
|
+
if rse_type in tmp_rse:
|
|
194
|
+
to_skip = False
|
|
195
|
+
break
|
|
196
|
+
if to_skip:
|
|
197
|
+
n_skipped_replicas += 1
|
|
198
|
+
self.logger.debug(
|
|
199
|
+
f"skipped {tmp_rse} for dataset {dataset_name} due to missing rule in {rule_rse_list} or rse type in {rule_rse_types}"
|
|
200
|
+
)
|
|
201
|
+
continue
|
|
202
|
+
else:
|
|
203
|
+
# use only complete replicas unless input is distributed or uses data carousel
|
|
204
|
+
tmp_statistics = tmp_stat_list[-1]
|
|
205
|
+
# skip unknown and incomplete
|
|
206
|
+
if tmp_statistics["found"] is None or tmp_statistics["found"] != tmp_statistics["total"]:
|
|
207
|
+
n_skipped_replicas += 1
|
|
208
|
+
continue
|
|
209
|
+
# update dataset locality table
|
|
210
|
+
self.taskBufferIF.updateDatasetLocality_JEDI(jedi_taskid=jedi_task_id, datasetid=dataset_id, rse=tmp_rse)
|
|
211
|
+
n_updated_replicas += 1
|
|
212
|
+
n_updated_ds += 1
|
|
213
|
+
except Exception as e:
|
|
214
|
+
self.logger.error(f"{self.__class__.__name__} failed in runImpl() with {str(e)}: {traceback.format_exc()}")
|
|
215
|
+
return
|