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,182 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from .MsgWrapper import MsgWrapper
|
|
4
|
+
|
|
5
|
+
_factoryModuleName = __name__.split(".")[-1]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# base class for factory
|
|
9
|
+
class FactoryBase:
|
|
10
|
+
# constructor
|
|
11
|
+
def __init__(self, vos, sourceLabels, logger, modConfig):
|
|
12
|
+
if isinstance(vos, list):
|
|
13
|
+
self.vos = vos
|
|
14
|
+
else:
|
|
15
|
+
try:
|
|
16
|
+
self.vos = vos.split("|")
|
|
17
|
+
except Exception:
|
|
18
|
+
self.vos = [vos]
|
|
19
|
+
if isinstance(sourceLabels, list):
|
|
20
|
+
self.sourceLabels = sourceLabels
|
|
21
|
+
else:
|
|
22
|
+
try:
|
|
23
|
+
self.sourceLabels = sourceLabels.split("|")
|
|
24
|
+
except Exception:
|
|
25
|
+
self.sourceLabels = [sourceLabels]
|
|
26
|
+
self.modConfig = modConfig
|
|
27
|
+
self.logger = MsgWrapper(logger, _factoryModuleName)
|
|
28
|
+
self.implMap = {}
|
|
29
|
+
self.className = None
|
|
30
|
+
self.classMap = {}
|
|
31
|
+
|
|
32
|
+
# initialize all modules
|
|
33
|
+
def initializeMods(self, *args):
|
|
34
|
+
# parse config
|
|
35
|
+
for configStr in self.modConfig.split(","):
|
|
36
|
+
configStr = configStr.strip()
|
|
37
|
+
items = configStr.split(":")
|
|
38
|
+
# check format
|
|
39
|
+
try:
|
|
40
|
+
vos = items[0].split("|")
|
|
41
|
+
sourceLabels = items[1].split("|")
|
|
42
|
+
moduleName = items[2]
|
|
43
|
+
className = items[3]
|
|
44
|
+
try:
|
|
45
|
+
subTypes = items[4].split("|")
|
|
46
|
+
except Exception:
|
|
47
|
+
subTypes = ["any"]
|
|
48
|
+
except Exception:
|
|
49
|
+
self.logger.error(f"wrong config definition : {configStr}")
|
|
50
|
+
continue
|
|
51
|
+
# loop over all VOs
|
|
52
|
+
for vo in vos:
|
|
53
|
+
# loop over all labels
|
|
54
|
+
for sourceLabel in sourceLabels:
|
|
55
|
+
# check vo and sourceLabel if specified
|
|
56
|
+
if vo not in ["", "any"] and vo not in self.vos and None not in self.vos and "any" not in self.vos:
|
|
57
|
+
continue
|
|
58
|
+
if (
|
|
59
|
+
sourceLabel not in ["", "any"]
|
|
60
|
+
and sourceLabel not in self.sourceLabels
|
|
61
|
+
and None not in self.sourceLabels
|
|
62
|
+
and "any" not in self.sourceLabels
|
|
63
|
+
):
|
|
64
|
+
continue
|
|
65
|
+
# loop over all sub types
|
|
66
|
+
for subType in subTypes:
|
|
67
|
+
# import
|
|
68
|
+
try:
|
|
69
|
+
# import module
|
|
70
|
+
self.logger.info(f"vo={vo} label={sourceLabel} subtype={subType}")
|
|
71
|
+
self.logger.info(f"importing {moduleName}")
|
|
72
|
+
mod = __import__(moduleName)
|
|
73
|
+
for subModuleName in moduleName.split(".")[1:]:
|
|
74
|
+
mod = getattr(mod, subModuleName)
|
|
75
|
+
# get class
|
|
76
|
+
self.logger.info(f"getting class {className}")
|
|
77
|
+
cls = getattr(mod, className)
|
|
78
|
+
# instantiate
|
|
79
|
+
self.logger.info("instantiating")
|
|
80
|
+
impl = cls(*args)
|
|
81
|
+
# set vo
|
|
82
|
+
impl.vo = vo
|
|
83
|
+
impl.prodSourceLabel = sourceLabel
|
|
84
|
+
# append
|
|
85
|
+
if vo not in self.implMap:
|
|
86
|
+
self.implMap[vo] = {}
|
|
87
|
+
self.classMap[vo] = {}
|
|
88
|
+
if sourceLabel not in self.implMap[vo]:
|
|
89
|
+
self.implMap[vo][sourceLabel] = {}
|
|
90
|
+
self.classMap[vo][sourceLabel] = {}
|
|
91
|
+
self.implMap[vo][sourceLabel][subType] = impl
|
|
92
|
+
self.classMap[vo][sourceLabel][subType] = cls
|
|
93
|
+
self.logger.info(f"{cls} is ready for {vo}:{sourceLabel}:{subType}")
|
|
94
|
+
except Exception:
|
|
95
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
96
|
+
self.logger.error(
|
|
97
|
+
"failed to import {mn}.{cn} for vo={vo} label={lb} subtype={st} due to {et} {ev}".format(
|
|
98
|
+
et=errtype.__name__, ev=errvalue, st=subType, vo=vo, lb=sourceLabel, cn=className, mn=moduleName
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
raise ImportError(f"failed to import {moduleName}.{className}")
|
|
102
|
+
# return
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
# get implementation for vo and sourceLabel. Only work with initializeMods()
|
|
106
|
+
def getImpl(self, vo, sourceLabel, subType="any", doRefresh=True):
|
|
107
|
+
# check VO
|
|
108
|
+
if vo in self.implMap:
|
|
109
|
+
# match VO
|
|
110
|
+
voImplMap = self.implMap[vo]
|
|
111
|
+
elif "any" in self.implMap:
|
|
112
|
+
# catch all
|
|
113
|
+
voImplMap = self.implMap["any"]
|
|
114
|
+
else:
|
|
115
|
+
return None
|
|
116
|
+
# check sourceLabel
|
|
117
|
+
if sourceLabel in voImplMap:
|
|
118
|
+
# match sourceLabel
|
|
119
|
+
srcImplMap = voImplMap[sourceLabel]
|
|
120
|
+
elif "any" in voImplMap:
|
|
121
|
+
# catch all
|
|
122
|
+
srcImplMap = voImplMap["any"]
|
|
123
|
+
else:
|
|
124
|
+
return None
|
|
125
|
+
# check subType
|
|
126
|
+
if subType in srcImplMap:
|
|
127
|
+
# match subType
|
|
128
|
+
tmpImpl = srcImplMap[subType]
|
|
129
|
+
if doRefresh:
|
|
130
|
+
tmpImpl.refresh()
|
|
131
|
+
return tmpImpl
|
|
132
|
+
elif "any" in srcImplMap:
|
|
133
|
+
# catch all
|
|
134
|
+
tmpImpl = srcImplMap["any"]
|
|
135
|
+
if doRefresh:
|
|
136
|
+
tmpImpl.refresh()
|
|
137
|
+
return tmpImpl
|
|
138
|
+
else:
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
# instantiate implementation for vo and sourceLabel. Only work with initializeMods()
|
|
142
|
+
def instantiateImpl(self, vo, sourceLabel, subType, *args):
|
|
143
|
+
# check VO
|
|
144
|
+
if vo in self.classMap:
|
|
145
|
+
# match VO
|
|
146
|
+
voImplMap = self.classMap[vo]
|
|
147
|
+
elif "any" in self.classMap:
|
|
148
|
+
# catch all
|
|
149
|
+
voImplMap = self.classMap["any"]
|
|
150
|
+
else:
|
|
151
|
+
return None
|
|
152
|
+
# check sourceLabel
|
|
153
|
+
if sourceLabel in voImplMap:
|
|
154
|
+
# match sourceLabel
|
|
155
|
+
srcImplMap = voImplMap[sourceLabel]
|
|
156
|
+
elif "any" in voImplMap:
|
|
157
|
+
# catch all
|
|
158
|
+
srcImplMap = voImplMap["any"]
|
|
159
|
+
else:
|
|
160
|
+
return None
|
|
161
|
+
# check subType
|
|
162
|
+
if subType in srcImplMap:
|
|
163
|
+
# match subType
|
|
164
|
+
impl = srcImplMap[subType](*args)
|
|
165
|
+
impl.vo = vo
|
|
166
|
+
impl.prodSourceLabel = sourceLabel
|
|
167
|
+
return impl
|
|
168
|
+
elif "any" in srcImplMap:
|
|
169
|
+
# catch all
|
|
170
|
+
impl = srcImplMap["any"](*args)
|
|
171
|
+
impl.vo = vo
|
|
172
|
+
impl.prodSourceLabel = sourceLabel
|
|
173
|
+
return impl
|
|
174
|
+
else:
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
# get class name of impl
|
|
178
|
+
def getClassName(self, vo=None, sourceLabel=None):
|
|
179
|
+
impl = self.getImpl(vo, sourceLabel, doRefresh=False)
|
|
180
|
+
if impl is None:
|
|
181
|
+
return None
|
|
182
|
+
return impl.__class__.__name__
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import multiprocessing
|
|
3
|
+
import multiprocessing.reduction
|
|
4
|
+
import os
|
|
5
|
+
import signal
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from multiprocessing.connection import reduce_connection
|
|
13
|
+
except ImportError:
|
|
14
|
+
from multiprocessing.reduction import reduce_connection
|
|
15
|
+
|
|
16
|
+
# import multiprocessing
|
|
17
|
+
# logger = multiprocessing.log_to_stderr()
|
|
18
|
+
# logger.setLevel(multiprocessing.SUBDEBUG)
|
|
19
|
+
|
|
20
|
+
###########################################################
|
|
21
|
+
#
|
|
22
|
+
# status codes for IPC
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# class for status code
|
|
27
|
+
class StatusCode(object):
|
|
28
|
+
def __init__(self, value):
|
|
29
|
+
self.value = value
|
|
30
|
+
|
|
31
|
+
def __str__(self):
|
|
32
|
+
return f"{self.value}"
|
|
33
|
+
|
|
34
|
+
# comparator
|
|
35
|
+
def __eq__(self, other):
|
|
36
|
+
try:
|
|
37
|
+
return self.value == other.value
|
|
38
|
+
except Exception:
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
def __ne__(self, other):
|
|
42
|
+
try:
|
|
43
|
+
return self.value != other.value
|
|
44
|
+
except Exception:
|
|
45
|
+
return True
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# mapping to accessors
|
|
49
|
+
statusCodeMap = {
|
|
50
|
+
"SC_SUCCEEDED": StatusCode(0),
|
|
51
|
+
"SC_FAILED": StatusCode(1),
|
|
52
|
+
"SC_FATAL": StatusCode(2),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# install the list of status codes to a class
|
|
57
|
+
def installSC(cls):
|
|
58
|
+
for sc, val in statusCodeMap.items():
|
|
59
|
+
setattr(cls, sc, val)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# install SCs in this module
|
|
63
|
+
installSC(sys.modules[__name__])
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
###########################################################
|
|
67
|
+
#
|
|
68
|
+
# classes for IPC
|
|
69
|
+
#
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# log message with timestamp
|
|
73
|
+
def dumpStdOut(sender, message):
|
|
74
|
+
timeNow = naive_utcnow()
|
|
75
|
+
print(f"{str(timeNow)} {sender}: INFO {message}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# object class for command
|
|
79
|
+
class CommandObject(object):
|
|
80
|
+
# constructor
|
|
81
|
+
def __init__(self, methodName, argList, argMap):
|
|
82
|
+
self.methodName = methodName
|
|
83
|
+
self.argList = argList
|
|
84
|
+
self.argMap = argMap
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# object class for response
|
|
88
|
+
class ReturnObject(object):
|
|
89
|
+
# constructor
|
|
90
|
+
def __init__(self):
|
|
91
|
+
self.statusCode = None
|
|
92
|
+
self.errorValue = None
|
|
93
|
+
self.returnValue = None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# process class
|
|
97
|
+
class ProcessClass(object):
|
|
98
|
+
# constructor
|
|
99
|
+
def __init__(self, pid, connection):
|
|
100
|
+
self.pid = pid
|
|
101
|
+
self.nused = 0
|
|
102
|
+
self.usedMemory = 0
|
|
103
|
+
self.nMemLookup = 20
|
|
104
|
+
# reduce connection to make it picklable
|
|
105
|
+
self.reduced_pipe = reduce_connection(connection)
|
|
106
|
+
|
|
107
|
+
# get connection
|
|
108
|
+
def connection(self):
|
|
109
|
+
# rebuild connection
|
|
110
|
+
return self.reduced_pipe[0](*self.reduced_pipe[1])
|
|
111
|
+
|
|
112
|
+
# reduce connection
|
|
113
|
+
def reduceConnection(self, connection):
|
|
114
|
+
self.reduced_pipe = reduce_connection(connection)
|
|
115
|
+
|
|
116
|
+
# get memory usage
|
|
117
|
+
def getMemUsage(self):
|
|
118
|
+
# update memory info
|
|
119
|
+
if self.nused % self.nMemLookup == 0:
|
|
120
|
+
try:
|
|
121
|
+
# read memory info from /proc
|
|
122
|
+
t = open(f"/proc/{self.pid}/status")
|
|
123
|
+
v = t.read()
|
|
124
|
+
t.close()
|
|
125
|
+
value = 0
|
|
126
|
+
for line in v.split("\n"):
|
|
127
|
+
if line.startswith("VmRSS"):
|
|
128
|
+
items = line.split()
|
|
129
|
+
value = int(items[1])
|
|
130
|
+
if items[2] in ["kB", "KB"]:
|
|
131
|
+
value /= 1024
|
|
132
|
+
elif items[2] in ["mB", "MB"]:
|
|
133
|
+
pass
|
|
134
|
+
break
|
|
135
|
+
self.usedMemory = value
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
return self.usedMemory
|
|
139
|
+
else:
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# method class
|
|
144
|
+
class MethodClass(object):
|
|
145
|
+
# constructor
|
|
146
|
+
def __init__(self, className, methodName, vo, connectionQueue, voIF):
|
|
147
|
+
self.className = className
|
|
148
|
+
self.methodName = methodName
|
|
149
|
+
self.vo = vo
|
|
150
|
+
self.connectionQueue = connectionQueue
|
|
151
|
+
self.voIF = voIF
|
|
152
|
+
self.pipeList = []
|
|
153
|
+
|
|
154
|
+
# method emulation
|
|
155
|
+
def __call__(self, *args, **kwargs):
|
|
156
|
+
commandObj = CommandObject(self.methodName, args, kwargs)
|
|
157
|
+
nTry = 3
|
|
158
|
+
for iTry in range(nTry):
|
|
159
|
+
# exceptions
|
|
160
|
+
retException = None
|
|
161
|
+
strException = None
|
|
162
|
+
try:
|
|
163
|
+
stepIdx = 0
|
|
164
|
+
# get child process
|
|
165
|
+
child_process = self.connectionQueue.get()
|
|
166
|
+
# get pipe
|
|
167
|
+
stepIdx = 1
|
|
168
|
+
pipe = child_process.connection()
|
|
169
|
+
# get ack
|
|
170
|
+
stepIdx = 2
|
|
171
|
+
timeoutPeriodACK = 30
|
|
172
|
+
if not pipe.poll(timeoutPeriodACK):
|
|
173
|
+
raise JEDITimeoutError(f"did not get ACK for {timeoutPeriodACK}sec")
|
|
174
|
+
ack = pipe.recv()
|
|
175
|
+
# send command
|
|
176
|
+
stepIdx = 3
|
|
177
|
+
pipe.send(commandObj)
|
|
178
|
+
# wait response
|
|
179
|
+
stepIdx = 4
|
|
180
|
+
timeoutPeriod = 600
|
|
181
|
+
timeNow = naive_utcnow()
|
|
182
|
+
if not pipe.poll(timeoutPeriod):
|
|
183
|
+
raise JEDITimeoutError(f"did not get response for {timeoutPeriod}sec")
|
|
184
|
+
regTime = naive_utcnow() - timeNow
|
|
185
|
+
if regTime > datetime.timedelta(seconds=60):
|
|
186
|
+
dumpStdOut(
|
|
187
|
+
self.className,
|
|
188
|
+
f"methodName={self.methodName} took {regTime.seconds}.{int(regTime.microseconds / 1000):03d} sec in pid={child_process.pid}",
|
|
189
|
+
)
|
|
190
|
+
# get response
|
|
191
|
+
stepIdx = 5
|
|
192
|
+
ret = pipe.recv()
|
|
193
|
+
# set exception type based on error
|
|
194
|
+
stepIdx = 6
|
|
195
|
+
if ret.statusCode == SC_FAILED:
|
|
196
|
+
retException = JEDITemporaryError
|
|
197
|
+
elif ret.statusCode == SC_FATAL:
|
|
198
|
+
retException = JEDIFatalError
|
|
199
|
+
except Exception:
|
|
200
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
201
|
+
retException = errtype
|
|
202
|
+
argStr = f"args={str(args)} kargs={str(kwargs)}"
|
|
203
|
+
strException = f"VO={self.vo} type={errtype.__name__} stepIdx={stepIdx} : {self.className}.{self.methodName} {errvalue} {argStr[:200]}"
|
|
204
|
+
# increment nused
|
|
205
|
+
child_process.nused += 1
|
|
206
|
+
# memory check
|
|
207
|
+
largeMemory = False
|
|
208
|
+
memUsed = child_process.getMemUsage()
|
|
209
|
+
if memUsed is not None:
|
|
210
|
+
memStr = f"pid={child_process.pid} memory={memUsed}MB"
|
|
211
|
+
if memUsed > 1.5 * 1024:
|
|
212
|
+
largeMemory = True
|
|
213
|
+
memStr += " exceeds memory limit"
|
|
214
|
+
dumpStdOut(self.className, memStr)
|
|
215
|
+
# kill old or problematic process
|
|
216
|
+
if child_process.nused > 1000 or retException not in [None, JEDITemporaryError, JEDIFatalError] or largeMemory:
|
|
217
|
+
dumpStdOut(
|
|
218
|
+
self.className,
|
|
219
|
+
f"methodName={self.methodName} ret={retException} nused={child_process.nused} {strException} in pid={child_process.pid}",
|
|
220
|
+
)
|
|
221
|
+
# close connection
|
|
222
|
+
try:
|
|
223
|
+
pipe.close()
|
|
224
|
+
except Exception:
|
|
225
|
+
pass
|
|
226
|
+
# terminate child process
|
|
227
|
+
try:
|
|
228
|
+
dumpStdOut(self.className, f"killing pid={child_process.pid}")
|
|
229
|
+
os.kill(child_process.pid, signal.SIGKILL)
|
|
230
|
+
dumpStdOut(self.className, f"waiting pid={child_process.pid}")
|
|
231
|
+
os.waitpid(child_process.pid, 0)
|
|
232
|
+
dumpStdOut(self.className, f"terminated pid={child_process.pid}")
|
|
233
|
+
except Exception:
|
|
234
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
235
|
+
if "No child processes" not in str(errvalue):
|
|
236
|
+
dumpStdOut(self.className, f"failed to terminate {child_process.pid} with {errtype}:{errvalue}")
|
|
237
|
+
# make new child process
|
|
238
|
+
self.voIF.launchChild()
|
|
239
|
+
else:
|
|
240
|
+
# reduce process object to avoid deadlock due to rebuilding of connection
|
|
241
|
+
child_process.reduceConnection(pipe)
|
|
242
|
+
self.connectionQueue.put(child_process)
|
|
243
|
+
# success, fatal error, or maximally attempted
|
|
244
|
+
if retException in [None, JEDIFatalError] or (iTry + 1 == nTry):
|
|
245
|
+
break
|
|
246
|
+
# sleep
|
|
247
|
+
time.sleep(1)
|
|
248
|
+
# raise exception
|
|
249
|
+
if retException is not None:
|
|
250
|
+
if strException is None:
|
|
251
|
+
strException = f"VO={self.vo} {ret.errorValue}"
|
|
252
|
+
raise retException(strException)
|
|
253
|
+
# return
|
|
254
|
+
if ret.statusCode == SC_SUCCEEDED:
|
|
255
|
+
return ret.returnValue
|
|
256
|
+
else:
|
|
257
|
+
raise retException(f"VO={self.vo} {ret.errorValue}")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# interface class to send command
|
|
261
|
+
class CommandSendInterface(object):
|
|
262
|
+
# constructor
|
|
263
|
+
def __init__(self, vo, maxChild, moduleName, className):
|
|
264
|
+
self.vo = vo
|
|
265
|
+
self.maxChild = maxChild
|
|
266
|
+
self.connectionQueue = multiprocessing.Queue(maxChild)
|
|
267
|
+
self.moduleName = moduleName
|
|
268
|
+
self.className = className
|
|
269
|
+
|
|
270
|
+
# factory method
|
|
271
|
+
def __getattr__(self, attrName):
|
|
272
|
+
return MethodClass(self.className, attrName, self.vo, self.connectionQueue, self)
|
|
273
|
+
|
|
274
|
+
# launcher for child processe
|
|
275
|
+
def launcher(self, channel):
|
|
276
|
+
# import module
|
|
277
|
+
mod = __import__(self.moduleName)
|
|
278
|
+
for subModuleName in self.moduleName.split(".")[1:]:
|
|
279
|
+
mod = getattr(mod, subModuleName)
|
|
280
|
+
# get class
|
|
281
|
+
cls = getattr(mod, self.className)
|
|
282
|
+
# start child process
|
|
283
|
+
msg = f"start {self.className} with pid={os.getpid()}"
|
|
284
|
+
dumpStdOut(self.moduleName, msg)
|
|
285
|
+
timeNow = naive_utcnow()
|
|
286
|
+
try:
|
|
287
|
+
cls(channel).start()
|
|
288
|
+
except Exception:
|
|
289
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
290
|
+
dumpStdOut(self.className, f"launcher crashed with {errtype}:{errvalue}")
|
|
291
|
+
|
|
292
|
+
# launch child processes to interact with DDM
|
|
293
|
+
def launchChild(self):
|
|
294
|
+
# make pipe
|
|
295
|
+
parent_conn, child_conn = multiprocessing.Pipe()
|
|
296
|
+
# make child process
|
|
297
|
+
child_process = multiprocessing.Process(target=self.launcher, args=(child_conn,))
|
|
298
|
+
# start child process
|
|
299
|
+
child_process.daemon = True
|
|
300
|
+
child_process.start()
|
|
301
|
+
# keep process in queue
|
|
302
|
+
processObj = ProcessClass(child_process.pid, parent_conn)
|
|
303
|
+
# sync to wait until object in the child process is instantiated
|
|
304
|
+
pipe = processObj.connection()
|
|
305
|
+
pipe.recv()
|
|
306
|
+
processObj.reduceConnection(pipe)
|
|
307
|
+
# ready
|
|
308
|
+
self.connectionQueue.put(processObj)
|
|
309
|
+
|
|
310
|
+
# initialize
|
|
311
|
+
def initialize(self):
|
|
312
|
+
for i in range(self.maxChild):
|
|
313
|
+
self.launchChild()
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
# interface class to receive command
|
|
317
|
+
class CommandReceiveInterface(object):
|
|
318
|
+
# constructor
|
|
319
|
+
def __init__(self, con):
|
|
320
|
+
self.con = con
|
|
321
|
+
self.cacheMap = {}
|
|
322
|
+
|
|
323
|
+
# make key for cache
|
|
324
|
+
def makeKey(self, className, methodName, argList, argMap):
|
|
325
|
+
try:
|
|
326
|
+
tmpKey = f"{className}:{methodName}:"
|
|
327
|
+
for argItem in argList:
|
|
328
|
+
tmpKey += f"{str(argItem)}:"
|
|
329
|
+
for argKey, argVal in argMap.items():
|
|
330
|
+
tmpKey += f"{argKey}={str(argVal)}:"
|
|
331
|
+
tmpKey = tmpKey[:-1]
|
|
332
|
+
return tmpKey
|
|
333
|
+
except Exception:
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
# main loop
|
|
337
|
+
def start(self):
|
|
338
|
+
# sync
|
|
339
|
+
self.con.send("ready")
|
|
340
|
+
# main loop
|
|
341
|
+
while True:
|
|
342
|
+
# send ACK
|
|
343
|
+
self.con.send("ack")
|
|
344
|
+
# get command
|
|
345
|
+
commandObj = self.con.recv()
|
|
346
|
+
# make return
|
|
347
|
+
retObj = ReturnObject()
|
|
348
|
+
# get class name
|
|
349
|
+
className = self.__class__.__name__
|
|
350
|
+
# check method name
|
|
351
|
+
if not hasattr(self, commandObj.methodName):
|
|
352
|
+
# method not found
|
|
353
|
+
retObj.statusCode = self.SC_FATAL
|
|
354
|
+
retObj.errorValue = f"type=AttributeError : {className} instance has no attribute {commandObj.methodName}"
|
|
355
|
+
else:
|
|
356
|
+
try:
|
|
357
|
+
# use cache
|
|
358
|
+
useCache = False
|
|
359
|
+
doExec = True
|
|
360
|
+
if "useResultCache" in commandObj.argMap:
|
|
361
|
+
# get time range
|
|
362
|
+
timeRange = commandObj.argMap["useResultCache"]
|
|
363
|
+
# delete from args map
|
|
364
|
+
del commandObj.argMap["useResultCache"]
|
|
365
|
+
# make key for cache
|
|
366
|
+
tmpCacheKey = self.makeKey(className, commandObj.methodName, commandObj.argList, commandObj.argMap)
|
|
367
|
+
if tmpCacheKey is not None:
|
|
368
|
+
useCache = True
|
|
369
|
+
# cache is fresh
|
|
370
|
+
if tmpCacheKey in self.cacheMap and self.cacheMap[tmpCacheKey]["utime"] + datetime.timedelta(seconds=timeRange) > naive_utcnow():
|
|
371
|
+
tmpRet = self.cacheMap[tmpCacheKey]["value"]
|
|
372
|
+
doExec = False
|
|
373
|
+
# exec
|
|
374
|
+
if doExec:
|
|
375
|
+
# get function
|
|
376
|
+
functionObj = getattr(self, commandObj.methodName)
|
|
377
|
+
# exec
|
|
378
|
+
tmpRet = functionObj(*commandObj.argList, **commandObj.argMap)
|
|
379
|
+
if isinstance(tmpRet, StatusCode):
|
|
380
|
+
# only status code was returned
|
|
381
|
+
retObj.statusCode = tmpRet
|
|
382
|
+
elif (isinstance(tmpRet, tuple) or isinstance(tmpRet, list)) and len(tmpRet) > 0 and isinstance(tmpRet[0], StatusCode):
|
|
383
|
+
retObj.statusCode = tmpRet[0]
|
|
384
|
+
# status code + return values
|
|
385
|
+
if len(tmpRet) > 1:
|
|
386
|
+
if retObj.statusCode == self.SC_SUCCEEDED:
|
|
387
|
+
if len(tmpRet) == 2:
|
|
388
|
+
retObj.returnValue = tmpRet[1]
|
|
389
|
+
else:
|
|
390
|
+
retObj.returnValue = tmpRet[1:]
|
|
391
|
+
else:
|
|
392
|
+
if len(tmpRet) == 2:
|
|
393
|
+
retObj.errorValue = tmpRet[1]
|
|
394
|
+
else:
|
|
395
|
+
retObj.errorValue = tmpRet[1:]
|
|
396
|
+
else:
|
|
397
|
+
retObj.statusCode = self.SC_SUCCEEDED
|
|
398
|
+
retObj.returnValue = tmpRet
|
|
399
|
+
except Exception:
|
|
400
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
401
|
+
# failed
|
|
402
|
+
retObj.statusCode = self.SC_FATAL
|
|
403
|
+
retObj.errorValue = f"type={errtype.__name__} : {className}.{commandObj.methodName} : {errvalue}"
|
|
404
|
+
# cache
|
|
405
|
+
if useCache and doExec and retObj.statusCode == self.SC_SUCCEEDED:
|
|
406
|
+
self.cacheMap[tmpCacheKey] = {"utime": naive_utcnow(), "value": tmpRet}
|
|
407
|
+
# return
|
|
408
|
+
self.con.send(retObj)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
# install SCs
|
|
412
|
+
installSC(CommandReceiveInterface)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
###########################################################
|
|
416
|
+
#
|
|
417
|
+
# exceptions for IPC
|
|
418
|
+
#
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
# exception for temporary error
|
|
422
|
+
class JEDITemporaryError(Exception):
|
|
423
|
+
pass
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
# exception for fatal error
|
|
427
|
+
class JEDIFatalError(Exception):
|
|
428
|
+
pass
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
# exception for timeout error
|
|
432
|
+
class JEDITimeoutError(Exception):
|
|
433
|
+
pass
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
5
|
+
|
|
6
|
+
from pandajedi.jediconfig import jedi_config
|
|
7
|
+
from pandaserver.taskbuffer import OraDBProxy
|
|
8
|
+
|
|
9
|
+
logger = PandaLogger().getLogger(__name__.split(".")[-1])
|
|
10
|
+
OraDBProxy._logger = logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# add handlers of filtered logger
|
|
14
|
+
tmpLoggerFiltered = PandaLogger().getLogger(__name__.split(".")[-1] + "Filtered")
|
|
15
|
+
for tmpHdr in tmpLoggerFiltered.handlers:
|
|
16
|
+
tmpHdr.setLevel(logging.INFO)
|
|
17
|
+
logger.addHandler(tmpHdr)
|
|
18
|
+
tmpLoggerFiltered.removeHandler(tmpHdr)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# get mb proxies used in DBProxy methods
|
|
22
|
+
def get_mb_proxy_dict():
|
|
23
|
+
if hasattr(jedi_config, "mq") and hasattr(jedi_config.mq, "configFile") and jedi_config.mq.configFile:
|
|
24
|
+
# delay import to open logger file inside python daemon
|
|
25
|
+
from pandajedi.jediorder.JediMsgProcessor import MsgProcAgent
|
|
26
|
+
|
|
27
|
+
in_q_list = []
|
|
28
|
+
out_q_list = ["jedi_jobtaskstatus", "jedi_contents_feeder", "jedi_job_generator"]
|
|
29
|
+
mq_agent = MsgProcAgent(config_file=jedi_config.mq.configFile)
|
|
30
|
+
mb_proxy_dict = mq_agent.start_passive_mode(in_q_list=in_q_list, out_q_list=out_q_list)
|
|
31
|
+
# stop with atexit
|
|
32
|
+
atexit.register(mq_agent.stop_passive_mode)
|
|
33
|
+
# return
|
|
34
|
+
return mb_proxy_dict
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# main class
|
|
38
|
+
class DBProxy(OraDBProxy.DBProxy):
|
|
39
|
+
# constructor
|
|
40
|
+
def __init__(self, useOtherError=False):
|
|
41
|
+
OraDBProxy.DBProxy.__init__(self, useOtherError)
|
|
42
|
+
|
|
43
|
+
# set JEDI attributes
|
|
44
|
+
self.set_jedi_attributes(jedi_config, get_mb_proxy_dict)
|
|
45
|
+
|
|
46
|
+
# connect to DB (just for INTR)
|
|
47
|
+
def connect(
|
|
48
|
+
self,
|
|
49
|
+
dbhost=jedi_config.db.dbhost,
|
|
50
|
+
dbpasswd=jedi_config.db.dbpasswd,
|
|
51
|
+
dbuser=jedi_config.db.dbuser,
|
|
52
|
+
dbname=jedi_config.db.dbname,
|
|
53
|
+
dbtimeout=None,
|
|
54
|
+
reconnect=False,
|
|
55
|
+
):
|
|
56
|
+
return OraDBProxy.DBProxy.connect(self, dbhost=dbhost, dbpasswd=dbpasswd, dbuser=dbuser, dbname=dbname, dbtimeout=dbtimeout, reconnect=reconnect)
|