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,1799 @@
|
|
|
1
|
+
"""
|
|
2
|
+
provide web interface to users
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
import time
|
|
11
|
+
import traceback
|
|
12
|
+
|
|
13
|
+
from pandacommon.pandalogger.LogWrapper import LogWrapper
|
|
14
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
15
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
16
|
+
|
|
17
|
+
import pandaserver.jobdispatcher.Protocol as Protocol
|
|
18
|
+
from pandaserver.brokerage.SiteMapper import SiteMapper
|
|
19
|
+
from pandaserver.config import panda_config
|
|
20
|
+
from pandaserver.dataservice.ddm import rucioAPI
|
|
21
|
+
from pandaserver.srvcore import CoreUtils
|
|
22
|
+
from pandaserver.srvcore.CoreUtils import clean_user_id, resolve_bool
|
|
23
|
+
from pandaserver.taskbuffer import JobUtils, PrioUtil
|
|
24
|
+
from pandaserver.taskbuffer.JediTaskSpec import JediTaskSpec
|
|
25
|
+
from pandaserver.taskbuffer.WrappedPickle import WrappedPickle
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import idds.common.constants
|
|
29
|
+
import idds.common.utils
|
|
30
|
+
from idds.client.client import Client as iDDS_Client
|
|
31
|
+
from idds.client.clientmanager import ClientManager as iDDS_ClientManager
|
|
32
|
+
except ImportError:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
MESSAGE_SSL = "SSL secure connection is required"
|
|
36
|
+
MESSAGE_PROD_ROLE = "production or pilot role required"
|
|
37
|
+
MESSAGE_TASK_ID = "jediTaskID must be an integer"
|
|
38
|
+
MESSAGE_DATABASE = "database error in the PanDA server"
|
|
39
|
+
MESSAGE_JSON = "failed to load JSON"
|
|
40
|
+
|
|
41
|
+
CODE_SSL = 100
|
|
42
|
+
CODE_LOGIC = 101
|
|
43
|
+
CODE_OTHER_PARAMS = 102
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
_logger = PandaLogger().getLogger("UserIF")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def resolve_true(variable):
|
|
50
|
+
return variable == "True"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def resolve_false(variable):
|
|
54
|
+
return variable != "False"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# main class
|
|
58
|
+
class UserIF:
|
|
59
|
+
# constructor
|
|
60
|
+
def __init__(self):
|
|
61
|
+
self.taskBuffer = None
|
|
62
|
+
|
|
63
|
+
# initialize
|
|
64
|
+
def init(self, taskBuffer):
|
|
65
|
+
self.taskBuffer = taskBuffer
|
|
66
|
+
|
|
67
|
+
# submit jobs
|
|
68
|
+
def submitJobs(self, jobsStr, user, host, userFQANs, prodRole=False, toPending=False):
|
|
69
|
+
try:
|
|
70
|
+
# deserialize jobspecs
|
|
71
|
+
try:
|
|
72
|
+
jobs = WrappedPickle.loads(jobsStr)
|
|
73
|
+
except Exception:
|
|
74
|
+
jobs = JobUtils.load_jobs_json(jobsStr)
|
|
75
|
+
_logger.debug(f"submitJobs {user} len:{len(jobs)} prodRole={prodRole} FQAN:{str(userFQANs)}")
|
|
76
|
+
maxJobs = 5000
|
|
77
|
+
if len(jobs) > maxJobs:
|
|
78
|
+
_logger.error(f"submitJobs: too many jobs more than {maxJobs}")
|
|
79
|
+
jobs = jobs[:maxJobs]
|
|
80
|
+
except Exception as ex:
|
|
81
|
+
_logger.error(f"submitJobs : {str(ex)} {traceback.format_exc()}")
|
|
82
|
+
jobs = []
|
|
83
|
+
# check prodSourceLabel
|
|
84
|
+
try:
|
|
85
|
+
good_labels = True
|
|
86
|
+
for tmpJob in jobs:
|
|
87
|
+
# check production role
|
|
88
|
+
if tmpJob.prodSourceLabel in ["managed"] and not prodRole:
|
|
89
|
+
good_labels = False
|
|
90
|
+
good_labels_message = f"submitJobs {user} missing prod-role for prodSourceLabel={tmpJob.prodSourceLabel}"
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
# check the job_label is valid
|
|
94
|
+
if tmpJob.job_label not in [None, "", "NULL"] and tmpJob.job_label not in JobUtils.job_labels:
|
|
95
|
+
good_labels = False
|
|
96
|
+
good_labels_message = f"submitJobs {user} wrong job_label={tmpJob.job_label}"
|
|
97
|
+
break
|
|
98
|
+
except Exception:
|
|
99
|
+
err_type, err_value = sys.exc_info()[:2]
|
|
100
|
+
_logger.error(f"submitJobs : checking good_labels {err_type} {err_value}")
|
|
101
|
+
good_labels = False
|
|
102
|
+
|
|
103
|
+
# reject injection for error with the labels
|
|
104
|
+
if not good_labels:
|
|
105
|
+
_logger.error(good_labels_message)
|
|
106
|
+
return f"ERROR: {good_labels_message}"
|
|
107
|
+
|
|
108
|
+
job0 = None
|
|
109
|
+
|
|
110
|
+
# get user VO
|
|
111
|
+
userVO = "atlas"
|
|
112
|
+
try:
|
|
113
|
+
job0 = jobs[0]
|
|
114
|
+
if job0.VO not in [None, "", "NULL"]:
|
|
115
|
+
userVO = job0.VO
|
|
116
|
+
except (IndexError, AttributeError) as e:
|
|
117
|
+
_logger.error(f"submitJobs : checking userVO. userVO not found, defaulting to {userVO}. (Exception {e})")
|
|
118
|
+
|
|
119
|
+
# atlas jobs require FQANs
|
|
120
|
+
if userVO == "atlas" and userFQANs == []:
|
|
121
|
+
_logger.error(f"submitJobs : VOMS FQANs are missing in your proxy. They are required for {userVO}")
|
|
122
|
+
# return "ERROR: VOMS FQANs are missing. They are required for {0}".format(userVO)
|
|
123
|
+
|
|
124
|
+
# get LSST pipeline username
|
|
125
|
+
if userVO.lower() == "lsst":
|
|
126
|
+
try:
|
|
127
|
+
if job0.prodUserName and job0.prodUserName.lower() != "none":
|
|
128
|
+
user = job0.prodUserName
|
|
129
|
+
except AttributeError:
|
|
130
|
+
_logger.error(f"submitJobs : checking username for userVO {userVO}: username not found, defaulting to {user}.")
|
|
131
|
+
|
|
132
|
+
# store jobs
|
|
133
|
+
ret = self.taskBuffer.storeJobs(
|
|
134
|
+
jobs,
|
|
135
|
+
user,
|
|
136
|
+
fqans=userFQANs,
|
|
137
|
+
hostname=host,
|
|
138
|
+
toPending=toPending,
|
|
139
|
+
userVO=userVO,
|
|
140
|
+
)
|
|
141
|
+
_logger.debug(f"submitJobs {user} ->:{len(ret)}")
|
|
142
|
+
|
|
143
|
+
# serialize
|
|
144
|
+
return WrappedPickle.dumps(ret)
|
|
145
|
+
|
|
146
|
+
# set debug mode
|
|
147
|
+
def setDebugMode(self, dn, pandaID, prodManager, modeOn, workingGroup):
|
|
148
|
+
ret = self.taskBuffer.setDebugMode(dn, pandaID, prodManager, modeOn, workingGroup)
|
|
149
|
+
# return
|
|
150
|
+
return ret
|
|
151
|
+
|
|
152
|
+
# insert sandbox file info
|
|
153
|
+
def insertSandboxFileInfo(self, userName, hostName, fileName, fileSize, checkSum):
|
|
154
|
+
ret = self.taskBuffer.insertSandboxFileInfo(userName, hostName, fileName, fileSize, checkSum)
|
|
155
|
+
# return
|
|
156
|
+
return ret
|
|
157
|
+
|
|
158
|
+
# check duplicated sandbox file
|
|
159
|
+
def checkSandboxFile(self, userName, fileSize, checkSum):
|
|
160
|
+
ret = self.taskBuffer.checkSandboxFile(userName, fileSize, checkSum)
|
|
161
|
+
# return
|
|
162
|
+
return ret
|
|
163
|
+
|
|
164
|
+
# get job status
|
|
165
|
+
def getJobStatus(self, idsStr, use_json, no_pickle=False):
|
|
166
|
+
try:
|
|
167
|
+
# deserialize IDs
|
|
168
|
+
if use_json or no_pickle:
|
|
169
|
+
ids = json.loads(idsStr)
|
|
170
|
+
else:
|
|
171
|
+
ids = WrappedPickle.loads(idsStr)
|
|
172
|
+
_logger.debug(f"getJobStatus len : {len(ids)}")
|
|
173
|
+
maxIDs = 5500
|
|
174
|
+
if len(ids) > maxIDs:
|
|
175
|
+
_logger.error(f"too long ID list more than {maxIDs}")
|
|
176
|
+
ids = ids[:maxIDs]
|
|
177
|
+
except Exception:
|
|
178
|
+
type, value, traceBack = sys.exc_info()
|
|
179
|
+
_logger.error(f"getJobStatus : {type} {value}")
|
|
180
|
+
ids = []
|
|
181
|
+
_logger.debug(f"getJobStatus start : {str(ids)} json={use_json}")
|
|
182
|
+
# peek jobs
|
|
183
|
+
ret = self.taskBuffer.peekJobs(ids, use_json=use_json)
|
|
184
|
+
_logger.debug("getJobStatus end")
|
|
185
|
+
# serialize
|
|
186
|
+
if use_json:
|
|
187
|
+
return json.dumps(ret)
|
|
188
|
+
if no_pickle:
|
|
189
|
+
return JobUtils.dump_jobs_json(ret)
|
|
190
|
+
return WrappedPickle.dumps(ret)
|
|
191
|
+
|
|
192
|
+
# get PandaIDs with TaskID
|
|
193
|
+
def getPandaIDsWithTaskID(self, jediTaskID):
|
|
194
|
+
# get PandaIDs
|
|
195
|
+
ret = self.taskBuffer.getPandaIDsWithTaskID(jediTaskID)
|
|
196
|
+
# serialize
|
|
197
|
+
return WrappedPickle.dumps(ret)
|
|
198
|
+
|
|
199
|
+
# get job statistics per site
|
|
200
|
+
def getJobStatisticsPerSite(self):
|
|
201
|
+
ret = self.taskBuffer.getJobStatistics()
|
|
202
|
+
return WrappedPickle.dumps(ret, convert_to_safe=True)
|
|
203
|
+
|
|
204
|
+
# get job statistics per site, source label, and resource type
|
|
205
|
+
def get_job_statistics_per_site_label_resource(self, time_window):
|
|
206
|
+
ret = self.taskBuffer.get_job_statistics_per_site_label_resource(time_window)
|
|
207
|
+
return json.dumps(ret)
|
|
208
|
+
|
|
209
|
+
# kill jobs
|
|
210
|
+
def killJobs(self, idsStr, user, host, code, prodManager, useMailAsID, fqans, killOpts=[]):
|
|
211
|
+
# deserialize IDs
|
|
212
|
+
ids = WrappedPickle.loads(idsStr)
|
|
213
|
+
if not isinstance(ids, list):
|
|
214
|
+
ids = [ids]
|
|
215
|
+
_logger.info(f"killJob : {user} {code} {prodManager} {fqans} {ids}")
|
|
216
|
+
try:
|
|
217
|
+
if useMailAsID:
|
|
218
|
+
_logger.debug(f"killJob : getting mail address for {user}")
|
|
219
|
+
nTry = 3
|
|
220
|
+
for iDDMTry in range(nTry):
|
|
221
|
+
status, userInfo = rucioAPI.finger(user)
|
|
222
|
+
if status:
|
|
223
|
+
_logger.debug(f"killJob : {user} is converted to {userInfo['email']}")
|
|
224
|
+
user = userInfo["email"]
|
|
225
|
+
break
|
|
226
|
+
time.sleep(1)
|
|
227
|
+
except Exception:
|
|
228
|
+
err_type, err_value = sys.exc_info()[:2]
|
|
229
|
+
_logger.error(f"killJob : failed to convert email address {user} : {err_type} {err_value}")
|
|
230
|
+
# get working groups with prod role
|
|
231
|
+
wgProdRole = []
|
|
232
|
+
for fqan in fqans:
|
|
233
|
+
tmpMatch = re.search("/atlas/([^/]+)/Role=production", fqan)
|
|
234
|
+
if tmpMatch is not None:
|
|
235
|
+
# ignore usatlas since it is used as atlas prod role
|
|
236
|
+
tmpWG = tmpMatch.group(1)
|
|
237
|
+
if tmpWG not in ["", "usatlas"] + wgProdRole:
|
|
238
|
+
wgProdRole.append(tmpWG)
|
|
239
|
+
# group production
|
|
240
|
+
wgProdRole.append(f"gr_{tmpWG}")
|
|
241
|
+
# kill jobs
|
|
242
|
+
ret = self.taskBuffer.killJobs(ids, user, code, prodManager, wgProdRole, killOpts)
|
|
243
|
+
return WrappedPickle.dumps(ret)
|
|
244
|
+
|
|
245
|
+
# reassign jobs
|
|
246
|
+
def reassignJobs(self, idsStr, user, host, forPending, firstSubmission):
|
|
247
|
+
# deserialize IDs
|
|
248
|
+
ids = WrappedPickle.loads(idsStr)
|
|
249
|
+
# reassign jobs
|
|
250
|
+
ret = self.taskBuffer.reassignJobs(
|
|
251
|
+
ids,
|
|
252
|
+
forPending=forPending,
|
|
253
|
+
firstSubmission=firstSubmission,
|
|
254
|
+
)
|
|
255
|
+
return WrappedPickle.dumps(ret)
|
|
256
|
+
|
|
257
|
+
# get script for offline running
|
|
258
|
+
def getScriptOfflineRunning(self, pandaID, days=None):
|
|
259
|
+
ret = self.taskBuffer.getScriptOfflineRunning(pandaID, days)
|
|
260
|
+
return ret
|
|
261
|
+
|
|
262
|
+
# get ban users
|
|
263
|
+
def get_ban_users(self):
|
|
264
|
+
ret = self.taskBuffer.get_ban_users()
|
|
265
|
+
return json.dumps(ret)
|
|
266
|
+
|
|
267
|
+
# get active JediTasks in a time range
|
|
268
|
+
def getJediTasksInTimeRange(self, dn, timeRange, fullFlag, minTaskID, task_type):
|
|
269
|
+
ret = self.taskBuffer.getJediTasksInTimeRange(dn, timeRange, fullFlag, minTaskID, task_type)
|
|
270
|
+
return WrappedPickle.dumps(ret)
|
|
271
|
+
|
|
272
|
+
# get details of JediTask
|
|
273
|
+
def getJediTaskDetails(self, jediTaskID, fullFlag, withTaskInfo):
|
|
274
|
+
ret = self.taskBuffer.getJediTaskDetails(jediTaskID, fullFlag, withTaskInfo)
|
|
275
|
+
return WrappedPickle.dumps(ret)
|
|
276
|
+
|
|
277
|
+
# get full job status
|
|
278
|
+
def getFullJobStatus(self, idsStr, dn):
|
|
279
|
+
try:
|
|
280
|
+
# deserialize jobspecs
|
|
281
|
+
ids = WrappedPickle.loads(idsStr)
|
|
282
|
+
# truncate
|
|
283
|
+
maxIDs = 5500
|
|
284
|
+
if len(ids) > maxIDs:
|
|
285
|
+
_logger.error(f"getFullJobStatus: too long ID list more than {maxIDs}")
|
|
286
|
+
ids = ids[:maxIDs]
|
|
287
|
+
except Exception:
|
|
288
|
+
type, value, traceBack = sys.exc_info()
|
|
289
|
+
_logger.error(f"getFullJobStatus : {type} {value}")
|
|
290
|
+
ids = []
|
|
291
|
+
_logger.debug(f"getFullJobStatus start : {dn} {str(ids)}")
|
|
292
|
+
# peek jobs
|
|
293
|
+
ret = self.taskBuffer.getFullJobStatus(ids)
|
|
294
|
+
_logger.debug("getFullJobStatus end")
|
|
295
|
+
return WrappedPickle.dumps(ret)
|
|
296
|
+
|
|
297
|
+
# insert task params
|
|
298
|
+
def insertTaskParams(self, taskParams, user, prodRole, fqans, properErrorCode, parent_tid):
|
|
299
|
+
ret = self.taskBuffer.insertTaskParamsPanda(
|
|
300
|
+
taskParams,
|
|
301
|
+
user,
|
|
302
|
+
prodRole,
|
|
303
|
+
fqans,
|
|
304
|
+
properErrorCode=properErrorCode,
|
|
305
|
+
parent_tid=parent_tid,
|
|
306
|
+
)
|
|
307
|
+
return ret
|
|
308
|
+
|
|
309
|
+
# kill task
|
|
310
|
+
def killTask(self, jediTaskID, user, prodRole, properErrorCode, broadcast):
|
|
311
|
+
ret = self.taskBuffer.sendCommandTaskPanda(
|
|
312
|
+
jediTaskID,
|
|
313
|
+
user,
|
|
314
|
+
prodRole,
|
|
315
|
+
"kill",
|
|
316
|
+
properErrorCode=properErrorCode,
|
|
317
|
+
broadcast=broadcast,
|
|
318
|
+
)
|
|
319
|
+
return ret
|
|
320
|
+
|
|
321
|
+
# finish task
|
|
322
|
+
def finishTask(self, jediTaskID, user, prodRole, properErrorCode, qualifier, broadcast):
|
|
323
|
+
ret = self.taskBuffer.sendCommandTaskPanda(
|
|
324
|
+
jediTaskID,
|
|
325
|
+
user,
|
|
326
|
+
prodRole,
|
|
327
|
+
"finish",
|
|
328
|
+
properErrorCode=properErrorCode,
|
|
329
|
+
comQualifier=qualifier,
|
|
330
|
+
broadcast=broadcast,
|
|
331
|
+
)
|
|
332
|
+
return ret
|
|
333
|
+
|
|
334
|
+
# reload input
|
|
335
|
+
def reloadInput(self, jediTaskID, user, prodRole, ignore_hard_exhausted):
|
|
336
|
+
if not prodRole and ignore_hard_exhausted:
|
|
337
|
+
ignore_hard_exhausted = False
|
|
338
|
+
com_qualifier = JediTaskSpec.get_retry_command_qualifiers(ignore_hard_exhausted=ignore_hard_exhausted)
|
|
339
|
+
com_comment = json.dumps([{}, com_qualifier])
|
|
340
|
+
ret = self.taskBuffer.sendCommandTaskPanda(jediTaskID, user, prodRole, "incexec", comComment=com_comment, properErrorCode=True)
|
|
341
|
+
return ret
|
|
342
|
+
|
|
343
|
+
# retry task
|
|
344
|
+
def retryTask(
|
|
345
|
+
self,
|
|
346
|
+
jediTaskID,
|
|
347
|
+
user,
|
|
348
|
+
prodRole,
|
|
349
|
+
properErrorCode,
|
|
350
|
+
newParams,
|
|
351
|
+
noChildRetry,
|
|
352
|
+
discardEvents,
|
|
353
|
+
disable_staging_mode,
|
|
354
|
+
keep_gshare_priority,
|
|
355
|
+
ignore_hard_exhausted,
|
|
356
|
+
):
|
|
357
|
+
# retry with new params
|
|
358
|
+
if newParams is not None:
|
|
359
|
+
try:
|
|
360
|
+
# convert to dict
|
|
361
|
+
newParams = PrioUtil.decodeJSON(newParams)
|
|
362
|
+
# get original params
|
|
363
|
+
taskParams = self.taskBuffer.getTaskParamsPanda(jediTaskID)
|
|
364
|
+
taskParamsJson = PrioUtil.decodeJSON(taskParams)
|
|
365
|
+
# replace with new values
|
|
366
|
+
for newKey in newParams:
|
|
367
|
+
newVal = newParams[newKey]
|
|
368
|
+
taskParamsJson[newKey] = newVal
|
|
369
|
+
taskParams = json.dumps(taskParamsJson)
|
|
370
|
+
# retry with new params
|
|
371
|
+
ret = self.taskBuffer.insertTaskParamsPanda(
|
|
372
|
+
taskParams,
|
|
373
|
+
user,
|
|
374
|
+
prodRole,
|
|
375
|
+
[],
|
|
376
|
+
properErrorCode=properErrorCode,
|
|
377
|
+
allowActiveTask=True,
|
|
378
|
+
)
|
|
379
|
+
except Exception:
|
|
380
|
+
err_type, err_value = sys.exc_info()[:2]
|
|
381
|
+
ret = 1, f"server error with {err_type}:{err_value}"
|
|
382
|
+
else:
|
|
383
|
+
if not prodRole and ignore_hard_exhausted:
|
|
384
|
+
ignore_hard_exhausted = False
|
|
385
|
+
com_qualifier = JediTaskSpec.get_retry_command_qualifiers(
|
|
386
|
+
noChildRetry, discardEvents, disable_staging_mode, keep_gshare_priority, ignore_hard_exhausted
|
|
387
|
+
)
|
|
388
|
+
com_qualifier = " ".join(com_qualifier)
|
|
389
|
+
# normal retry
|
|
390
|
+
ret = self.taskBuffer.sendCommandTaskPanda(
|
|
391
|
+
jediTaskID,
|
|
392
|
+
user,
|
|
393
|
+
prodRole,
|
|
394
|
+
"retry",
|
|
395
|
+
properErrorCode=properErrorCode,
|
|
396
|
+
comQualifier=com_qualifier,
|
|
397
|
+
)
|
|
398
|
+
if properErrorCode is True and ret[0] == 5:
|
|
399
|
+
# retry failed analysis jobs
|
|
400
|
+
jobdefList = self.taskBuffer.getJobdefIDsForFailedJob(jediTaskID)
|
|
401
|
+
cUID = CoreUtils.clean_user_id(user)
|
|
402
|
+
for jobID in jobdefList:
|
|
403
|
+
self.taskBuffer.finalizePendingJobs(cUID, jobID)
|
|
404
|
+
self.taskBuffer.increaseAttemptNrPanda(jediTaskID, 5)
|
|
405
|
+
return_str = f"retry has been triggered for failed jobs while the task is still {ret[1]}"
|
|
406
|
+
if newParams is None:
|
|
407
|
+
ret = 0, return_str
|
|
408
|
+
else:
|
|
409
|
+
ret = 3, return_str
|
|
410
|
+
return ret
|
|
411
|
+
|
|
412
|
+
# reassign task
|
|
413
|
+
def reassignTask(self, jediTaskID, user, prodRole, comComment):
|
|
414
|
+
ret = self.taskBuffer.sendCommandTaskPanda(
|
|
415
|
+
jediTaskID,
|
|
416
|
+
user,
|
|
417
|
+
prodRole,
|
|
418
|
+
"reassign",
|
|
419
|
+
comComment=comComment,
|
|
420
|
+
properErrorCode=True,
|
|
421
|
+
)
|
|
422
|
+
return ret
|
|
423
|
+
|
|
424
|
+
# pause task
|
|
425
|
+
def pauseTask(self, jediTaskID, user, prodRole):
|
|
426
|
+
ret = self.taskBuffer.sendCommandTaskPanda(jediTaskID, user, prodRole, "pause", properErrorCode=True)
|
|
427
|
+
return ret
|
|
428
|
+
|
|
429
|
+
# resume task
|
|
430
|
+
def resumeTask(self, jediTaskID, user, prodRole):
|
|
431
|
+
ret = self.taskBuffer.sendCommandTaskPanda(jediTaskID, user, prodRole, "resume", properErrorCode=True)
|
|
432
|
+
return ret
|
|
433
|
+
|
|
434
|
+
# force avalanche for task
|
|
435
|
+
def avalancheTask(self, jediTaskID, user, prodRole):
|
|
436
|
+
ret = self.taskBuffer.sendCommandTaskPanda(jediTaskID, user, prodRole, "avalanche", properErrorCode=True)
|
|
437
|
+
return ret
|
|
438
|
+
|
|
439
|
+
# send command to task
|
|
440
|
+
def send_command_to_task(self, jedi_task_id, user, prod_role, command_string):
|
|
441
|
+
ret = self.taskBuffer.sendCommandTaskPanda(jedi_task_id, user, prod_role, command_string, properErrorCode=True)
|
|
442
|
+
return ret
|
|
443
|
+
|
|
444
|
+
# change task priority
|
|
445
|
+
def changeTaskPriority(self, jediTaskID, newPriority):
|
|
446
|
+
ret = self.taskBuffer.changeTaskPriorityPanda(jediTaskID, newPriority)
|
|
447
|
+
return ret
|
|
448
|
+
|
|
449
|
+
# increase attempt number for unprocessed files
|
|
450
|
+
def increaseAttemptNrPanda(self, jediTaskID, increasedNr):
|
|
451
|
+
ret = self.taskBuffer.increaseAttemptNrPanda(jediTaskID, increasedNr)
|
|
452
|
+
return ret
|
|
453
|
+
|
|
454
|
+
# change task attribute
|
|
455
|
+
def changeTaskAttributePanda(self, jediTaskID, attrName, attrValue):
|
|
456
|
+
ret = self.taskBuffer.changeTaskAttributePanda(jediTaskID, attrName, attrValue)
|
|
457
|
+
return ret
|
|
458
|
+
|
|
459
|
+
# change split rule for task
|
|
460
|
+
def changeTaskSplitRulePanda(self, jediTaskID, attrName, attrValue):
|
|
461
|
+
ret = self.taskBuffer.changeTaskSplitRulePanda(jediTaskID, attrName, attrValue)
|
|
462
|
+
return ret
|
|
463
|
+
|
|
464
|
+
# reactivate task
|
|
465
|
+
def reactivateTask(self, jediTaskID, keep_attempt_nr, trigger_job_generation):
|
|
466
|
+
ret = self.taskBuffer.reactivateTask(jediTaskID, keep_attempt_nr, trigger_job_generation)
|
|
467
|
+
return ret
|
|
468
|
+
|
|
469
|
+
# get task status
|
|
470
|
+
def getTaskStatus(self, jediTaskID):
|
|
471
|
+
ret = self.taskBuffer.getTaskStatus(jediTaskID)
|
|
472
|
+
return ret[0]
|
|
473
|
+
|
|
474
|
+
# reassign share
|
|
475
|
+
def reassignShare(self, jedi_task_ids, share_dest, reassign_running):
|
|
476
|
+
return self.taskBuffer.reassignShare(jedi_task_ids, share_dest, reassign_running)
|
|
477
|
+
|
|
478
|
+
# get taskParamsMap
|
|
479
|
+
def getTaskParamsMap(self, jediTaskID):
|
|
480
|
+
# get taskParamsMap
|
|
481
|
+
ret = self.taskBuffer.getTaskParamsMap(jediTaskID)
|
|
482
|
+
return ret
|
|
483
|
+
|
|
484
|
+
# update workers
|
|
485
|
+
def updateWorkers(self, user, host, harvesterID, data):
|
|
486
|
+
ret = self.taskBuffer.updateWorkers(harvesterID, data)
|
|
487
|
+
if ret is None:
|
|
488
|
+
return_value = (False, MESSAGE_DATABASE)
|
|
489
|
+
else:
|
|
490
|
+
return_value = (True, ret)
|
|
491
|
+
return json.dumps(return_value)
|
|
492
|
+
|
|
493
|
+
# update workers
|
|
494
|
+
def updateServiceMetrics(self, user, host, harvesterID, data):
|
|
495
|
+
ret = self.taskBuffer.updateServiceMetrics(harvesterID, data)
|
|
496
|
+
if ret is None:
|
|
497
|
+
return_value = (False, MESSAGE_DATABASE)
|
|
498
|
+
else:
|
|
499
|
+
return_value = (True, ret)
|
|
500
|
+
return json.dumps(return_value)
|
|
501
|
+
|
|
502
|
+
# add harvester dialog messages
|
|
503
|
+
def addHarvesterDialogs(self, user, harvesterID, dialogs):
|
|
504
|
+
ret = self.taskBuffer.addHarvesterDialogs(harvesterID, dialogs)
|
|
505
|
+
if not ret:
|
|
506
|
+
return_value = (False, MESSAGE_DATABASE)
|
|
507
|
+
else:
|
|
508
|
+
return_value = (True, "")
|
|
509
|
+
return json.dumps(return_value)
|
|
510
|
+
|
|
511
|
+
# heartbeat for harvester
|
|
512
|
+
def harvesterIsAlive(self, user, host, harvesterID, data):
|
|
513
|
+
ret = self.taskBuffer.harvesterIsAlive(user, host, harvesterID, data)
|
|
514
|
+
if ret is None:
|
|
515
|
+
return_value = (False, MESSAGE_DATABASE)
|
|
516
|
+
else:
|
|
517
|
+
return_value = (True, ret)
|
|
518
|
+
return json.dumps(return_value)
|
|
519
|
+
|
|
520
|
+
# get stats of workers
|
|
521
|
+
def getWorkerStats(self):
|
|
522
|
+
return self.taskBuffer.getWorkerStats()
|
|
523
|
+
|
|
524
|
+
# report stat of workers
|
|
525
|
+
def reportWorkerStats_jobtype(self, harvesterID, siteName, paramsList):
|
|
526
|
+
return self.taskBuffer.reportWorkerStats_jobtype(harvesterID, siteName, paramsList)
|
|
527
|
+
|
|
528
|
+
# set num slots for workload provisioning
|
|
529
|
+
def setNumSlotsForWP(self, pandaQueueName, numSlots, gshare, resourceType, validPeriod):
|
|
530
|
+
return_value = self.taskBuffer.setNumSlotsForWP(pandaQueueName, numSlots, gshare, resourceType, validPeriod)
|
|
531
|
+
return json.dumps(return_value)
|
|
532
|
+
|
|
533
|
+
# enable jumbo jobs
|
|
534
|
+
def enableJumboJobs(self, jediTaskID, totalJumboJobs, nJumboPerSite):
|
|
535
|
+
return_value = self.taskBuffer.enableJumboJobs(jediTaskID, totalJumboJobs, nJumboPerSite)
|
|
536
|
+
if totalJumboJobs > 0 and return_value[0] == 0:
|
|
537
|
+
self.avalancheTask(jediTaskID, "panda", True)
|
|
538
|
+
return json.dumps(return_value)
|
|
539
|
+
|
|
540
|
+
# get user job metadata
|
|
541
|
+
def getUserJobMetadata(self, jediTaskID):
|
|
542
|
+
return_value = self.taskBuffer.getUserJobMetadata(jediTaskID)
|
|
543
|
+
return json.dumps(return_value)
|
|
544
|
+
|
|
545
|
+
# get jumbo job datasets
|
|
546
|
+
def getJumboJobDatasets(self, n_days, grace_period):
|
|
547
|
+
return_value = self.taskBuffer.getJumboJobDatasets(n_days, grace_period)
|
|
548
|
+
# serialize
|
|
549
|
+
return json.dumps(return_value)
|
|
550
|
+
|
|
551
|
+
# sweep panda queue
|
|
552
|
+
def sweepPQ(self, panda_queue, status_list, ce_list, submission_host_list):
|
|
553
|
+
try:
|
|
554
|
+
panda_queue_des = json.loads(panda_queue)
|
|
555
|
+
status_list_des = json.loads(status_list)
|
|
556
|
+
ce_list_des = json.loads(ce_list)
|
|
557
|
+
submission_host_list_des = json.loads(submission_host_list)
|
|
558
|
+
except Exception:
|
|
559
|
+
_logger.error("Problem deserializing variables")
|
|
560
|
+
|
|
561
|
+
ret = self.taskBuffer.sweepPQ(panda_queue_des, status_list_des, ce_list_des, submission_host_list_des)
|
|
562
|
+
return WrappedPickle.dumps(ret)
|
|
563
|
+
|
|
564
|
+
# send command to a job
|
|
565
|
+
def send_command_to_job(self, panda_id, com):
|
|
566
|
+
ret = self.taskBuffer.send_command_to_job(panda_id, com)
|
|
567
|
+
return ret
|
|
568
|
+
|
|
569
|
+
# set user secret
|
|
570
|
+
def set_user_secret(self, owner, key, value):
|
|
571
|
+
ret = self.taskBuffer.set_user_secret(owner, key, value)
|
|
572
|
+
return ret
|
|
573
|
+
|
|
574
|
+
# get user secrets
|
|
575
|
+
def get_user_secrets(self, owner, keys, get_json):
|
|
576
|
+
ret = self.taskBuffer.get_user_secrets(owner, keys, get_json)
|
|
577
|
+
return ret
|
|
578
|
+
|
|
579
|
+
# get files in datasets
|
|
580
|
+
def get_files_in_datasets(self, task_id, dataset_types):
|
|
581
|
+
ret = self.taskBuffer.get_files_in_datasets(task_id, dataset_types)
|
|
582
|
+
return ret
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
# Singleton
|
|
586
|
+
userIF = UserIF()
|
|
587
|
+
del UserIF
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
# get FQANs
|
|
591
|
+
def _getFQAN(req):
|
|
592
|
+
fqans = []
|
|
593
|
+
for tmp_key in req.subprocess_env:
|
|
594
|
+
tmp_value = req.subprocess_env[tmp_key]
|
|
595
|
+
# Scan VOMS attributes
|
|
596
|
+
# compact style
|
|
597
|
+
if tmp_key.startswith("GRST_CRED_") and tmp_value.startswith("VOMS"):
|
|
598
|
+
fqan = tmp_value.split()[-1]
|
|
599
|
+
fqans.append(fqan)
|
|
600
|
+
|
|
601
|
+
# old style
|
|
602
|
+
elif tmp_key.startswith("GRST_CONN_"):
|
|
603
|
+
tmp_items = tmp_value.split(":")
|
|
604
|
+
if len(tmp_items) == 2 and tmp_items[0] == "fqan":
|
|
605
|
+
fqans.append(tmp_items[-1])
|
|
606
|
+
|
|
607
|
+
return fqans
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
# get DN
|
|
611
|
+
def _getDN(req):
|
|
612
|
+
real_dn = ""
|
|
613
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
614
|
+
# remove redundant CN
|
|
615
|
+
real_dn = CoreUtils.get_bare_dn(req.subprocess_env["SSL_CLIENT_S_DN"], keep_proxy=True)
|
|
616
|
+
return real_dn
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
# check role
|
|
620
|
+
def _has_production_role(req):
|
|
621
|
+
# check DN
|
|
622
|
+
user = _getDN(req)
|
|
623
|
+
for sdn in panda_config.production_dns:
|
|
624
|
+
if sdn in user:
|
|
625
|
+
return True
|
|
626
|
+
# get FQANs
|
|
627
|
+
fqans = _getFQAN(req)
|
|
628
|
+
# loop over all FQANs
|
|
629
|
+
for fqan in fqans:
|
|
630
|
+
# check production role
|
|
631
|
+
for rolePat in [
|
|
632
|
+
"/atlas/usatlas/Role=production",
|
|
633
|
+
"/atlas/Role=production",
|
|
634
|
+
"^/[^/]+/Role=production",
|
|
635
|
+
]:
|
|
636
|
+
if fqan.startswith(rolePat):
|
|
637
|
+
return True
|
|
638
|
+
if re.search(rolePat, fqan):
|
|
639
|
+
return True
|
|
640
|
+
return False
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
# get primary working group with prod role
|
|
644
|
+
def _getWGwithPR(req):
|
|
645
|
+
try:
|
|
646
|
+
fqans = _getFQAN(req)
|
|
647
|
+
for fqan in fqans:
|
|
648
|
+
tmpMatch = re.search("/[^/]+/([^/]+)/Role=production", fqan)
|
|
649
|
+
if tmpMatch is not None:
|
|
650
|
+
# ignore usatlas since it is used as atlas prod role
|
|
651
|
+
tmpWG = tmpMatch.group(1)
|
|
652
|
+
if tmpWG not in ["", "usatlas"]:
|
|
653
|
+
return tmpWG.split("-")[-1].lower()
|
|
654
|
+
except Exception:
|
|
655
|
+
pass
|
|
656
|
+
return None
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
# security check
|
|
660
|
+
def isSecure(req):
|
|
661
|
+
# check security
|
|
662
|
+
if not Protocol.isSecure(req):
|
|
663
|
+
return False
|
|
664
|
+
# disable limited proxy
|
|
665
|
+
if "/CN=limited proxy" in req.subprocess_env["SSL_CLIENT_S_DN"]:
|
|
666
|
+
_logger.warning(f"access via limited proxy : {req.subprocess_env['SSL_CLIENT_S_DN']}")
|
|
667
|
+
return False
|
|
668
|
+
return True
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
"""
|
|
672
|
+
web service interface
|
|
673
|
+
|
|
674
|
+
"""
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
# submit jobs
|
|
678
|
+
def submitJobs(req, jobs, toPending=None):
|
|
679
|
+
# check security
|
|
680
|
+
if not isSecure(req):
|
|
681
|
+
return False
|
|
682
|
+
# get DN
|
|
683
|
+
user = None
|
|
684
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
685
|
+
user = _getDN(req)
|
|
686
|
+
# get FQAN
|
|
687
|
+
fqans = _getFQAN(req)
|
|
688
|
+
# hostname
|
|
689
|
+
host = req.get_remote_host()
|
|
690
|
+
# production Role
|
|
691
|
+
is_production_role = _has_production_role(req)
|
|
692
|
+
# to pending
|
|
693
|
+
toPending = resolve_true(toPending)
|
|
694
|
+
|
|
695
|
+
return userIF.submitJobs(jobs, user, host, fqans, is_production_role, toPending)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
# get job status
|
|
699
|
+
def getJobStatus(req, ids, no_pickle=None):
|
|
700
|
+
return userIF.getJobStatus(ids, req.acceptJson(), no_pickle)
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
# set debug mode
|
|
704
|
+
def setDebugMode(req, pandaID, modeOn):
|
|
705
|
+
tmp_log = LogWrapper(_logger, f"setDebugMode {pandaID} {modeOn}")
|
|
706
|
+
# get DN
|
|
707
|
+
if "SSL_CLIENT_S_DN" not in req.subprocess_env:
|
|
708
|
+
error_str = MESSAGE_SSL
|
|
709
|
+
tmp_log.error(error_str)
|
|
710
|
+
return f"ERROR: {error_str}"
|
|
711
|
+
user = _getDN(req)
|
|
712
|
+
# check role
|
|
713
|
+
is_production_manager = _has_production_role(req)
|
|
714
|
+
fqans = _getFQAN(req)
|
|
715
|
+
# mode
|
|
716
|
+
modeOn = resolve_true(modeOn)
|
|
717
|
+
|
|
718
|
+
# get the primary working group with prod role
|
|
719
|
+
working_group = _getWGwithPR(req)
|
|
720
|
+
tmp_log.debug(f"user={user} mgr={is_production_manager} wg={working_group} fqans={str(fqans)}")
|
|
721
|
+
# exec
|
|
722
|
+
return userIF.setDebugMode(user, pandaID, is_production_manager, modeOn, working_group)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
# insert sandbox file info
|
|
726
|
+
def insertSandboxFileInfo(req, userName, fileName, fileSize, checkSum):
|
|
727
|
+
tmp_log = LogWrapper(_logger, f"insertSandboxFileInfo {userName} {fileName}")
|
|
728
|
+
# get DN
|
|
729
|
+
if "SSL_CLIENT_S_DN" not in req.subprocess_env:
|
|
730
|
+
error_str = MESSAGE_SSL
|
|
731
|
+
tmp_log.error(error_str)
|
|
732
|
+
return f"ERROR: {error_str}"
|
|
733
|
+
|
|
734
|
+
# check role
|
|
735
|
+
is_production_manager = _has_production_role(req)
|
|
736
|
+
if not is_production_manager:
|
|
737
|
+
tmp_log.error(MESSAGE_PROD_ROLE)
|
|
738
|
+
return f"ERROR: {MESSAGE_PROD_ROLE}"
|
|
739
|
+
|
|
740
|
+
# hostname
|
|
741
|
+
if hasattr(panda_config, "sandboxHostname") and panda_config.sandboxHostname:
|
|
742
|
+
hostName = panda_config.sandboxHostname
|
|
743
|
+
else:
|
|
744
|
+
hostName = req.get_remote_host()
|
|
745
|
+
# exec
|
|
746
|
+
return userIF.insertSandboxFileInfo(userName, hostName, fileName, fileSize, checkSum)
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
# check duplicated sandbox file
|
|
750
|
+
def checkSandboxFile(req, fileSize, checkSum):
|
|
751
|
+
# get DN
|
|
752
|
+
if "SSL_CLIENT_S_DN" not in req.subprocess_env:
|
|
753
|
+
return f"ERROR: {MESSAGE_SSL}"
|
|
754
|
+
user = _getDN(req)
|
|
755
|
+
# exec
|
|
756
|
+
return userIF.checkSandboxFile(user, fileSize, checkSum)
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
# get job statistics per site and resource
|
|
760
|
+
def get_job_statistics_per_site_label_resource(req, time_window=None):
|
|
761
|
+
return userIF.get_job_statistics_per_site_label_resource(time_window)
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
# get job statistics per site
|
|
765
|
+
def getJobStatisticsPerSite(req):
|
|
766
|
+
return userIF.getJobStatisticsPerSite()
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
# kill jobs
|
|
770
|
+
def killJobs(req, ids, code=None, useMailAsID=None, killOpts=None):
|
|
771
|
+
# check security
|
|
772
|
+
if not isSecure(req):
|
|
773
|
+
return False
|
|
774
|
+
# get DN
|
|
775
|
+
user = None
|
|
776
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
777
|
+
user = _getDN(req)
|
|
778
|
+
# check role
|
|
779
|
+
is_production_manager = _has_production_role(req)
|
|
780
|
+
# get FQANs
|
|
781
|
+
fqans = _getFQAN(req)
|
|
782
|
+
# use email address as ID
|
|
783
|
+
useMailAsID = resolve_true(useMailAsID)
|
|
784
|
+
|
|
785
|
+
# hostname
|
|
786
|
+
host = req.get_remote_host()
|
|
787
|
+
# options
|
|
788
|
+
if killOpts is None:
|
|
789
|
+
killOpts = []
|
|
790
|
+
else:
|
|
791
|
+
killOpts = killOpts.split(",")
|
|
792
|
+
return userIF.killJobs(ids, user, host, code, is_production_manager, useMailAsID, fqans, killOpts)
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
# reassign jobs
|
|
796
|
+
def reassignJobs(req, ids, forPending=None, firstSubmission=None):
|
|
797
|
+
# check security
|
|
798
|
+
if not isSecure(req):
|
|
799
|
+
return False
|
|
800
|
+
# get DN
|
|
801
|
+
user = None
|
|
802
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
803
|
+
user = _getDN(req)
|
|
804
|
+
# hostname
|
|
805
|
+
host = req.get_remote_host()
|
|
806
|
+
# for pending
|
|
807
|
+
forPending = resolve_true(forPending)
|
|
808
|
+
|
|
809
|
+
# first submission
|
|
810
|
+
firstSubmission = resolve_false(firstSubmission)
|
|
811
|
+
|
|
812
|
+
return userIF.reassignJobs(ids, user, host, forPending, firstSubmission)
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
# get ban users
|
|
816
|
+
def get_ban_users(req):
|
|
817
|
+
return userIF.get_ban_users()
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
# get script for offline running
|
|
821
|
+
def getScriptOfflineRunning(req, pandaID, days=None):
|
|
822
|
+
try:
|
|
823
|
+
if days is not None:
|
|
824
|
+
days = int(days)
|
|
825
|
+
except Exception:
|
|
826
|
+
days = None
|
|
827
|
+
return userIF.getScriptOfflineRunning(pandaID, days)
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
# get active JediTasks in a time range
|
|
831
|
+
def getJediTasksInTimeRange(req, timeRange, dn=None, fullFlag=None, minTaskID=None, task_type="user"):
|
|
832
|
+
# check security
|
|
833
|
+
if not isSecure(req):
|
|
834
|
+
return False
|
|
835
|
+
# get DN
|
|
836
|
+
if "SSL_CLIENT_S_DN" not in req.subprocess_env:
|
|
837
|
+
return False
|
|
838
|
+
if dn is None:
|
|
839
|
+
dn = _getDN(req)
|
|
840
|
+
fullFlag = resolve_true(fullFlag)
|
|
841
|
+
|
|
842
|
+
try:
|
|
843
|
+
minTaskID = int(minTaskID)
|
|
844
|
+
except Exception:
|
|
845
|
+
minTaskID = None
|
|
846
|
+
_logger.debug(f"getJediTasksInTimeRange {dn} {timeRange}")
|
|
847
|
+
# execute
|
|
848
|
+
return userIF.getJediTasksInTimeRange(dn, timeRange, fullFlag, minTaskID, task_type)
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
# get details of JediTask
|
|
852
|
+
def getJediTaskDetails(req, jediTaskID, fullFlag, withTaskInfo):
|
|
853
|
+
# check security
|
|
854
|
+
if not isSecure(req):
|
|
855
|
+
return False
|
|
856
|
+
# get DN
|
|
857
|
+
if "SSL_CLIENT_S_DN" not in req.subprocess_env:
|
|
858
|
+
return False
|
|
859
|
+
# option
|
|
860
|
+
fullFlag = resolve_true(fullFlag)
|
|
861
|
+
withTaskInfo = resolve_true(withTaskInfo)
|
|
862
|
+
|
|
863
|
+
_logger.debug(f"getJediTaskDetails {jediTaskID} {fullFlag} {withTaskInfo}")
|
|
864
|
+
# execute
|
|
865
|
+
return userIF.getJediTaskDetails(jediTaskID, fullFlag, withTaskInfo)
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
# get full job status
|
|
869
|
+
def getFullJobStatus(req, ids):
|
|
870
|
+
# check security
|
|
871
|
+
if not isSecure(req):
|
|
872
|
+
return False
|
|
873
|
+
# get DN
|
|
874
|
+
if "SSL_CLIENT_S_DN" not in req.subprocess_env:
|
|
875
|
+
return False
|
|
876
|
+
dn = _getDN(req)
|
|
877
|
+
return userIF.getFullJobStatus(ids, dn)
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
# insert task params
|
|
881
|
+
def insertTaskParams(req, taskParams=None, properErrorCode=None, parent_tid=None):
|
|
882
|
+
tmp_log = LogWrapper(_logger, f"insertTaskParams-{naive_utcnow().isoformat('/')}")
|
|
883
|
+
tmp_log.debug("start")
|
|
884
|
+
properErrorCode = resolve_true(properErrorCode)
|
|
885
|
+
|
|
886
|
+
# check security
|
|
887
|
+
if not isSecure(req):
|
|
888
|
+
tmp_log.debug(MESSAGE_SSL)
|
|
889
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
890
|
+
# get DN
|
|
891
|
+
user = None
|
|
892
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
893
|
+
user = _getDN(req)
|
|
894
|
+
|
|
895
|
+
# check format
|
|
896
|
+
try:
|
|
897
|
+
json.loads(taskParams)
|
|
898
|
+
except Exception:
|
|
899
|
+
tmp_log.debug(MESSAGE_JSON)
|
|
900
|
+
return WrappedPickle.dumps((False, MESSAGE_JSON))
|
|
901
|
+
# check role
|
|
902
|
+
is_production_role = _has_production_role(req)
|
|
903
|
+
# get FQANs
|
|
904
|
+
fqans = _getFQAN(req)
|
|
905
|
+
|
|
906
|
+
tmp_log.debug(f"user={user} prodRole={is_production_role} FQAN:{str(fqans)} parent_tid={parent_tid}")
|
|
907
|
+
ret = userIF.insertTaskParams(taskParams, user, is_production_role, fqans, properErrorCode, parent_tid)
|
|
908
|
+
try:
|
|
909
|
+
tmp_log.debug(ret[1])
|
|
910
|
+
except Exception:
|
|
911
|
+
pass
|
|
912
|
+
return WrappedPickle.dumps(ret)
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
# kill task
|
|
916
|
+
def killTask(req, jediTaskID=None, properErrorCode=None, broadcast=None):
|
|
917
|
+
properErrorCode = resolve_true(properErrorCode)
|
|
918
|
+
broadcast = resolve_true(broadcast)
|
|
919
|
+
|
|
920
|
+
# check security
|
|
921
|
+
if not isSecure(req):
|
|
922
|
+
error_code = CODE_SSL if properErrorCode else False
|
|
923
|
+
return WrappedPickle.dumps((error_code, MESSAGE_SSL))
|
|
924
|
+
|
|
925
|
+
# get DN
|
|
926
|
+
user = None
|
|
927
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
928
|
+
user = _getDN(req)
|
|
929
|
+
# check role
|
|
930
|
+
is_production_role = _has_production_role(req)
|
|
931
|
+
# check jediTaskID
|
|
932
|
+
try:
|
|
933
|
+
jediTaskID = int(jediTaskID)
|
|
934
|
+
except Exception:
|
|
935
|
+
error_code = CODE_LOGIC if properErrorCode else False
|
|
936
|
+
return WrappedPickle.dumps((error_code, MESSAGE_TASK_ID))
|
|
937
|
+
|
|
938
|
+
ret = userIF.killTask(jediTaskID, user, is_production_role, properErrorCode, broadcast)
|
|
939
|
+
return WrappedPickle.dumps(ret)
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
# retry task
|
|
943
|
+
def retryTask(
|
|
944
|
+
req,
|
|
945
|
+
jediTaskID,
|
|
946
|
+
properErrorCode=None,
|
|
947
|
+
newParams=None,
|
|
948
|
+
noChildRetry=None,
|
|
949
|
+
discardEvents=None,
|
|
950
|
+
disable_staging_mode=None,
|
|
951
|
+
keep_gshare_priority=None,
|
|
952
|
+
ignore_hard_exhausted=None,
|
|
953
|
+
):
|
|
954
|
+
properErrorCode = resolve_true(properErrorCode)
|
|
955
|
+
noChildRetry = resolve_true(noChildRetry)
|
|
956
|
+
discardEvents = resolve_true(discardEvents)
|
|
957
|
+
disable_staging_mode = resolve_true(disable_staging_mode)
|
|
958
|
+
keep_gshare_priority = resolve_true(keep_gshare_priority)
|
|
959
|
+
ignore_hard_exhausted = resolve_true(ignore_hard_exhausted)
|
|
960
|
+
|
|
961
|
+
# check security
|
|
962
|
+
if not isSecure(req):
|
|
963
|
+
error_code = CODE_SSL if properErrorCode else False
|
|
964
|
+
return WrappedPickle.dumps((error_code, MESSAGE_SSL))
|
|
965
|
+
|
|
966
|
+
# get DN
|
|
967
|
+
user = None
|
|
968
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
969
|
+
user = _getDN(req)
|
|
970
|
+
# check role
|
|
971
|
+
is_production_role = _has_production_role(req)
|
|
972
|
+
# check jediTaskID
|
|
973
|
+
try:
|
|
974
|
+
jediTaskID = int(jediTaskID)
|
|
975
|
+
except Exception:
|
|
976
|
+
error_code = CODE_LOGIC if properErrorCode else False
|
|
977
|
+
return WrappedPickle.dumps((error_code, MESSAGE_TASK_ID))
|
|
978
|
+
|
|
979
|
+
ret = userIF.retryTask(
|
|
980
|
+
jediTaskID,
|
|
981
|
+
user,
|
|
982
|
+
is_production_role,
|
|
983
|
+
properErrorCode,
|
|
984
|
+
newParams,
|
|
985
|
+
noChildRetry,
|
|
986
|
+
discardEvents,
|
|
987
|
+
disable_staging_mode,
|
|
988
|
+
keep_gshare_priority,
|
|
989
|
+
ignore_hard_exhausted,
|
|
990
|
+
)
|
|
991
|
+
return WrappedPickle.dumps(ret)
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
# reassign task to site/cloud
|
|
995
|
+
def reassignTask(req, jediTaskID, site=None, cloud=None, nucleus=None, soft=None, mode=None):
|
|
996
|
+
# check security
|
|
997
|
+
if not isSecure(req):
|
|
998
|
+
return WrappedPickle.dumps((CODE_SSL, MESSAGE_SSL))
|
|
999
|
+
# get DN
|
|
1000
|
+
user = None
|
|
1001
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1002
|
+
user = _getDN(req)
|
|
1003
|
+
# check role
|
|
1004
|
+
is_production_role = _has_production_role(req)
|
|
1005
|
+
# check jediTaskID
|
|
1006
|
+
try:
|
|
1007
|
+
jediTaskID = int(jediTaskID)
|
|
1008
|
+
except Exception:
|
|
1009
|
+
return WrappedPickle.dumps((CODE_LOGIC, MESSAGE_TASK_ID))
|
|
1010
|
+
# site or cloud
|
|
1011
|
+
if site is not None:
|
|
1012
|
+
# set 'y' to go back to oldStatus immediately
|
|
1013
|
+
comComment = f"site:{site}:y"
|
|
1014
|
+
elif nucleus is not None:
|
|
1015
|
+
comComment = f"nucleus:{nucleus}:n"
|
|
1016
|
+
else:
|
|
1017
|
+
comComment = f"cloud:{cloud}:n"
|
|
1018
|
+
if mode == "nokill":
|
|
1019
|
+
comComment += ":nokill reassign"
|
|
1020
|
+
elif mode == "soft" or soft == "True":
|
|
1021
|
+
comComment += ":soft reassign"
|
|
1022
|
+
ret = userIF.reassignTask(jediTaskID, user, is_production_role, comComment)
|
|
1023
|
+
return WrappedPickle.dumps(ret)
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
# finish task
|
|
1027
|
+
def finishTask(req, jediTaskID=None, properErrorCode=None, soft=None, broadcast=None):
|
|
1028
|
+
properErrorCode = resolve_true(properErrorCode)
|
|
1029
|
+
broadcast = resolve_true(broadcast)
|
|
1030
|
+
qualifier = None
|
|
1031
|
+
if soft == "True":
|
|
1032
|
+
qualifier = "soft"
|
|
1033
|
+
|
|
1034
|
+
# check security
|
|
1035
|
+
if not isSecure(req):
|
|
1036
|
+
error_code = CODE_SSL if properErrorCode else False
|
|
1037
|
+
return WrappedPickle.dumps((error_code, MESSAGE_SSL))
|
|
1038
|
+
|
|
1039
|
+
# get DN
|
|
1040
|
+
user = None
|
|
1041
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1042
|
+
user = _getDN(req)
|
|
1043
|
+
# check role
|
|
1044
|
+
is_production_role = _has_production_role(req)
|
|
1045
|
+
# check jediTaskID
|
|
1046
|
+
try:
|
|
1047
|
+
jediTaskID = int(jediTaskID)
|
|
1048
|
+
except Exception:
|
|
1049
|
+
error_code = CODE_LOGIC if properErrorCode else False
|
|
1050
|
+
return WrappedPickle.dumps((error_code, MESSAGE_TASK_ID))
|
|
1051
|
+
|
|
1052
|
+
ret = userIF.finishTask(jediTaskID, user, is_production_role, properErrorCode, qualifier, broadcast)
|
|
1053
|
+
return WrappedPickle.dumps(ret)
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
# reload input
|
|
1057
|
+
def reloadInput(req, jediTaskID, properErrorCode=None, ignore_hard_exhausted=None):
|
|
1058
|
+
properErrorCode = resolve_true(properErrorCode)
|
|
1059
|
+
# check security
|
|
1060
|
+
if not isSecure(req):
|
|
1061
|
+
error_code = CODE_SSL if properErrorCode else False
|
|
1062
|
+
return WrappedPickle.dumps((error_code, MESSAGE_SSL))
|
|
1063
|
+
|
|
1064
|
+
# get DN
|
|
1065
|
+
user = None
|
|
1066
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1067
|
+
user = _getDN(req)
|
|
1068
|
+
# check role
|
|
1069
|
+
is_production_role = _has_production_role(req)
|
|
1070
|
+
# check jediTaskID
|
|
1071
|
+
try:
|
|
1072
|
+
jediTaskID = int(jediTaskID)
|
|
1073
|
+
except Exception:
|
|
1074
|
+
error_code = CODE_LOGIC if properErrorCode else False
|
|
1075
|
+
return WrappedPickle.dumps((error_code, MESSAGE_TASK_ID))
|
|
1076
|
+
ignore_hard_exhausted = resolve_true(ignore_hard_exhausted)
|
|
1077
|
+
ret = userIF.reloadInput(jediTaskID, user, is_production_role, ignore_hard_exhausted)
|
|
1078
|
+
return WrappedPickle.dumps(ret)
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
# change task priority
|
|
1082
|
+
def changeTaskPriority(req, jediTaskID=None, newPriority=None):
|
|
1083
|
+
# check security
|
|
1084
|
+
if not isSecure(req):
|
|
1085
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1086
|
+
# check role
|
|
1087
|
+
is_production_role = _has_production_role(req)
|
|
1088
|
+
# only prod managers can use this method
|
|
1089
|
+
if not is_production_role:
|
|
1090
|
+
return "Failed : production or pilot role required"
|
|
1091
|
+
# check jediTaskID
|
|
1092
|
+
try:
|
|
1093
|
+
jediTaskID = int(jediTaskID)
|
|
1094
|
+
except Exception:
|
|
1095
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1096
|
+
# check priority
|
|
1097
|
+
try:
|
|
1098
|
+
newPriority = int(newPriority)
|
|
1099
|
+
except Exception:
|
|
1100
|
+
return WrappedPickle.dumps((False, "newPriority must be an integer"))
|
|
1101
|
+
ret = userIF.changeTaskPriority(jediTaskID, newPriority)
|
|
1102
|
+
return WrappedPickle.dumps(ret)
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
# increase attempt number for unprocessed files
|
|
1106
|
+
def increaseAttemptNrPanda(req, jediTaskID, increasedNr):
|
|
1107
|
+
# check security
|
|
1108
|
+
if not isSecure(req):
|
|
1109
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1110
|
+
|
|
1111
|
+
# check role
|
|
1112
|
+
is_production_role = _has_production_role(req)
|
|
1113
|
+
# only prod managers can use this method
|
|
1114
|
+
if not is_production_role:
|
|
1115
|
+
return WrappedPickle.dumps((3, MESSAGE_PROD_ROLE))
|
|
1116
|
+
|
|
1117
|
+
# check jediTaskID
|
|
1118
|
+
try:
|
|
1119
|
+
jediTaskID = int(jediTaskID)
|
|
1120
|
+
except Exception:
|
|
1121
|
+
return WrappedPickle.dumps((4, MESSAGE_TASK_ID))
|
|
1122
|
+
|
|
1123
|
+
# check value for increase
|
|
1124
|
+
try:
|
|
1125
|
+
increasedNr = int(increasedNr)
|
|
1126
|
+
except Exception:
|
|
1127
|
+
increasedNr = -1
|
|
1128
|
+
if increasedNr < 0:
|
|
1129
|
+
return WrappedPickle.dumps((4, "increase must be a positive integer"))
|
|
1130
|
+
|
|
1131
|
+
return WrappedPickle.dumps(userIF.increaseAttemptNrPanda(jediTaskID, increasedNr))
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
# change task attribute
|
|
1135
|
+
def changeTaskAttributePanda(req, jediTaskID, attrName, attrValue):
|
|
1136
|
+
# check security
|
|
1137
|
+
if not isSecure(req):
|
|
1138
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1139
|
+
|
|
1140
|
+
# check role
|
|
1141
|
+
is_production_role = _has_production_role(req)
|
|
1142
|
+
# only prod managers can use this method
|
|
1143
|
+
if not is_production_role:
|
|
1144
|
+
return WrappedPickle.dumps((False, MESSAGE_PROD_ROLE))
|
|
1145
|
+
# check jediTaskID
|
|
1146
|
+
try:
|
|
1147
|
+
jediTaskID = int(jediTaskID)
|
|
1148
|
+
except Exception:
|
|
1149
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1150
|
+
# check attribute
|
|
1151
|
+
if attrName not in ["ramCount", "wallTime", "cpuTime", "coreCount"]:
|
|
1152
|
+
return WrappedPickle.dumps((2, f"disallowed to update {attrName}"))
|
|
1153
|
+
ret = userIF.changeTaskAttributePanda(jediTaskID, attrName, attrValue)
|
|
1154
|
+
return WrappedPickle.dumps((ret, None))
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
# change split rule for task
|
|
1158
|
+
def changeTaskSplitRulePanda(req, jediTaskID, attrName, attrValue):
|
|
1159
|
+
# check security
|
|
1160
|
+
if not isSecure(req):
|
|
1161
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1162
|
+
|
|
1163
|
+
# check role
|
|
1164
|
+
is_production_role = _has_production_role(req)
|
|
1165
|
+
# only prod managers can use this method
|
|
1166
|
+
if not is_production_role:
|
|
1167
|
+
return WrappedPickle.dumps((False, MESSAGE_PROD_ROLE))
|
|
1168
|
+
# check jediTaskID
|
|
1169
|
+
try:
|
|
1170
|
+
jediTaskID = int(jediTaskID)
|
|
1171
|
+
except Exception:
|
|
1172
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1173
|
+
# check attribute
|
|
1174
|
+
if attrName not in [
|
|
1175
|
+
"AI",
|
|
1176
|
+
"TW",
|
|
1177
|
+
"EC",
|
|
1178
|
+
"ES",
|
|
1179
|
+
"MF",
|
|
1180
|
+
"NG",
|
|
1181
|
+
"NI",
|
|
1182
|
+
"NF",
|
|
1183
|
+
"NJ",
|
|
1184
|
+
"AV",
|
|
1185
|
+
"IL",
|
|
1186
|
+
"LI",
|
|
1187
|
+
"LC",
|
|
1188
|
+
"CC",
|
|
1189
|
+
"OT",
|
|
1190
|
+
"UZ",
|
|
1191
|
+
]:
|
|
1192
|
+
return WrappedPickle.dumps((2, f"disallowed to update {attrName}"))
|
|
1193
|
+
ret = userIF.changeTaskSplitRulePanda(jediTaskID, attrName, attrValue)
|
|
1194
|
+
return WrappedPickle.dumps((ret, None))
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
# pause task
|
|
1198
|
+
def pauseTask(req, jediTaskID):
|
|
1199
|
+
# check security
|
|
1200
|
+
if not isSecure(req):
|
|
1201
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1202
|
+
# get DN
|
|
1203
|
+
user = None
|
|
1204
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1205
|
+
user = _getDN(req)
|
|
1206
|
+
# check role
|
|
1207
|
+
is_production_role = _has_production_role(req)
|
|
1208
|
+
# only prod managers can use this method
|
|
1209
|
+
if not is_production_role:
|
|
1210
|
+
return WrappedPickle.dumps((False, MESSAGE_PROD_ROLE))
|
|
1211
|
+
# check jediTaskID
|
|
1212
|
+
try:
|
|
1213
|
+
jediTaskID = int(jediTaskID)
|
|
1214
|
+
except Exception:
|
|
1215
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1216
|
+
ret = userIF.pauseTask(jediTaskID, user, is_production_role)
|
|
1217
|
+
return WrappedPickle.dumps(ret)
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
# resume task
|
|
1221
|
+
def resumeTask(req, jediTaskID):
|
|
1222
|
+
# check security
|
|
1223
|
+
if not isSecure(req):
|
|
1224
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1225
|
+
# get DN
|
|
1226
|
+
user = None
|
|
1227
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1228
|
+
user = _getDN(req)
|
|
1229
|
+
# check role
|
|
1230
|
+
is_production_role = _has_production_role(req)
|
|
1231
|
+
# only prod managers can use this method
|
|
1232
|
+
if not is_production_role:
|
|
1233
|
+
return WrappedPickle.dumps((False, "production role required"))
|
|
1234
|
+
# check jediTaskID
|
|
1235
|
+
try:
|
|
1236
|
+
jediTaskID = int(jediTaskID)
|
|
1237
|
+
except Exception:
|
|
1238
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1239
|
+
ret = userIF.resumeTask(jediTaskID, user, is_production_role)
|
|
1240
|
+
return WrappedPickle.dumps(ret)
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
# force avalanche for task
|
|
1244
|
+
def avalancheTask(req, jediTaskID):
|
|
1245
|
+
# check security
|
|
1246
|
+
if not isSecure(req):
|
|
1247
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1248
|
+
# get DN
|
|
1249
|
+
user = None
|
|
1250
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1251
|
+
user = _getDN(req)
|
|
1252
|
+
# check role
|
|
1253
|
+
is_production_role = _has_production_role(req)
|
|
1254
|
+
# only prod managers can use this method
|
|
1255
|
+
if not is_production_role:
|
|
1256
|
+
return WrappedPickle.dumps((False, "production role required"))
|
|
1257
|
+
# check jediTaskID
|
|
1258
|
+
try:
|
|
1259
|
+
jediTaskID = int(jediTaskID)
|
|
1260
|
+
except Exception:
|
|
1261
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1262
|
+
ret = userIF.avalancheTask(jediTaskID, user, is_production_role)
|
|
1263
|
+
return WrappedPickle.dumps(ret)
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
# release task
|
|
1267
|
+
def release_task(req, jedi_task_id):
|
|
1268
|
+
# check security
|
|
1269
|
+
if not isSecure(req):
|
|
1270
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1271
|
+
# get DN
|
|
1272
|
+
user = None
|
|
1273
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1274
|
+
user = _getDN(req)
|
|
1275
|
+
# check role
|
|
1276
|
+
prod_role = _has_production_role(req)
|
|
1277
|
+
# only prod managers can use this method
|
|
1278
|
+
if not prod_role:
|
|
1279
|
+
return json.dumps((False, "production role required"))
|
|
1280
|
+
# check jediTaskID
|
|
1281
|
+
try:
|
|
1282
|
+
jedi_task_id = int(jedi_task_id)
|
|
1283
|
+
except Exception:
|
|
1284
|
+
return json.dumps((False, MESSAGE_TASK_ID))
|
|
1285
|
+
ret = userIF.send_command_to_task(jedi_task_id, user, prod_role, "release")
|
|
1286
|
+
return json.dumps(ret)
|
|
1287
|
+
|
|
1288
|
+
|
|
1289
|
+
# kill unfinished jobs
|
|
1290
|
+
def killUnfinishedJobs(req, jediTaskID, code=None, useMailAsID=None):
|
|
1291
|
+
# check security
|
|
1292
|
+
if not isSecure(req):
|
|
1293
|
+
return False
|
|
1294
|
+
# get DN
|
|
1295
|
+
user = None
|
|
1296
|
+
if "SSL_CLIENT_S_DN" in req.subprocess_env:
|
|
1297
|
+
user = _getDN(req)
|
|
1298
|
+
# check role
|
|
1299
|
+
is_production_manager = False
|
|
1300
|
+
# get FQANs
|
|
1301
|
+
fqans = _getFQAN(req)
|
|
1302
|
+
# loop over all FQANs
|
|
1303
|
+
for fqan in fqans:
|
|
1304
|
+
# check production role
|
|
1305
|
+
for rolePat in ["/atlas/usatlas/Role=production", "/atlas/Role=production"]:
|
|
1306
|
+
if fqan.startswith(rolePat):
|
|
1307
|
+
is_production_manager = True
|
|
1308
|
+
break
|
|
1309
|
+
# escape
|
|
1310
|
+
if is_production_manager:
|
|
1311
|
+
break
|
|
1312
|
+
# use email address as ID
|
|
1313
|
+
useMailAsID = resolve_true(useMailAsID)
|
|
1314
|
+
# hostname
|
|
1315
|
+
host = req.get_remote_host()
|
|
1316
|
+
# get PandaIDs
|
|
1317
|
+
ids = userIF.getPandaIDsWithTaskID(jediTaskID)
|
|
1318
|
+
# kill
|
|
1319
|
+
return userIF.killJobs(ids, user, host, code, is_production_manager, useMailAsID, fqans)
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
# change modificationTime for task
|
|
1323
|
+
def changeTaskModTimePanda(req, jediTaskID, diffValue):
|
|
1324
|
+
# check security
|
|
1325
|
+
if not isSecure(req):
|
|
1326
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1327
|
+
|
|
1328
|
+
# check role
|
|
1329
|
+
is_production_role = _has_production_role(req)
|
|
1330
|
+
# only prod managers can use this method
|
|
1331
|
+
if not is_production_role:
|
|
1332
|
+
return WrappedPickle.dumps((False, MESSAGE_PROD_ROLE))
|
|
1333
|
+
# check jediTaskID
|
|
1334
|
+
try:
|
|
1335
|
+
jediTaskID = int(jediTaskID)
|
|
1336
|
+
except Exception:
|
|
1337
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1338
|
+
try:
|
|
1339
|
+
diffValue = int(diffValue)
|
|
1340
|
+
attrValue = datetime.datetime.now() + datetime.timedelta(hours=diffValue)
|
|
1341
|
+
except Exception:
|
|
1342
|
+
return WrappedPickle.dumps((False, f"failed to convert {diffValue} to time diff"))
|
|
1343
|
+
ret = userIF.changeTaskAttributePanda(jediTaskID, "modificationTime", attrValue)
|
|
1344
|
+
return WrappedPickle.dumps((ret, None))
|
|
1345
|
+
|
|
1346
|
+
|
|
1347
|
+
# get PandaIDs with TaskID
|
|
1348
|
+
def getPandaIDsWithTaskID(req, jediTaskID):
|
|
1349
|
+
try:
|
|
1350
|
+
jediTaskID = int(jediTaskID)
|
|
1351
|
+
except Exception:
|
|
1352
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1353
|
+
idsStr = userIF.getPandaIDsWithTaskID(jediTaskID)
|
|
1354
|
+
# deserialize
|
|
1355
|
+
ids = WrappedPickle.loads(idsStr)
|
|
1356
|
+
|
|
1357
|
+
return WrappedPickle.dumps(ids)
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
# reactivate Task
|
|
1361
|
+
def reactivateTask(req, jediTaskID, keep_attempt_nr=None, trigger_job_generation=None):
|
|
1362
|
+
# check security
|
|
1363
|
+
if not isSecure(req):
|
|
1364
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1365
|
+
# check role
|
|
1366
|
+
is_production_manager = _has_production_role(req)
|
|
1367
|
+
if not is_production_manager:
|
|
1368
|
+
msg = "production role is required"
|
|
1369
|
+
_logger.error(f"reactivateTask: {msg}")
|
|
1370
|
+
return WrappedPickle.dumps((False, msg))
|
|
1371
|
+
try:
|
|
1372
|
+
jediTaskID = int(jediTaskID)
|
|
1373
|
+
except Exception:
|
|
1374
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1375
|
+
keep_attempt_nr = resolve_true(keep_attempt_nr)
|
|
1376
|
+
trigger_job_generation = resolve_true(trigger_job_generation)
|
|
1377
|
+
|
|
1378
|
+
ret = userIF.reactivateTask(jediTaskID, keep_attempt_nr, trigger_job_generation)
|
|
1379
|
+
|
|
1380
|
+
return WrappedPickle.dumps(ret)
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
# get task status
|
|
1384
|
+
def getTaskStatus(req, jediTaskID):
|
|
1385
|
+
try:
|
|
1386
|
+
jediTaskID = int(jediTaskID)
|
|
1387
|
+
except Exception:
|
|
1388
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1389
|
+
ret = userIF.getTaskStatus(jediTaskID)
|
|
1390
|
+
return WrappedPickle.dumps(ret)
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
# reassign share
|
|
1394
|
+
def reassignShare(req, jedi_task_ids_pickle, share, reassign_running):
|
|
1395
|
+
# check security
|
|
1396
|
+
if not isSecure(req):
|
|
1397
|
+
return WrappedPickle.dumps((False, MESSAGE_SSL))
|
|
1398
|
+
|
|
1399
|
+
# check role
|
|
1400
|
+
prod_role = _has_production_role(req)
|
|
1401
|
+
if not prod_role:
|
|
1402
|
+
return WrappedPickle.dumps((False, MESSAGE_PROD_ROLE))
|
|
1403
|
+
|
|
1404
|
+
jedi_task_ids = WrappedPickle.loads(jedi_task_ids_pickle)
|
|
1405
|
+
_logger.debug(f"reassignShare: jedi_task_ids: {jedi_task_ids}, share: {share}, reassign_running: {reassign_running} (type {type(reassign_running)})")
|
|
1406
|
+
|
|
1407
|
+
if isinstance(reassign_running, str) and "true" in reassign_running.lower():
|
|
1408
|
+
reassign_running = True
|
|
1409
|
+
else:
|
|
1410
|
+
reassign_running = False
|
|
1411
|
+
|
|
1412
|
+
if not ((isinstance(jedi_task_ids, list) or (isinstance(jedi_task_ids, tuple)) and isinstance(share, str))):
|
|
1413
|
+
return WrappedPickle.dumps((False, "jedi_task_ids must be tuple/list and share must be string"))
|
|
1414
|
+
|
|
1415
|
+
ret = userIF.reassignShare(jedi_task_ids, share, reassign_running)
|
|
1416
|
+
return WrappedPickle.dumps(ret)
|
|
1417
|
+
|
|
1418
|
+
|
|
1419
|
+
# get taskParamsMap with TaskID
|
|
1420
|
+
def getTaskParamsMap(req, jediTaskID):
|
|
1421
|
+
try:
|
|
1422
|
+
jediTaskID = int(jediTaskID)
|
|
1423
|
+
except Exception:
|
|
1424
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1425
|
+
ret = userIF.getTaskParamsMap(jediTaskID)
|
|
1426
|
+
return WrappedPickle.dumps(ret)
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
# update workers
|
|
1430
|
+
def updateWorkers(req, harvesterID, workers):
|
|
1431
|
+
# check security
|
|
1432
|
+
if not isSecure(req):
|
|
1433
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1434
|
+
# get DN
|
|
1435
|
+
user = _getDN(req)
|
|
1436
|
+
# hostname
|
|
1437
|
+
host = req.get_remote_host()
|
|
1438
|
+
return_value = None
|
|
1439
|
+
tStart = naive_utcnow()
|
|
1440
|
+
# convert
|
|
1441
|
+
try:
|
|
1442
|
+
data = json.loads(workers)
|
|
1443
|
+
except Exception:
|
|
1444
|
+
return_value = json.dumps((False, MESSAGE_JSON))
|
|
1445
|
+
# update
|
|
1446
|
+
if return_value is None:
|
|
1447
|
+
return_value = userIF.updateWorkers(user, host, harvesterID, data)
|
|
1448
|
+
tDelta = naive_utcnow() - tStart
|
|
1449
|
+
_logger.debug(f"updateWorkers {harvesterID} took {tDelta.seconds}.{tDelta.microseconds // 1000:03d} sec")
|
|
1450
|
+
|
|
1451
|
+
return return_value
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
# update workers
|
|
1455
|
+
def updateServiceMetrics(req, harvesterID, metrics):
|
|
1456
|
+
# check security
|
|
1457
|
+
if not isSecure(req):
|
|
1458
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1459
|
+
|
|
1460
|
+
user = _getDN(req)
|
|
1461
|
+
|
|
1462
|
+
host = req.get_remote_host()
|
|
1463
|
+
return_value = None
|
|
1464
|
+
tStart = naive_utcnow()
|
|
1465
|
+
|
|
1466
|
+
# convert
|
|
1467
|
+
try:
|
|
1468
|
+
data = json.loads(metrics)
|
|
1469
|
+
except Exception:
|
|
1470
|
+
return_value = json.dumps((False, MESSAGE_JSON))
|
|
1471
|
+
|
|
1472
|
+
# update
|
|
1473
|
+
if return_value is None:
|
|
1474
|
+
return_value = userIF.updateServiceMetrics(user, host, harvesterID, data)
|
|
1475
|
+
|
|
1476
|
+
tDelta = naive_utcnow() - tStart
|
|
1477
|
+
_logger.debug(f"updateServiceMetrics {harvesterID} took {tDelta.seconds}.{tDelta.microseconds // 1000:03d} sec")
|
|
1478
|
+
|
|
1479
|
+
return return_value
|
|
1480
|
+
|
|
1481
|
+
|
|
1482
|
+
# add harvester dialog messages
|
|
1483
|
+
def addHarvesterDialogs(req, harvesterID, dialogs):
|
|
1484
|
+
# check security
|
|
1485
|
+
if not isSecure(req):
|
|
1486
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1487
|
+
# get DN
|
|
1488
|
+
user = _getDN(req)
|
|
1489
|
+
# convert
|
|
1490
|
+
try:
|
|
1491
|
+
data = json.loads(dialogs)
|
|
1492
|
+
except Exception:
|
|
1493
|
+
return json.dumps((False, MESSAGE_JSON))
|
|
1494
|
+
# update
|
|
1495
|
+
return userIF.addHarvesterDialogs(user, harvesterID, data)
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
# heartbeat for harvester
|
|
1499
|
+
def harvesterIsAlive(req, harvesterID, data=None):
|
|
1500
|
+
# check security
|
|
1501
|
+
if not isSecure(req):
|
|
1502
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1503
|
+
# get DN
|
|
1504
|
+
user = _getDN(req)
|
|
1505
|
+
# hostname
|
|
1506
|
+
host = req.get_remote_host()
|
|
1507
|
+
# convert
|
|
1508
|
+
try:
|
|
1509
|
+
if data is not None:
|
|
1510
|
+
data = json.loads(data)
|
|
1511
|
+
else:
|
|
1512
|
+
data = dict()
|
|
1513
|
+
except Exception:
|
|
1514
|
+
return json.dumps((False, MESSAGE_JSON))
|
|
1515
|
+
# update
|
|
1516
|
+
return userIF.harvesterIsAlive(user, host, harvesterID, data)
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
# get stats of workers
|
|
1520
|
+
def getWorkerStats(req):
|
|
1521
|
+
# get
|
|
1522
|
+
ret = userIF.getWorkerStats()
|
|
1523
|
+
return json.dumps(ret)
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
# report stat of workers
|
|
1527
|
+
def reportWorkerStats_jobtype(req, harvesterID, siteName, paramsList):
|
|
1528
|
+
# check security
|
|
1529
|
+
if not isSecure(req):
|
|
1530
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1531
|
+
# update
|
|
1532
|
+
ret = userIF.reportWorkerStats_jobtype(harvesterID, siteName, paramsList)
|
|
1533
|
+
return json.dumps(ret)
|
|
1534
|
+
|
|
1535
|
+
|
|
1536
|
+
# set num slots for workload provisioning
|
|
1537
|
+
def setNumSlotsForWP(req, pandaQueueName, numSlots, gshare=None, resourceType=None, validPeriod=None):
|
|
1538
|
+
# check security
|
|
1539
|
+
if not isSecure(req):
|
|
1540
|
+
return json.dumps((CODE_SSL, MESSAGE_SSL))
|
|
1541
|
+
# check role
|
|
1542
|
+
if not _has_production_role(req):
|
|
1543
|
+
return json.dumps((CODE_LOGIC, "production role is required in the certificate"))
|
|
1544
|
+
# convert
|
|
1545
|
+
try:
|
|
1546
|
+
numSlots = int(numSlots)
|
|
1547
|
+
except Exception:
|
|
1548
|
+
return json.dumps((CODE_OTHER_PARAMS, "numSlots must be int"))
|
|
1549
|
+
# execute
|
|
1550
|
+
return userIF.setNumSlotsForWP(pandaQueueName, numSlots, gshare, resourceType, validPeriod)
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
# enable jumbo jobs
|
|
1554
|
+
def enableJumboJobs(req, jediTaskID, nJumboJobs, nJumboPerSite=None):
|
|
1555
|
+
# check security
|
|
1556
|
+
if not isSecure(req):
|
|
1557
|
+
return json.dumps((CODE_SSL, MESSAGE_SSL))
|
|
1558
|
+
# check role
|
|
1559
|
+
if not _has_production_role(req):
|
|
1560
|
+
return json.dumps((CODE_LOGIC, "production role is required in the certificate"))
|
|
1561
|
+
# convert
|
|
1562
|
+
try:
|
|
1563
|
+
nJumboJobs = int(nJumboJobs)
|
|
1564
|
+
except Exception:
|
|
1565
|
+
return json.dumps((CODE_OTHER_PARAMS, "nJumboJobs must be int"))
|
|
1566
|
+
try:
|
|
1567
|
+
nJumboPerSite = int(nJumboPerSite)
|
|
1568
|
+
except Exception:
|
|
1569
|
+
nJumboPerSite = nJumboJobs
|
|
1570
|
+
# execute
|
|
1571
|
+
return userIF.enableJumboJobs(jediTaskID, nJumboJobs, nJumboPerSite)
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
# get user job metadata
|
|
1575
|
+
def getUserJobMetadata(req, jediTaskID):
|
|
1576
|
+
try:
|
|
1577
|
+
jediTaskID = int(jediTaskID)
|
|
1578
|
+
except Exception:
|
|
1579
|
+
return WrappedPickle.dumps((False, MESSAGE_TASK_ID))
|
|
1580
|
+
return userIF.getUserJobMetadata(jediTaskID)
|
|
1581
|
+
|
|
1582
|
+
|
|
1583
|
+
# get jumbo job datasets
|
|
1584
|
+
def getJumboJobDatasets(req, n_days, grace_period=0):
|
|
1585
|
+
try:
|
|
1586
|
+
n_days = int(n_days)
|
|
1587
|
+
except Exception:
|
|
1588
|
+
return WrappedPickle.dumps((False, "wrong n_days"))
|
|
1589
|
+
try:
|
|
1590
|
+
grace_period = int(grace_period)
|
|
1591
|
+
except Exception:
|
|
1592
|
+
return WrappedPickle.dumps((False, "wrong grace_period"))
|
|
1593
|
+
return userIF.getJumboJobDatasets(n_days, grace_period)
|
|
1594
|
+
|
|
1595
|
+
|
|
1596
|
+
# send Harvester the command to clean up the workers for a panda queue
|
|
1597
|
+
def sweepPQ(req, panda_queue, status_list, ce_list, submission_host_list):
|
|
1598
|
+
# check security
|
|
1599
|
+
if not isSecure(req):
|
|
1600
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1601
|
+
# check role
|
|
1602
|
+
prod_role = _has_production_role(req)
|
|
1603
|
+
if not prod_role:
|
|
1604
|
+
return json.dumps((False, MESSAGE_PROD_ROLE))
|
|
1605
|
+
|
|
1606
|
+
return json.dumps((True, userIF.sweepPQ(panda_queue, status_list, ce_list, submission_host_list)))
|
|
1607
|
+
|
|
1608
|
+
|
|
1609
|
+
# json decoder for idds constants
|
|
1610
|
+
def decode_idds_enum(d):
|
|
1611
|
+
if "__idds_const__" in d:
|
|
1612
|
+
items = d["__idds_const__"].split(".")
|
|
1613
|
+
obj = idds.common.constants
|
|
1614
|
+
for item in items:
|
|
1615
|
+
obj = getattr(obj, item)
|
|
1616
|
+
return obj
|
|
1617
|
+
else:
|
|
1618
|
+
return d
|
|
1619
|
+
|
|
1620
|
+
|
|
1621
|
+
# relay iDDS command
|
|
1622
|
+
def relay_idds_command(req, command_name, args=None, kwargs=None, manager=None, json_outputs=None):
|
|
1623
|
+
tmp_log = LogWrapper(
|
|
1624
|
+
_logger,
|
|
1625
|
+
f"relay_idds_command-{naive_utcnow().isoformat('/')}",
|
|
1626
|
+
)
|
|
1627
|
+
# check security
|
|
1628
|
+
if not isSecure(req):
|
|
1629
|
+
tmp_log.error(MESSAGE_SSL)
|
|
1630
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1631
|
+
try:
|
|
1632
|
+
manager = resolve_bool(manager)
|
|
1633
|
+
if not manager:
|
|
1634
|
+
manager = False
|
|
1635
|
+
if "+" in command_name:
|
|
1636
|
+
command_name, idds_host = command_name.split("+")
|
|
1637
|
+
else:
|
|
1638
|
+
idds_host = idds.common.utils.get_rest_host()
|
|
1639
|
+
if manager:
|
|
1640
|
+
c = iDDS_ClientManager(idds_host)
|
|
1641
|
+
else:
|
|
1642
|
+
c = iDDS_Client(idds_host)
|
|
1643
|
+
if not hasattr(c, command_name):
|
|
1644
|
+
tmp_str = f"{command_name} is not a command of iDDS {c.__class__.__name__}"
|
|
1645
|
+
tmp_log.error(tmp_str)
|
|
1646
|
+
return json.dumps((False, tmp_str))
|
|
1647
|
+
if args:
|
|
1648
|
+
try:
|
|
1649
|
+
args = idds.common.utils.json_loads(args)
|
|
1650
|
+
except Exception as e:
|
|
1651
|
+
tmp_log.warning(f"failed to load args json with {str(e)}")
|
|
1652
|
+
args = json.loads(args, object_hook=decode_idds_enum)
|
|
1653
|
+
else:
|
|
1654
|
+
args = []
|
|
1655
|
+
if kwargs:
|
|
1656
|
+
try:
|
|
1657
|
+
kwargs = idds.common.utils.json_loads(kwargs)
|
|
1658
|
+
except Exception as e:
|
|
1659
|
+
tmp_log.warning(f"failed to load kwargs json with {str(e)}")
|
|
1660
|
+
kwargs = json.loads(kwargs, object_hook=decode_idds_enum)
|
|
1661
|
+
else:
|
|
1662
|
+
kwargs = {}
|
|
1663
|
+
# json outputs
|
|
1664
|
+
if json_outputs and manager:
|
|
1665
|
+
c.setup_json_outputs()
|
|
1666
|
+
# set original username
|
|
1667
|
+
dn = req.subprocess_env.get("SSL_CLIENT_S_DN")
|
|
1668
|
+
if dn:
|
|
1669
|
+
c.set_original_user(user_name=clean_user_id(dn))
|
|
1670
|
+
tmp_log.debug(f"execute: class={c.__class__.__name__} com={command_name} host={idds_host} args={str(args)[:200]} kwargs={str(kwargs)[:200]}")
|
|
1671
|
+
ret = getattr(c, command_name)(*args, **kwargs)
|
|
1672
|
+
tmp_log.debug(f"ret: {str(ret)[:200]}")
|
|
1673
|
+
try:
|
|
1674
|
+
return json.dumps((True, ret))
|
|
1675
|
+
except Exception:
|
|
1676
|
+
return idds.common.utils.json_dumps((True, ret))
|
|
1677
|
+
except Exception as e:
|
|
1678
|
+
tmp_str = f"failed to execute command with {str(e)}"
|
|
1679
|
+
tmp_log.error(f"{tmp_str} {traceback.format_exc()}")
|
|
1680
|
+
return json.dumps((False, tmp_str))
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
# relay iDDS workflow command with ownership check
|
|
1684
|
+
def execute_idds_workflow_command(req, command_name, kwargs=None, json_outputs=None):
|
|
1685
|
+
try:
|
|
1686
|
+
tmp_log = LogWrapper(
|
|
1687
|
+
_logger,
|
|
1688
|
+
f"execute_idds_workflow_command-{naive_utcnow().isoformat('/')}",
|
|
1689
|
+
)
|
|
1690
|
+
if kwargs:
|
|
1691
|
+
try:
|
|
1692
|
+
kwargs = idds.common.utils.json_loads(kwargs)
|
|
1693
|
+
except Exception:
|
|
1694
|
+
kwargs = json.loads(kwargs, object_hook=decode_idds_enum)
|
|
1695
|
+
else:
|
|
1696
|
+
kwargs = {}
|
|
1697
|
+
if "+" in command_name:
|
|
1698
|
+
command_name, idds_host = command_name.split("+")
|
|
1699
|
+
else:
|
|
1700
|
+
idds_host = idds.common.utils.get_rest_host()
|
|
1701
|
+
# check permission
|
|
1702
|
+
if command_name in ["get_status"]:
|
|
1703
|
+
check_owner = False
|
|
1704
|
+
elif command_name in ["abort", "suspend", "resume", "retry", "finish"]:
|
|
1705
|
+
check_owner = True
|
|
1706
|
+
else:
|
|
1707
|
+
tmp_message = f"{command_name} is unsupported"
|
|
1708
|
+
tmp_log.error(tmp_message)
|
|
1709
|
+
return json.dumps((False, tmp_message))
|
|
1710
|
+
# check owner
|
|
1711
|
+
c = iDDS_ClientManager(idds_host)
|
|
1712
|
+
if json_outputs:
|
|
1713
|
+
c.setup_json_outputs()
|
|
1714
|
+
dn = req.subprocess_env.get("SSL_CLIENT_S_DN")
|
|
1715
|
+
if check_owner:
|
|
1716
|
+
# requester
|
|
1717
|
+
if not dn:
|
|
1718
|
+
tmp_message = "SSL_CLIENT_S_DN is missing in HTTP request"
|
|
1719
|
+
tmp_log.error(tmp_message)
|
|
1720
|
+
return json.dumps((False, tmp_message))
|
|
1721
|
+
requester = clean_user_id(dn)
|
|
1722
|
+
# get request_id
|
|
1723
|
+
request_id = kwargs.get("request_id")
|
|
1724
|
+
if request_id is None:
|
|
1725
|
+
tmp_message = "request_id is missing"
|
|
1726
|
+
tmp_log.error(tmp_message)
|
|
1727
|
+
return json.dumps((False, tmp_message))
|
|
1728
|
+
# get request
|
|
1729
|
+
req = c.get_requests(request_id=request_id)
|
|
1730
|
+
if not req:
|
|
1731
|
+
tmp_message = f"request {request_id} is not found"
|
|
1732
|
+
tmp_log.error(tmp_message)
|
|
1733
|
+
return json.dumps((False, tmp_message))
|
|
1734
|
+
user_name = req[0].get("username")
|
|
1735
|
+
if user_name and user_name != requester:
|
|
1736
|
+
tmp_message = f"request {request_id} is not owned by {requester}"
|
|
1737
|
+
tmp_log.error(tmp_message)
|
|
1738
|
+
return json.dumps((False, tmp_message))
|
|
1739
|
+
# set original username
|
|
1740
|
+
if dn:
|
|
1741
|
+
c.set_original_user(user_name=clean_user_id(dn))
|
|
1742
|
+
# execute command
|
|
1743
|
+
tmp_log.debug(f"com={command_name} host={idds_host} kwargs={str(kwargs)}")
|
|
1744
|
+
ret = getattr(c, command_name)(**kwargs)
|
|
1745
|
+
tmp_log.debug(str(ret))
|
|
1746
|
+
if isinstance(ret, dict) and "message" in ret:
|
|
1747
|
+
return json.dumps((True, [ret["status"], ret["message"]]))
|
|
1748
|
+
return json.dumps((True, ret))
|
|
1749
|
+
except Exception as e:
|
|
1750
|
+
tmp_log.error(f"failed with {str(e)} {traceback.format_exc()}")
|
|
1751
|
+
return json.dumps((False, f"server failed with {str(e)}"))
|
|
1752
|
+
|
|
1753
|
+
|
|
1754
|
+
# send command to a job
|
|
1755
|
+
def send_command_to_job(req, panda_id, com):
|
|
1756
|
+
# check security
|
|
1757
|
+
if not isSecure(req):
|
|
1758
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1759
|
+
# check role
|
|
1760
|
+
prod_role = _has_production_role(req)
|
|
1761
|
+
if not prod_role:
|
|
1762
|
+
return json.dumps((False, MESSAGE_PROD_ROLE))
|
|
1763
|
+
return json.dumps(userIF.send_command_to_job(panda_id, com))
|
|
1764
|
+
|
|
1765
|
+
|
|
1766
|
+
# set user secret
|
|
1767
|
+
def set_user_secret(req, key=None, value=None):
|
|
1768
|
+
tmp_log = LogWrapper(_logger, f"set_user_secret-{naive_utcnow().isoformat('/')}")
|
|
1769
|
+
# get owner
|
|
1770
|
+
dn = req.subprocess_env.get("SSL_CLIENT_S_DN")
|
|
1771
|
+
if not dn:
|
|
1772
|
+
tmp_message = "SSL_CLIENT_S_DN is missing in HTTP request"
|
|
1773
|
+
tmp_log.error(tmp_message)
|
|
1774
|
+
return json.dumps((False, tmp_message))
|
|
1775
|
+
owner = clean_user_id(dn)
|
|
1776
|
+
return json.dumps(userIF.set_user_secret(owner, key, value))
|
|
1777
|
+
|
|
1778
|
+
|
|
1779
|
+
# get user secrets
|
|
1780
|
+
def get_user_secrets(req, keys=None, get_json=None):
|
|
1781
|
+
tmp_log = LogWrapper(_logger, f"get_user_secrets-{naive_utcnow().isoformat('/')}")
|
|
1782
|
+
# get owner
|
|
1783
|
+
dn = req.subprocess_env.get("SSL_CLIENT_S_DN")
|
|
1784
|
+
get_json = resolve_true(get_json)
|
|
1785
|
+
|
|
1786
|
+
if not dn:
|
|
1787
|
+
tmp_message = "SSL_CLIENT_S_DN is missing in HTTP request"
|
|
1788
|
+
tmp_log.error(tmp_message)
|
|
1789
|
+
return json.dumps((False, tmp_message))
|
|
1790
|
+
owner = clean_user_id(dn)
|
|
1791
|
+
return json.dumps(userIF.get_user_secrets(owner, keys, get_json))
|
|
1792
|
+
|
|
1793
|
+
|
|
1794
|
+
# get files in datasets
|
|
1795
|
+
def get_files_in_datasets(req, task_id, dataset_types="input,pseudo_input"):
|
|
1796
|
+
# check security
|
|
1797
|
+
if not isSecure(req):
|
|
1798
|
+
return json.dumps((False, MESSAGE_SSL))
|
|
1799
|
+
return json.dumps(userIF.get_files_in_datasets(task_id, dataset_types))
|