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,350 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import xml.dom.minidom
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class dom_job:
|
|
9
|
+
"""infiles[inds]=[file1,file2...]
|
|
10
|
+
outfiles = [file1,file2...]
|
|
11
|
+
command - script that will be executed on the grid
|
|
12
|
+
prepend - list of (option,value) prepended to output file name
|
|
13
|
+
forward - list of (option,value) forwarded to the grid job
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(s, domjob=None, primaryds=None, defaultcmd=None, defaultout=[]):
|
|
17
|
+
"""Loads <job></job> from xml file.
|
|
18
|
+
If primaryds is set, makes sure it is present in job spec"""
|
|
19
|
+
s.infiles = {}
|
|
20
|
+
s.outfiles = []
|
|
21
|
+
s.command = defaultcmd
|
|
22
|
+
s.prepend = []
|
|
23
|
+
s.forward = []
|
|
24
|
+
if not domjob:
|
|
25
|
+
return
|
|
26
|
+
# script executed on the grid node for this job
|
|
27
|
+
if len(domjob.getElementsByTagName("command")) > 0:
|
|
28
|
+
s.command = dom_parser.text(domjob.getElementsByTagName("command")[0])
|
|
29
|
+
# input files
|
|
30
|
+
for inds in domjob.getElementsByTagName("inds"):
|
|
31
|
+
name = dom_parser.text(inds.getElementsByTagName("name")[0])
|
|
32
|
+
files = inds.getElementsByTagName("file")
|
|
33
|
+
if len(files) == 0:
|
|
34
|
+
continue
|
|
35
|
+
s.infiles[name] = []
|
|
36
|
+
for file in files:
|
|
37
|
+
s.infiles[name].append(dom_parser.text(file))
|
|
38
|
+
if primaryds and primaryds not in s.infiles.keys():
|
|
39
|
+
print(f"ERROR: primaryds={primaryds} must be present in each job")
|
|
40
|
+
sys.exit(0)
|
|
41
|
+
# output files (also, drop duplicates within this job)
|
|
42
|
+
outfiles = set(defaultout)
|
|
43
|
+
[outfiles.add(dom_parser.text(v)) for v in domjob.getElementsByTagName("output")]
|
|
44
|
+
s.outfiles = list(outfiles)
|
|
45
|
+
# gearing options
|
|
46
|
+
for o in domjob.getElementsByTagName("option"):
|
|
47
|
+
name = o.attributes["name"].value
|
|
48
|
+
value = dom_parser.text(o)
|
|
49
|
+
prepend = dom_parser.true(o.attributes["prepend"].value)
|
|
50
|
+
forward = dom_parser.true(o.attributes["forward"].value)
|
|
51
|
+
if prepend:
|
|
52
|
+
s.prepend.append((name, value))
|
|
53
|
+
if forward:
|
|
54
|
+
s.forward.append((name, value))
|
|
55
|
+
|
|
56
|
+
def to_dom(s):
|
|
57
|
+
"""Converts this job to a dom tree branch"""
|
|
58
|
+
x = xml.dom.minidom.Document()
|
|
59
|
+
job = x.createElement("job")
|
|
60
|
+
for inds in s.infiles.keys():
|
|
61
|
+
job.appendChild(x.createElement("inds"))
|
|
62
|
+
job.childNodes[-1].appendChild(x.createElement("name"))
|
|
63
|
+
job.childNodes[-1].childNodes[-1].appendChild(x.createTextNode(inds))
|
|
64
|
+
for file in s.infiles[inds]:
|
|
65
|
+
job.childNodes[-1].appendChild(x.createElement("file"))
|
|
66
|
+
job.childNodes[-1].childNodes[-1].appendChild(x.createTextNode(file))
|
|
67
|
+
for outfile in s.outfiles:
|
|
68
|
+
job.appendChild(x.createElement("output"))
|
|
69
|
+
job.childNodes[-1].appendChild(x.createTextNode(outfile))
|
|
70
|
+
if s.command:
|
|
71
|
+
job.appendChild(x.createElement("command"))
|
|
72
|
+
job.childNodes[-1].appendChild(x.createTextNode(s.command))
|
|
73
|
+
for option in s.prepend + list(set(s.prepend + s.forward) - set(s.prepend)):
|
|
74
|
+
job.appendChild(x.createElement("option"))
|
|
75
|
+
job.childNodes[-1].setAttribute("name", str(option[0]))
|
|
76
|
+
if option in s.forward:
|
|
77
|
+
job.childNodes[-1].setAttribute("forward", "true")
|
|
78
|
+
else:
|
|
79
|
+
job.childNodes[-1].setAttribute("forward", "false")
|
|
80
|
+
if option in s.prepend:
|
|
81
|
+
job.childNodes[-1].setAttribute("prepend", "true")
|
|
82
|
+
else:
|
|
83
|
+
job.childNodes[-1].setAttribute("prepend", "false")
|
|
84
|
+
job.childNodes[-1].appendChild(x.createTextNode(str(option[1])))
|
|
85
|
+
return job
|
|
86
|
+
|
|
87
|
+
def files_in_DS(s, DS):
|
|
88
|
+
"""Returns a list of files used in a given job in a given dataset"""
|
|
89
|
+
if DS in s.infiles:
|
|
90
|
+
return s.infiles[DS]
|
|
91
|
+
else:
|
|
92
|
+
return []
|
|
93
|
+
|
|
94
|
+
def forward_opts(s):
|
|
95
|
+
"""passable string of forward options"""
|
|
96
|
+
return " ".join([f"{v[0]}={v[1]}" for v in s.forward])
|
|
97
|
+
|
|
98
|
+
def prepend_string(s):
|
|
99
|
+
"""a tag string prepended to output files"""
|
|
100
|
+
return "_".join([f"{v[0]}{v[1]}" for v in s.prepend])
|
|
101
|
+
|
|
102
|
+
def exec_string(s):
|
|
103
|
+
"""exec string for prun.
|
|
104
|
+
If user requested to run script run.sh (via <command>run.sh</command>), it will return
|
|
105
|
+
opt1=value1 opt2=value2 opt3=value3 run.sh
|
|
106
|
+
This way, all options will be set inside run.sh
|
|
107
|
+
"""
|
|
108
|
+
return f"{s.forward_opts()} {s.command}"
|
|
109
|
+
|
|
110
|
+
def exec_string_enc(s):
|
|
111
|
+
"""exec string for prun.
|
|
112
|
+
If user requested to run script run.sh (via <command>run.sh</command>), it will return
|
|
113
|
+
opt1=value1 opt2=value2 opt3=value3 run.sh
|
|
114
|
+
This way, all options will be set inside run.sh
|
|
115
|
+
"""
|
|
116
|
+
comStr = f"{s.forward_opts()} {s.command}"
|
|
117
|
+
return quote(comStr)
|
|
118
|
+
|
|
119
|
+
def get_outmap_str(s, outMap):
|
|
120
|
+
"""return mapping of original and new filenames"""
|
|
121
|
+
newMap = {}
|
|
122
|
+
for oldLFN, fileSpec in outMap.items():
|
|
123
|
+
newMap[oldLFN] = str(fileSpec.lfn)
|
|
124
|
+
return str(newMap)
|
|
125
|
+
|
|
126
|
+
def outputs_list(s, prepend=False):
|
|
127
|
+
"""python list with finalized output file names"""
|
|
128
|
+
if prepend and s.prepend_string():
|
|
129
|
+
return [s.prepend_string() + "." + o for o in s.outfiles]
|
|
130
|
+
else:
|
|
131
|
+
return [o for o in s.outfiles]
|
|
132
|
+
|
|
133
|
+
def outputs(s, prepend=False):
|
|
134
|
+
"""Comma-separated list of output files accepted by prun"""
|
|
135
|
+
return ",".join(s.outputs_list(prepend))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class dom_parser:
|
|
139
|
+
def __init__(s, fname=None, xmlStr=None):
|
|
140
|
+
"""creates a dom object out of a text file (if provided)"""
|
|
141
|
+
s.fname = fname
|
|
142
|
+
s.dom = None
|
|
143
|
+
s.title = None
|
|
144
|
+
s.tag = None
|
|
145
|
+
s.command = None
|
|
146
|
+
s.outds = None
|
|
147
|
+
s.inds = {}
|
|
148
|
+
s.global_outfiles = []
|
|
149
|
+
s.jobs = []
|
|
150
|
+
s.primaryds = None
|
|
151
|
+
if fname:
|
|
152
|
+
s.dom = xml.dom.minidom.parse(fname)
|
|
153
|
+
s.parse()
|
|
154
|
+
s.check()
|
|
155
|
+
if xmlStr is not None:
|
|
156
|
+
s.dom = xml.dom.minidom.parseString(xmlStr)
|
|
157
|
+
s.parse()
|
|
158
|
+
s.check()
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def true(v):
|
|
162
|
+
"""define True"""
|
|
163
|
+
return v in ("1", "true", "True", "TRUE", "yes", "Yes", "YES")
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def text(pnode):
|
|
167
|
+
"""extracts the value stored in the node"""
|
|
168
|
+
rc = []
|
|
169
|
+
for node in pnode.childNodes:
|
|
170
|
+
if node.nodeType == node.TEXT_NODE:
|
|
171
|
+
rc.append(str(node.data).strip())
|
|
172
|
+
return "".join(rc)
|
|
173
|
+
|
|
174
|
+
def parse(s):
|
|
175
|
+
"""loads submission configuration from an xml file"""
|
|
176
|
+
try:
|
|
177
|
+
# general settings
|
|
178
|
+
if len(s.dom.getElementsByTagName("title")) > 0:
|
|
179
|
+
s.title = dom_parser.text(s.dom.getElementsByTagName("title")[0])
|
|
180
|
+
else:
|
|
181
|
+
s.title = "Default title"
|
|
182
|
+
if len(s.dom.getElementsByTagName("tag")) > 0:
|
|
183
|
+
s.tag = dom_parser.text(s.dom.getElementsByTagName("tag")[0])
|
|
184
|
+
else:
|
|
185
|
+
s.tag = "default_tag"
|
|
186
|
+
s.command = None # can be overridden in subjobs
|
|
187
|
+
for elm in s.dom.getElementsByTagName("submission")[0].childNodes:
|
|
188
|
+
if elm.nodeName != "command":
|
|
189
|
+
continue
|
|
190
|
+
s.command = dom_parser.text(elm)
|
|
191
|
+
break
|
|
192
|
+
s.global_outfiles = [] # subjobs can append *additional* outputs
|
|
193
|
+
for elm in s.dom.getElementsByTagName("submission")[0].childNodes:
|
|
194
|
+
if elm.nodeName != "output":
|
|
195
|
+
continue
|
|
196
|
+
s.global_outfiles.append(dom_parser.text(elm))
|
|
197
|
+
s.outds = dom_parser.text(s.dom.getElementsByTagName("outds")[0])
|
|
198
|
+
# declaration of all input datasets
|
|
199
|
+
primarydss = []
|
|
200
|
+
for elm in s.dom.getElementsByTagName("submission")[0].childNodes:
|
|
201
|
+
if elm.nodeName != "inds":
|
|
202
|
+
continue
|
|
203
|
+
if "primary" in elm.attributes.keys() and dom_parser.true(elm.attributes["primary"].value):
|
|
204
|
+
primary = True
|
|
205
|
+
else:
|
|
206
|
+
primary = False
|
|
207
|
+
stream = dom_parser.text(elm.getElementsByTagName("stream")[0])
|
|
208
|
+
name = dom_parser.text(elm.getElementsByTagName("name")[0])
|
|
209
|
+
s.inds[name] = stream
|
|
210
|
+
if primary:
|
|
211
|
+
primarydss.append(name)
|
|
212
|
+
# see if one of the input datasets was explicitly labeled as inDS
|
|
213
|
+
if len(primarydss) == 1:
|
|
214
|
+
s.primaryds = primarydss[0]
|
|
215
|
+
else:
|
|
216
|
+
s.primaryds = None
|
|
217
|
+
for job in s.dom.getElementsByTagName("job"):
|
|
218
|
+
s.jobs.append(dom_job(job, primaryds=s.primaryds, defaultcmd=s.command, defaultout=s.global_outfiles))
|
|
219
|
+
except Exception:
|
|
220
|
+
print("ERROR: failed to parse" + " " + s.fname)
|
|
221
|
+
raise
|
|
222
|
+
|
|
223
|
+
def to_dom(s):
|
|
224
|
+
"""Converts this submission to a dom tree branch"""
|
|
225
|
+
x = xml.dom.minidom.Document()
|
|
226
|
+
submission = x.createElement("submission")
|
|
227
|
+
if s.title:
|
|
228
|
+
submission.appendChild(x.createElement("title"))
|
|
229
|
+
submission.childNodes[-1].appendChild(x.createTextNode(s.title))
|
|
230
|
+
if s.tag:
|
|
231
|
+
submission.appendChild(x.createElement("tag"))
|
|
232
|
+
submission.childNodes[-1].appendChild(x.createTextNode(s.tag))
|
|
233
|
+
for name, stream in s.inds.items():
|
|
234
|
+
submission.appendChild(x.createElement("inds"))
|
|
235
|
+
if name == s.primaryds:
|
|
236
|
+
submission.childNodes[-1].setAttribute("primary", "true")
|
|
237
|
+
else:
|
|
238
|
+
submission.childNodes[-1].setAttribute("primary", "false")
|
|
239
|
+
submission.childNodes[-1].appendChild(x.createElement("stream"))
|
|
240
|
+
submission.childNodes[-1].childNodes[-1].appendChild(x.createTextNode(stream))
|
|
241
|
+
submission.childNodes[-1].appendChild(x.createElement("name"))
|
|
242
|
+
submission.childNodes[-1].childNodes[-1].appendChild(x.createTextNode(name))
|
|
243
|
+
if s.command:
|
|
244
|
+
submission.appendChild(x.createElement("command"))
|
|
245
|
+
submission.childNodes[-1].appendChild(x.createTextNode(s.command))
|
|
246
|
+
for outfile in s.global_outfiles:
|
|
247
|
+
submission.appendChild(x.createElement("output"))
|
|
248
|
+
submission.childNodes[-1].appendChild(x.createTextNode(outfile))
|
|
249
|
+
submission.appendChild(x.createElement("outds"))
|
|
250
|
+
submission.childNodes[-1].appendChild(x.createTextNode(s.outds))
|
|
251
|
+
for job in s.jobs:
|
|
252
|
+
submission.appendChild(job.to_dom())
|
|
253
|
+
return submission
|
|
254
|
+
|
|
255
|
+
def check(s):
|
|
256
|
+
"""checks that all output files have unique qualifiers"""
|
|
257
|
+
quals = []
|
|
258
|
+
for j in s.jobs:
|
|
259
|
+
quals += j.outputs_list(True)
|
|
260
|
+
if len(list(set(quals))) != len(quals):
|
|
261
|
+
print("ERROR: found non-unique output file names across the jobs")
|
|
262
|
+
print("(you likely need to review xml options with prepend=true)")
|
|
263
|
+
sys.exit(0)
|
|
264
|
+
|
|
265
|
+
def input_datasets(s):
|
|
266
|
+
"""returns a list of all used input datasets"""
|
|
267
|
+
DSs = set()
|
|
268
|
+
for j in s.jobs:
|
|
269
|
+
for ds in j.infiles.keys():
|
|
270
|
+
DSs.add(ds)
|
|
271
|
+
return list(DSs)
|
|
272
|
+
|
|
273
|
+
def inDS(s):
|
|
274
|
+
"""chooses a dataset we'll call inDS; others will become secondaryDS"""
|
|
275
|
+
# user manually labeled one of datasets as primary, so make it inDS:
|
|
276
|
+
if s.primaryds:
|
|
277
|
+
return s.primaryds
|
|
278
|
+
# OR: choose inDS dataset randomly
|
|
279
|
+
else:
|
|
280
|
+
return s.input_datasets()[0]
|
|
281
|
+
|
|
282
|
+
def secondaryDSs(s):
|
|
283
|
+
"""returns all secondaryDSs. This excludes inDS, unless inDS is managed by prun"""
|
|
284
|
+
return [d for d in s.input_datasets() if d != s.inDS()]
|
|
285
|
+
|
|
286
|
+
def writeInputToTxt(s):
|
|
287
|
+
"""Prepares prun option --writeInputToTxt
|
|
288
|
+
comma-separated list of STREAM:STREAM.files.dat
|
|
289
|
+
"""
|
|
290
|
+
out = []
|
|
291
|
+
DSs = s.secondaryDSs()
|
|
292
|
+
for i, DS in enumerate(DSs):
|
|
293
|
+
if DS in s.inds:
|
|
294
|
+
stream = s.inds[DS]
|
|
295
|
+
else:
|
|
296
|
+
stream = "IN%d" % (i + 1,)
|
|
297
|
+
out.append(f"{stream}:{stream}.files.dat")
|
|
298
|
+
out.append("IN:IN.files.dat")
|
|
299
|
+
return ",".join(out)
|
|
300
|
+
|
|
301
|
+
def files_in_DS(s, DS, regex=False):
|
|
302
|
+
"""Returns a list of all files from a given dataset
|
|
303
|
+
that will be used in at least one job in this submission
|
|
304
|
+
If regex==True, the list is converted to a regex string
|
|
305
|
+
"""
|
|
306
|
+
assert DS in s.input_datasets(), f"ERROR: dataset {DS} was not requested in the xml file"
|
|
307
|
+
files = []
|
|
308
|
+
for j in s.jobs:
|
|
309
|
+
if DS in j.infiles.keys():
|
|
310
|
+
files += j.infiles[DS]
|
|
311
|
+
if regex:
|
|
312
|
+
return "|".join(sorted(list(set(files))))
|
|
313
|
+
else:
|
|
314
|
+
return sorted(list(set(files)))
|
|
315
|
+
|
|
316
|
+
def nJobs(s):
|
|
317
|
+
return len(s.jobs)
|
|
318
|
+
|
|
319
|
+
def dump(s, verbose=True):
|
|
320
|
+
"""prints a summary of this submission"""
|
|
321
|
+
|
|
322
|
+
def P(key, value=""):
|
|
323
|
+
if value == "":
|
|
324
|
+
print(key)
|
|
325
|
+
else:
|
|
326
|
+
print((key + ":").ljust(14) + " " + value)
|
|
327
|
+
|
|
328
|
+
P("XML FILE LOADED", s.fname)
|
|
329
|
+
P("Title", s.title)
|
|
330
|
+
P("Command", s.command)
|
|
331
|
+
P("InDS", s.inDS())
|
|
332
|
+
P("Output DS", s.outds)
|
|
333
|
+
P("njobs", s.nJobs())
|
|
334
|
+
if verbose:
|
|
335
|
+
for i, job in enumerate(s.jobs):
|
|
336
|
+
P("===============> JOB%d" % i)
|
|
337
|
+
P("command", job.exec_string())
|
|
338
|
+
P("outfiles", job.outputs())
|
|
339
|
+
P("INPUTS:")
|
|
340
|
+
j = 0
|
|
341
|
+
for dsname, files in job.infiles.items():
|
|
342
|
+
P(" Dataset%d" % j, dsname)
|
|
343
|
+
for k, fname in enumerate(files):
|
|
344
|
+
P(" File%d" % k, fname)
|
|
345
|
+
j += 1
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
if __name__ == "__main__":
|
|
349
|
+
p = dom_parser("./job.xml")
|
|
350
|
+
p.dump()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Derived FileSpec to preserve changed attributes in pickle as it is impossible to change FileSpec.reserveChangedState
|
|
2
|
+
# consistently with all clients at the same time
|
|
3
|
+
|
|
4
|
+
from pandaserver.taskbuffer.FileSpec import FileSpec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PickleFileSpec(FileSpec):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
FileSpec.__init__(self)
|
|
10
|
+
object.__setattr__(self, "_reserveChangedState", True)
|
|
11
|
+
|
|
12
|
+
def update(self, spec):
|
|
13
|
+
spec._reserveChangedState = True
|
|
14
|
+
self.__setstate__(spec.__getstate__())
|
|
15
|
+
|
|
16
|
+
def __setstate__(self, state):
|
|
17
|
+
object.__setattr__(self, "_reserveChangedState", True)
|
|
18
|
+
FileSpec.__setstate__(self, state)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Derived JobSpec to preserve changed attributes in pickle as it is impossible to change JobSpec.reserveChangedState
|
|
2
|
+
# consistently with all clients at the same time
|
|
3
|
+
|
|
4
|
+
from pandaserver.taskbuffer.JobSpec import JobSpec
|
|
5
|
+
from pandaserver.taskbuffer.PickleFileSpec import PickleFileSpec
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PickleJobSpec(JobSpec):
|
|
9
|
+
def __init__(self):
|
|
10
|
+
JobSpec.__init__(self)
|
|
11
|
+
object.__setattr__(self, "_reserveChangedState", True)
|
|
12
|
+
|
|
13
|
+
def update(self, spec):
|
|
14
|
+
spec._reserveChangedState = True
|
|
15
|
+
self.__setstate__(spec.__getstate__())
|
|
16
|
+
p_file_list = []
|
|
17
|
+
for file in self.Files:
|
|
18
|
+
p_file = PickleFileSpec()
|
|
19
|
+
p_file.update(file)
|
|
20
|
+
p_file_list.append(p_file)
|
|
21
|
+
object.__setattr__(self, "Files", p_file_list)
|
|
22
|
+
|
|
23
|
+
def __setstate__(self, state):
|
|
24
|
+
object.__setattr__(self, "_reserveChangedState", True)
|
|
25
|
+
JobSpec.__setstate__(self, state)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# special permission
|
|
5
|
+
PERMISSION_KEY = "k"
|
|
6
|
+
PERMISSION_PROXY = "p"
|
|
7
|
+
PERMISSION_TOKEN_KEY = "t"
|
|
8
|
+
PERMISSION_SUPER_USER = "s"
|
|
9
|
+
PERMISSION_SUPER_GROUP = "g"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# convert UTF-8 to ASCII in json dumps
|
|
13
|
+
# This is needed for Python 2, but not for Python 3
|
|
14
|
+
def unicodeConvert(input):
|
|
15
|
+
if isinstance(input, dict):
|
|
16
|
+
retMap = {}
|
|
17
|
+
for tmpKey in input:
|
|
18
|
+
tmpVal = input[tmpKey]
|
|
19
|
+
retMap[unicodeConvert(tmpKey)] = unicodeConvert(tmpVal)
|
|
20
|
+
return retMap
|
|
21
|
+
elif isinstance(input, list):
|
|
22
|
+
retList = []
|
|
23
|
+
for tmpItem in input:
|
|
24
|
+
retList.append(unicodeConvert(tmpItem))
|
|
25
|
+
return retList
|
|
26
|
+
elif isinstance(input, str) and sys.version_info[0] == 2:
|
|
27
|
+
return input.encode("utf-8")
|
|
28
|
+
return input
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# decode
|
|
32
|
+
# This is needed for Python 2, but not for Python 3
|
|
33
|
+
def decodeJSON(inputStr):
|
|
34
|
+
return json.loads(inputStr, object_hook=unicodeConvert)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# calculate priority for user jobs
|
|
38
|
+
def calculatePriority(priorityOffset, serNum, weight):
|
|
39
|
+
priority = int(1000 + priorityOffset - (serNum / 5) - int(100 * weight))
|
|
40
|
+
return priority
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from pandaserver.taskbuffer import JobUtils
|
|
2
|
+
|
|
3
|
+
processGroups = [
|
|
4
|
+
("others", []),
|
|
5
|
+
("evgen", ["evgen"]),
|
|
6
|
+
("simul", ["simul"]),
|
|
7
|
+
("reprocessing", ["reprocessing"]),
|
|
8
|
+
("test", ["prod_test", "validation"] + JobUtils.list_ptest_prod_sources),
|
|
9
|
+
("mcore", ["mcore"]),
|
|
10
|
+
("group", ["group"]),
|
|
11
|
+
("deriv", ["deriv"]),
|
|
12
|
+
("pile", ["pile"]),
|
|
13
|
+
("merge", ["merge"]),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
# ('evgensimul', ['evgen','simul']),
|
|
17
|
+
|
|
18
|
+
# maximum number of debug jobs per user
|
|
19
|
+
maxDebugJobs = 3
|
|
20
|
+
|
|
21
|
+
# maximum number of debug jobs for prod role
|
|
22
|
+
maxDebugProdJobs = 30
|
|
23
|
+
|
|
24
|
+
# maximum number of debug jobs for working group
|
|
25
|
+
maxDebugWgJobs = 10
|
|
26
|
+
|
|
27
|
+
# extension level for GP
|
|
28
|
+
extensionLevel_1 = 1
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# get corresponding group
|
|
32
|
+
def getProcessGroup(valGroup):
|
|
33
|
+
tmpGroup = None
|
|
34
|
+
for tmpKey, tmpList in processGroups:
|
|
35
|
+
# set default
|
|
36
|
+
if tmpGroup is None:
|
|
37
|
+
tmpGroup = tmpKey
|
|
38
|
+
continue
|
|
39
|
+
if valGroup in tmpList:
|
|
40
|
+
tmpGroup = tmpKey
|
|
41
|
+
break
|
|
42
|
+
# return
|
|
43
|
+
return tmpGroup
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# convert cloud and processingType for extended PG
|
|
47
|
+
def converCPTforEPG(cloud, processingType, coreCount, workingGroup=None):
|
|
48
|
+
if coreCount in [0, 1, None]:
|
|
49
|
+
# use group queue for GP jobs
|
|
50
|
+
if workingGroup is not None and workingGroup.startswith("GP_"):
|
|
51
|
+
return cloud, "group"
|
|
52
|
+
return cloud, processingType
|
|
53
|
+
else:
|
|
54
|
+
# use MCORE queue for MPC jobs in all clouds
|
|
55
|
+
return cloud, "mcore"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# count the number of jobs per group
|
|
59
|
+
def countJobsPerGroup(valMap):
|
|
60
|
+
ret = {}
|
|
61
|
+
# loop over all clouds
|
|
62
|
+
for cloud in valMap:
|
|
63
|
+
cloudVal = valMap[cloud]
|
|
64
|
+
# add cloud
|
|
65
|
+
ret.setdefault(cloud, {})
|
|
66
|
+
# loop over all sites
|
|
67
|
+
for site in cloudVal:
|
|
68
|
+
siteVal = cloudVal[site]
|
|
69
|
+
# add site
|
|
70
|
+
ret[cloud].setdefault(site, {})
|
|
71
|
+
# loop over all types
|
|
72
|
+
for pType in siteVal:
|
|
73
|
+
typeVal = siteVal[pType]
|
|
74
|
+
# get process group
|
|
75
|
+
tmpGroup = getProcessGroup(pType)
|
|
76
|
+
# add group
|
|
77
|
+
ret[cloud][site].setdefault(tmpGroup, {})
|
|
78
|
+
# loop over all status
|
|
79
|
+
for jobStatus in typeVal:
|
|
80
|
+
statVal = typeVal[jobStatus]
|
|
81
|
+
ret[cloud][site][tmpGroup].setdefault(jobStatus, 0)
|
|
82
|
+
# add
|
|
83
|
+
ret[cloud][site][tmpGroup][jobStatus] += statVal
|
|
84
|
+
# return
|
|
85
|
+
return ret
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# count the number of jobs per group for analysis
|
|
89
|
+
def countJobsPerGroupForAnal(valMap):
|
|
90
|
+
ret = {}
|
|
91
|
+
# loop over all sites
|
|
92
|
+
for site in valMap:
|
|
93
|
+
siteVal = valMap[site]
|
|
94
|
+
# add site
|
|
95
|
+
ret.setdefault(site, {})
|
|
96
|
+
# loop over all types
|
|
97
|
+
for pType in siteVal:
|
|
98
|
+
typeVal = siteVal[pType]
|
|
99
|
+
# get process group
|
|
100
|
+
tmpGroup = getProcessGroup(pType)
|
|
101
|
+
# add group
|
|
102
|
+
if tmpGroup not in ret[site]:
|
|
103
|
+
ret[site][tmpGroup] = {}
|
|
104
|
+
# loop over all status
|
|
105
|
+
for jobStatus in typeVal:
|
|
106
|
+
statVal = typeVal[jobStatus]
|
|
107
|
+
ret[site][tmpGroup].setdefault(jobStatus, 0)
|
|
108
|
+
# add
|
|
109
|
+
ret[site][tmpGroup][jobStatus] += statVal
|
|
110
|
+
# return
|
|
111
|
+
return ret
|