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,1006 @@
|
|
|
1
|
+
"""
|
|
2
|
+
job specification
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
11
|
+
|
|
12
|
+
from pandaserver.taskbuffer.FileSpec import FileSpec
|
|
13
|
+
|
|
14
|
+
reserveChangedState = False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class JobSpec(object):
|
|
18
|
+
# attributes
|
|
19
|
+
_attributes = (
|
|
20
|
+
"PandaID",
|
|
21
|
+
"jobDefinitionID",
|
|
22
|
+
"schedulerID",
|
|
23
|
+
"pilotID",
|
|
24
|
+
"creationTime",
|
|
25
|
+
"creationHost",
|
|
26
|
+
"modificationTime",
|
|
27
|
+
"modificationHost",
|
|
28
|
+
"AtlasRelease",
|
|
29
|
+
"transformation",
|
|
30
|
+
"homepackage",
|
|
31
|
+
"prodSeriesLabel",
|
|
32
|
+
"prodSourceLabel",
|
|
33
|
+
"prodUserID",
|
|
34
|
+
"assignedPriority",
|
|
35
|
+
"currentPriority",
|
|
36
|
+
"attemptNr",
|
|
37
|
+
"maxAttempt",
|
|
38
|
+
"jobStatus",
|
|
39
|
+
"jobName",
|
|
40
|
+
"maxCpuCount",
|
|
41
|
+
"maxCpuUnit",
|
|
42
|
+
"maxDiskCount",
|
|
43
|
+
"maxDiskUnit",
|
|
44
|
+
"ipConnectivity",
|
|
45
|
+
"minRamCount",
|
|
46
|
+
"minRamUnit",
|
|
47
|
+
"startTime",
|
|
48
|
+
"endTime",
|
|
49
|
+
"cpuConsumptionTime",
|
|
50
|
+
"cpuConsumptionUnit",
|
|
51
|
+
"commandToPilot",
|
|
52
|
+
"transExitCode",
|
|
53
|
+
"pilotErrorCode",
|
|
54
|
+
"pilotErrorDiag",
|
|
55
|
+
"exeErrorCode",
|
|
56
|
+
"exeErrorDiag",
|
|
57
|
+
"supErrorCode",
|
|
58
|
+
"supErrorDiag",
|
|
59
|
+
"ddmErrorCode",
|
|
60
|
+
"ddmErrorDiag",
|
|
61
|
+
"brokerageErrorCode",
|
|
62
|
+
"brokerageErrorDiag",
|
|
63
|
+
"jobDispatcherErrorCode",
|
|
64
|
+
"jobDispatcherErrorDiag",
|
|
65
|
+
"taskBufferErrorCode",
|
|
66
|
+
"taskBufferErrorDiag",
|
|
67
|
+
"computingSite",
|
|
68
|
+
"computingElement",
|
|
69
|
+
"jobParameters",
|
|
70
|
+
"metadata",
|
|
71
|
+
"prodDBlock",
|
|
72
|
+
"dispatchDBlock",
|
|
73
|
+
"destinationDBlock",
|
|
74
|
+
"destinationSE",
|
|
75
|
+
"nEvents",
|
|
76
|
+
"grid",
|
|
77
|
+
"cloud",
|
|
78
|
+
"cpuConversion",
|
|
79
|
+
"sourceSite",
|
|
80
|
+
"destinationSite",
|
|
81
|
+
"transferType",
|
|
82
|
+
"taskID",
|
|
83
|
+
"cmtConfig",
|
|
84
|
+
"stateChangeTime",
|
|
85
|
+
"prodDBUpdateTime",
|
|
86
|
+
"lockedby",
|
|
87
|
+
"relocationFlag",
|
|
88
|
+
"jobExecutionID",
|
|
89
|
+
"VO",
|
|
90
|
+
"pilotTiming",
|
|
91
|
+
"workingGroup",
|
|
92
|
+
"processingType",
|
|
93
|
+
"prodUserName",
|
|
94
|
+
"nInputFiles",
|
|
95
|
+
"countryGroup",
|
|
96
|
+
"batchID",
|
|
97
|
+
"parentID",
|
|
98
|
+
"specialHandling",
|
|
99
|
+
"jobsetID",
|
|
100
|
+
"coreCount",
|
|
101
|
+
"nInputDataFiles",
|
|
102
|
+
"inputFileType",
|
|
103
|
+
"inputFileProject",
|
|
104
|
+
"inputFileBytes",
|
|
105
|
+
"nOutputDataFiles",
|
|
106
|
+
"outputFileBytes",
|
|
107
|
+
"jobMetrics",
|
|
108
|
+
"workQueue_ID",
|
|
109
|
+
"jediTaskID",
|
|
110
|
+
"jobSubStatus",
|
|
111
|
+
"actualCoreCount",
|
|
112
|
+
"reqID",
|
|
113
|
+
"maxRSS",
|
|
114
|
+
"maxVMEM",
|
|
115
|
+
"maxSWAP",
|
|
116
|
+
"maxPSS",
|
|
117
|
+
"avgRSS",
|
|
118
|
+
"avgVMEM",
|
|
119
|
+
"avgSWAP",
|
|
120
|
+
"avgPSS",
|
|
121
|
+
"maxWalltime",
|
|
122
|
+
"nucleus",
|
|
123
|
+
"eventService",
|
|
124
|
+
"failedAttempt",
|
|
125
|
+
"hs06sec",
|
|
126
|
+
"gshare",
|
|
127
|
+
"hs06",
|
|
128
|
+
"totRCHAR",
|
|
129
|
+
"totWCHAR",
|
|
130
|
+
"totRBYTES",
|
|
131
|
+
"totWBYTES",
|
|
132
|
+
"rateRCHAR",
|
|
133
|
+
"rateWCHAR",
|
|
134
|
+
"rateRBYTES",
|
|
135
|
+
"rateWBYTES",
|
|
136
|
+
"resource_type",
|
|
137
|
+
"diskIO",
|
|
138
|
+
"memory_leak",
|
|
139
|
+
"memory_leak_x2",
|
|
140
|
+
"container_name",
|
|
141
|
+
"job_label",
|
|
142
|
+
"gco2_regional",
|
|
143
|
+
"gco2_global",
|
|
144
|
+
"cpu_architecture_level",
|
|
145
|
+
"outputFileType",
|
|
146
|
+
)
|
|
147
|
+
# slots
|
|
148
|
+
__slots__ = _attributes + ("Files", "_changedAttrs", "_reserveChangedState")
|
|
149
|
+
# attributes which have 0 by default
|
|
150
|
+
_zeroAttrs = (
|
|
151
|
+
"assignedPriority",
|
|
152
|
+
"currentPriority",
|
|
153
|
+
"attemptNr",
|
|
154
|
+
"maxAttempt",
|
|
155
|
+
"maxCpuCount",
|
|
156
|
+
"maxDiskCount",
|
|
157
|
+
"minRamCount",
|
|
158
|
+
"cpuConsumptionTime",
|
|
159
|
+
"pilotErrorCode",
|
|
160
|
+
"exeErrorCode",
|
|
161
|
+
"supErrorCode",
|
|
162
|
+
"ddmErrorCode",
|
|
163
|
+
"brokerageErrorCode",
|
|
164
|
+
"jobDispatcherErrorCode",
|
|
165
|
+
"taskBufferErrorCode",
|
|
166
|
+
"nEvents",
|
|
167
|
+
"relocationFlag",
|
|
168
|
+
"jobExecutionID",
|
|
169
|
+
"nOutputDataFiles",
|
|
170
|
+
"outputFileBytes",
|
|
171
|
+
)
|
|
172
|
+
# attribute to be suppressed. They are in another table
|
|
173
|
+
_suppAttrs = ("jobParameters", "metadata")
|
|
174
|
+
# mapping between sequence and attr
|
|
175
|
+
_seqAttrMap = {"PandaID": "ATLAS_PANDA.JOBSDEFINED4_PANDAID_SEQ.nextval"}
|
|
176
|
+
# limit length
|
|
177
|
+
_limitLength = {
|
|
178
|
+
"ddmErrorDiag": 500,
|
|
179
|
+
"taskBufferErrorDiag": 300,
|
|
180
|
+
"jobDispatcherErrorDiag": 250,
|
|
181
|
+
"brokerageErrorDiag": 250,
|
|
182
|
+
"pilotErrorDiag": 500,
|
|
183
|
+
"exeErrorDiag": 500,
|
|
184
|
+
"jobSubStatus": 80,
|
|
185
|
+
"supErrorDiag": 250,
|
|
186
|
+
"commandToPilot": 250,
|
|
187
|
+
"inputFileType": 32,
|
|
188
|
+
"outputFileType": 32,
|
|
189
|
+
"grid": 50,
|
|
190
|
+
"sourceSite": 36,
|
|
191
|
+
"destinationSite": 36,
|
|
192
|
+
}
|
|
193
|
+
# tag for special handling
|
|
194
|
+
_tagForSH = {
|
|
195
|
+
"altStgOut": "ao",
|
|
196
|
+
"acceptPartial": "ap",
|
|
197
|
+
"allOkEvents": "at",
|
|
198
|
+
"notDiscardEvents": "de",
|
|
199
|
+
"decAttOnFailedES": "df",
|
|
200
|
+
"debugMode": "dm",
|
|
201
|
+
"dynamicNumEvents": "dy",
|
|
202
|
+
"encJobParams": "ej",
|
|
203
|
+
"fakeJobToIgnore": "fake",
|
|
204
|
+
"homeCloud": "hc",
|
|
205
|
+
"hpoWorkflow": "ho",
|
|
206
|
+
"inFilePosEvtNum": "if",
|
|
207
|
+
"inputPrestaging": "ip",
|
|
208
|
+
"lumiBlock": "lb",
|
|
209
|
+
"noLoopingCheck": "lc",
|
|
210
|
+
"mergeAtOs": "mo",
|
|
211
|
+
"noExecStrCnv": "nc",
|
|
212
|
+
"onSiteMerging": "om",
|
|
213
|
+
"pushStatusChanges": "pc",
|
|
214
|
+
"pushJob": "pj",
|
|
215
|
+
"putLogToOS": "po",
|
|
216
|
+
"registerEsFiles": "re",
|
|
217
|
+
"retryRam": "rr",
|
|
218
|
+
"resurrectConsumers": "rs",
|
|
219
|
+
"requestType": "rt",
|
|
220
|
+
"jobCloning": "sc",
|
|
221
|
+
"scoutJob": "sj",
|
|
222
|
+
"taskQueuedTime": "tq",
|
|
223
|
+
"usePrefetcher": "up",
|
|
224
|
+
"useSecrets": "us",
|
|
225
|
+
"useZipToPin": "uz",
|
|
226
|
+
"writeInputToFile": "wf",
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# constructor
|
|
230
|
+
def __init__(self):
|
|
231
|
+
# install attributes
|
|
232
|
+
for attr in self._attributes:
|
|
233
|
+
object.__setattr__(self, attr, None)
|
|
234
|
+
# files list
|
|
235
|
+
object.__setattr__(self, "Files", [])
|
|
236
|
+
# map of changed attributes
|
|
237
|
+
object.__setattr__(self, "_changedAttrs", {})
|
|
238
|
+
# reserve changed state at instance level
|
|
239
|
+
object.__setattr__(self, "_reserveChangedState", False)
|
|
240
|
+
|
|
241
|
+
# override __getattribute__ for SQL
|
|
242
|
+
def __getattribute__(self, name):
|
|
243
|
+
ret = object.__getattribute__(self, name)
|
|
244
|
+
if ret is None:
|
|
245
|
+
return "NULL"
|
|
246
|
+
return ret
|
|
247
|
+
|
|
248
|
+
# override __setattr__ to collecte the changed attributes
|
|
249
|
+
def __setattr__(self, name, value):
|
|
250
|
+
oldVal = getattr(self, name)
|
|
251
|
+
object.__setattr__(self, name, value)
|
|
252
|
+
newVal = getattr(self, name)
|
|
253
|
+
if name == "jobStatus":
|
|
254
|
+
if oldVal != newVal:
|
|
255
|
+
self.stateChangeTime = naive_utcnow()
|
|
256
|
+
# collect changed attributes
|
|
257
|
+
if oldVal != newVal and name not in self._suppAttrs:
|
|
258
|
+
self._changedAttrs[name] = value
|
|
259
|
+
|
|
260
|
+
# reset changed attribute list
|
|
261
|
+
def resetChangedList(self):
|
|
262
|
+
object.__setattr__(self, "_changedAttrs", {})
|
|
263
|
+
|
|
264
|
+
# add File to files list
|
|
265
|
+
def addFile(self, file):
|
|
266
|
+
# set owner
|
|
267
|
+
file.setOwner(self)
|
|
268
|
+
# append
|
|
269
|
+
self.Files.append(file)
|
|
270
|
+
|
|
271
|
+
# pack tuple into JobSpec
|
|
272
|
+
def pack(self, values):
|
|
273
|
+
for i in range(len(self._attributes)):
|
|
274
|
+
attr = self._attributes[i]
|
|
275
|
+
val = values[i]
|
|
276
|
+
object.__setattr__(self, attr, val)
|
|
277
|
+
|
|
278
|
+
# return a tuple of values
|
|
279
|
+
def values(self):
|
|
280
|
+
ret = []
|
|
281
|
+
for attr in self._attributes:
|
|
282
|
+
val = getattr(self, attr)
|
|
283
|
+
ret.append(val)
|
|
284
|
+
return tuple(ret)
|
|
285
|
+
|
|
286
|
+
# return map of values
|
|
287
|
+
def valuesMap(self, useSeq=False, onlyChanged=False):
|
|
288
|
+
ret = {}
|
|
289
|
+
for attr in self._attributes:
|
|
290
|
+
if useSeq and attr in self._seqAttrMap:
|
|
291
|
+
continue
|
|
292
|
+
if onlyChanged:
|
|
293
|
+
if attr not in self._changedAttrs:
|
|
294
|
+
continue
|
|
295
|
+
val = getattr(self, attr)
|
|
296
|
+
if val == "NULL":
|
|
297
|
+
if attr in self._zeroAttrs:
|
|
298
|
+
val = 0
|
|
299
|
+
else:
|
|
300
|
+
val = None
|
|
301
|
+
# jobParameters/metadata go to another table
|
|
302
|
+
if attr in self._suppAttrs:
|
|
303
|
+
val = None
|
|
304
|
+
# truncate too long values
|
|
305
|
+
if attr in self._limitLength:
|
|
306
|
+
if val is not None:
|
|
307
|
+
val = val[: self._limitLength[attr]]
|
|
308
|
+
ret[f":{attr}"] = val
|
|
309
|
+
return ret
|
|
310
|
+
|
|
311
|
+
# return state values to be pickled
|
|
312
|
+
def __getstate__(self):
|
|
313
|
+
state = []
|
|
314
|
+
for attr in self._attributes:
|
|
315
|
+
val = getattr(self, attr)
|
|
316
|
+
state.append(val)
|
|
317
|
+
if reserveChangedState or self._reserveChangedState:
|
|
318
|
+
state.append(self._changedAttrs)
|
|
319
|
+
# append File info
|
|
320
|
+
state.append(self.Files)
|
|
321
|
+
return state
|
|
322
|
+
|
|
323
|
+
# restore state from the unpickled state values
|
|
324
|
+
def __setstate__(self, state):
|
|
325
|
+
for i in range(len(self._attributes)):
|
|
326
|
+
# schema evolution is supported only when adding attributes
|
|
327
|
+
if i + 1 < len(state):
|
|
328
|
+
object.__setattr__(self, self._attributes[i], state[i])
|
|
329
|
+
else:
|
|
330
|
+
object.__setattr__(self, self._attributes[i], "NULL")
|
|
331
|
+
object.__setattr__(self, "Files", state[-1])
|
|
332
|
+
if not hasattr(self, "_reserveChangedState"):
|
|
333
|
+
object.__setattr__(self, "_reserveChangedState", False)
|
|
334
|
+
if reserveChangedState or self._reserveChangedState:
|
|
335
|
+
object.__setattr__(self, "_changedAttrs", state[-2])
|
|
336
|
+
else:
|
|
337
|
+
object.__setattr__(self, "_changedAttrs", {})
|
|
338
|
+
|
|
339
|
+
# return column names for INSERT or full SELECT
|
|
340
|
+
def columnNames(cls):
|
|
341
|
+
ret = ""
|
|
342
|
+
for attr in cls._attributes:
|
|
343
|
+
if ret != "":
|
|
344
|
+
ret += ","
|
|
345
|
+
ret += attr
|
|
346
|
+
return ret
|
|
347
|
+
|
|
348
|
+
columnNames = classmethod(columnNames)
|
|
349
|
+
|
|
350
|
+
# return expression of values for INSERT
|
|
351
|
+
def valuesExpression(cls):
|
|
352
|
+
ret = "VALUES("
|
|
353
|
+
for attr in cls._attributes:
|
|
354
|
+
ret += "%s"
|
|
355
|
+
if attr != cls._attributes[len(cls._attributes) - 1]:
|
|
356
|
+
ret += ","
|
|
357
|
+
ret += ")"
|
|
358
|
+
return ret
|
|
359
|
+
|
|
360
|
+
valuesExpression = classmethod(valuesExpression)
|
|
361
|
+
|
|
362
|
+
# return expression of bind values for INSERT
|
|
363
|
+
def bindValuesExpression(cls, useSeq=False):
|
|
364
|
+
from pandaserver.config import panda_config
|
|
365
|
+
|
|
366
|
+
ret = "VALUES("
|
|
367
|
+
for attr in cls._attributes:
|
|
368
|
+
if useSeq and attr in cls._seqAttrMap:
|
|
369
|
+
if panda_config.backend == "mysql":
|
|
370
|
+
# mysql
|
|
371
|
+
ret += f"NULL,"
|
|
372
|
+
else:
|
|
373
|
+
# oracle
|
|
374
|
+
ret += f"{cls._seqAttrMap[attr]},"
|
|
375
|
+
else:
|
|
376
|
+
ret += f":{attr},"
|
|
377
|
+
ret = ret[:-1]
|
|
378
|
+
ret += ")"
|
|
379
|
+
return ret
|
|
380
|
+
|
|
381
|
+
bindValuesExpression = classmethod(bindValuesExpression)
|
|
382
|
+
|
|
383
|
+
# return an expression for UPDATE
|
|
384
|
+
def updateExpression(cls):
|
|
385
|
+
ret = ""
|
|
386
|
+
for attr in cls._attributes:
|
|
387
|
+
ret = ret + attr + "=%s"
|
|
388
|
+
if attr != cls._attributes[len(cls._attributes) - 1]:
|
|
389
|
+
ret += ","
|
|
390
|
+
return ret
|
|
391
|
+
|
|
392
|
+
updateExpression = classmethod(updateExpression)
|
|
393
|
+
|
|
394
|
+
# return an expression of bind variables for UPDATE
|
|
395
|
+
def bindUpdateExpression(cls):
|
|
396
|
+
ret = ""
|
|
397
|
+
for attr in cls._attributes:
|
|
398
|
+
ret += f"{attr}=:{attr},"
|
|
399
|
+
ret = ret[:-1]
|
|
400
|
+
ret += " "
|
|
401
|
+
return ret
|
|
402
|
+
|
|
403
|
+
bindUpdateExpression = classmethod(bindUpdateExpression)
|
|
404
|
+
|
|
405
|
+
# comparison function for sort
|
|
406
|
+
def compFunc(cls, a, b):
|
|
407
|
+
iPandaID = list(cls._attributes).index("PandaID")
|
|
408
|
+
iPriority = list(cls._attributes).index("currentPriority")
|
|
409
|
+
if a[iPriority] > b[iPriority]:
|
|
410
|
+
return -1
|
|
411
|
+
elif a[iPriority] < b[iPriority]:
|
|
412
|
+
return 1
|
|
413
|
+
else:
|
|
414
|
+
if a[iPandaID] > b[iPandaID]:
|
|
415
|
+
return 1
|
|
416
|
+
elif a[iPandaID] < b[iPandaID]:
|
|
417
|
+
return -1
|
|
418
|
+
else:
|
|
419
|
+
return 0
|
|
420
|
+
|
|
421
|
+
compFunc = classmethod(compFunc)
|
|
422
|
+
|
|
423
|
+
# return an expression of bind variables for UPDATE to update only changed attributes
|
|
424
|
+
def bindUpdateChangesExpression(self):
|
|
425
|
+
ret = ""
|
|
426
|
+
for attr in self._attributes:
|
|
427
|
+
if attr in self._changedAttrs:
|
|
428
|
+
ret += f"{attr}=:{attr},"
|
|
429
|
+
ret = ret[:-1]
|
|
430
|
+
ret += " "
|
|
431
|
+
return ret
|
|
432
|
+
|
|
433
|
+
# check if goint to merging
|
|
434
|
+
def produceUnMerge(self):
|
|
435
|
+
for tmpFile in self.Files:
|
|
436
|
+
if tmpFile.isUnMergedOutput():
|
|
437
|
+
return True
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
# truncate string attribute
|
|
441
|
+
def truncateStringAttr(cls, attr, val):
|
|
442
|
+
if attr not in cls._limitLength:
|
|
443
|
+
return val
|
|
444
|
+
if val is None:
|
|
445
|
+
return val
|
|
446
|
+
return val[: cls._limitLength[attr]]
|
|
447
|
+
|
|
448
|
+
truncateStringAttr = classmethod(truncateStringAttr)
|
|
449
|
+
|
|
450
|
+
# set DDM backend
|
|
451
|
+
def setDdmBackEnd(self, backEnd):
|
|
452
|
+
if self.specialHandling in [None, ""]:
|
|
453
|
+
self.specialHandling = "ddm:" + backEnd
|
|
454
|
+
else:
|
|
455
|
+
if "ddm:" in self.specialHandling:
|
|
456
|
+
self.specialHandling = re.sub("ddm:[,]+", "ddm:" + backEnd, self.specialHandling)
|
|
457
|
+
else:
|
|
458
|
+
self.specialHandling = self.specialHandling + "," + "ddm:" + backEnd
|
|
459
|
+
|
|
460
|
+
# set LB number
|
|
461
|
+
def setLumiBlockNr(self, lumiBlockNr):
|
|
462
|
+
if self.specialHandling in ["", None, "NULL"]:
|
|
463
|
+
self.specialHandling = f"lb:{lumiBlockNr}"
|
|
464
|
+
else:
|
|
465
|
+
self.specialHandling += f",lb:{lumiBlockNr}"
|
|
466
|
+
|
|
467
|
+
# get LB number
|
|
468
|
+
def getLumiBlockNr(self):
|
|
469
|
+
if self.specialHandling is not None:
|
|
470
|
+
for tmpItem in self.specialHandling.split(","):
|
|
471
|
+
if tmpItem.startswith("lb:"):
|
|
472
|
+
return int(tmpItem.split(":")[-1])
|
|
473
|
+
return None
|
|
474
|
+
|
|
475
|
+
# get DDM backend
|
|
476
|
+
def getDdmBackEnd(self):
|
|
477
|
+
if self.specialHandling is None:
|
|
478
|
+
return None
|
|
479
|
+
for tmpItem in self.specialHandling.split(","):
|
|
480
|
+
if tmpItem.startswith("ddm:"):
|
|
481
|
+
return tmpItem.split(":")[-1]
|
|
482
|
+
return None
|
|
483
|
+
|
|
484
|
+
# set to accept partial finish
|
|
485
|
+
def setToAcceptPartialFinish(self):
|
|
486
|
+
self.set_special_handling("acceptPartial")
|
|
487
|
+
|
|
488
|
+
# accept partial finish
|
|
489
|
+
def acceptPartialFinish(self):
|
|
490
|
+
return self.check_special_handling("acceptPartial")
|
|
491
|
+
|
|
492
|
+
# set home cloud
|
|
493
|
+
def setHomeCloud(self, homeCloud):
|
|
494
|
+
if self.specialHandling in ["", None, "NULL"]:
|
|
495
|
+
self.specialHandling = f"hc:{homeCloud}"
|
|
496
|
+
else:
|
|
497
|
+
self.specialHandling += f",hc:{homeCloud}"
|
|
498
|
+
|
|
499
|
+
# get cloud
|
|
500
|
+
def getCloud(self):
|
|
501
|
+
if self.specialHandling is not None:
|
|
502
|
+
for tmpItem in self.specialHandling.split(","):
|
|
503
|
+
if tmpItem.startswith("hc:"): # hc: Home Cloud
|
|
504
|
+
return tmpItem.split(":")[-1]
|
|
505
|
+
return self.cloud
|
|
506
|
+
|
|
507
|
+
# check if cancelled or it's flavor
|
|
508
|
+
def isCancelled(self):
|
|
509
|
+
return self.jobStatus in ["cancelled", "closed"]
|
|
510
|
+
|
|
511
|
+
# get file names which were uploaded to alternative locations
|
|
512
|
+
def altStgOutFileList(self):
|
|
513
|
+
try:
|
|
514
|
+
if self.jobMetrics is not None:
|
|
515
|
+
for item in self.jobMetrics.split():
|
|
516
|
+
if item.startswith("altTransferred="):
|
|
517
|
+
return item.split("=")[-1].split(",")
|
|
518
|
+
except Exception:
|
|
519
|
+
pass
|
|
520
|
+
return []
|
|
521
|
+
|
|
522
|
+
# check special handling
|
|
523
|
+
def check_special_handling(self, key):
|
|
524
|
+
if self.specialHandling:
|
|
525
|
+
return self._tagForSH[key] in self.specialHandling.split(",")
|
|
526
|
+
return False
|
|
527
|
+
|
|
528
|
+
# set special handling
|
|
529
|
+
def set_special_handling(self, key):
|
|
530
|
+
if self.specialHandling:
|
|
531
|
+
items = self.specialHandling.split(",")
|
|
532
|
+
else:
|
|
533
|
+
items = []
|
|
534
|
+
if self._tagForSH[key] not in items:
|
|
535
|
+
items.append(self._tagForSH[key])
|
|
536
|
+
self.specialHandling = ",".join(items)
|
|
537
|
+
|
|
538
|
+
# get mode for alternative stage-out
|
|
539
|
+
def getAltStgOut(self):
|
|
540
|
+
if self.specialHandling is not None:
|
|
541
|
+
for tmpItem in self.specialHandling.split(","):
|
|
542
|
+
if tmpItem.startswith(f"{self._tagForSH['altStgOut']}:"):
|
|
543
|
+
return tmpItem.split(":")[-1]
|
|
544
|
+
return None
|
|
545
|
+
|
|
546
|
+
# set alternative stage-out
|
|
547
|
+
def setAltStgOut(self, mode):
|
|
548
|
+
if self.specialHandling is not None:
|
|
549
|
+
items = self.specialHandling.split(",")
|
|
550
|
+
else:
|
|
551
|
+
items = []
|
|
552
|
+
# remove old value
|
|
553
|
+
newItems = []
|
|
554
|
+
for tmpItem in items:
|
|
555
|
+
if tmpItem.startswith(f"{self._tagForSH['altStgOut']}:"):
|
|
556
|
+
continue
|
|
557
|
+
newItems.append(tmpItem)
|
|
558
|
+
newItems.append(f"{self._tagForSH['altStgOut']}:{mode}")
|
|
559
|
+
self.specialHandling = ",".join(newItems)
|
|
560
|
+
|
|
561
|
+
# put log files to OS
|
|
562
|
+
def putLogToOS(self):
|
|
563
|
+
return self.check_special_handling("putLogToOS")
|
|
564
|
+
|
|
565
|
+
# set to put log files to OS
|
|
566
|
+
def setToPutLogToOS(self):
|
|
567
|
+
self.set_special_handling("putLogToOS")
|
|
568
|
+
|
|
569
|
+
# write input to file
|
|
570
|
+
def writeInputToFile(self):
|
|
571
|
+
return self.check_special_handling("writeInputToFile")
|
|
572
|
+
|
|
573
|
+
# set to write input to file
|
|
574
|
+
def setToWriteInputToFile(self):
|
|
575
|
+
self.set_special_handling("writeInputToFile")
|
|
576
|
+
|
|
577
|
+
# set request type
|
|
578
|
+
def setRequestType(self, reqType):
|
|
579
|
+
if self.specialHandling is not None:
|
|
580
|
+
items = self.specialHandling.split(",")
|
|
581
|
+
else:
|
|
582
|
+
items = []
|
|
583
|
+
newItems = []
|
|
584
|
+
setFlag = False
|
|
585
|
+
for item in items:
|
|
586
|
+
if not item.startswith(self._tagForSH["requestType"]):
|
|
587
|
+
newItems.append(item)
|
|
588
|
+
newItems.append(f"{self._tagForSH['requestType']}={reqType}")
|
|
589
|
+
self.specialHandling = ",".join(newItems)
|
|
590
|
+
|
|
591
|
+
# sort files
|
|
592
|
+
def sortFiles(self):
|
|
593
|
+
try:
|
|
594
|
+
lfnMap = {}
|
|
595
|
+
for tmpFile in self.Files:
|
|
596
|
+
if tmpFile.lfn not in lfnMap:
|
|
597
|
+
lfnMap[tmpFile.lfn] = []
|
|
598
|
+
lfnMap[tmpFile.lfn].append(tmpFile)
|
|
599
|
+
lfns = sorted(lfnMap)
|
|
600
|
+
newFiles = []
|
|
601
|
+
for tmpLFN in lfns:
|
|
602
|
+
for tmpFile in lfnMap[tmpLFN]:
|
|
603
|
+
newFiles.append(tmpFile)
|
|
604
|
+
self.Files = newFiles
|
|
605
|
+
except Exception:
|
|
606
|
+
pass
|
|
607
|
+
|
|
608
|
+
# get zip file map
|
|
609
|
+
def getZipFileMap(self):
|
|
610
|
+
zipMap = dict()
|
|
611
|
+
try:
|
|
612
|
+
if self.jobParameters is not None:
|
|
613
|
+
zipStr = re.search("<ZIP_MAP>(.+)</ZIP_MAP>", self.jobParameters)
|
|
614
|
+
if zipStr is not None:
|
|
615
|
+
for item in zipStr.group(1).split():
|
|
616
|
+
zipFile, conFiles = item.split(":")
|
|
617
|
+
conFiles = conFiles.split(",")
|
|
618
|
+
zipMap[zipFile] = conFiles
|
|
619
|
+
except Exception:
|
|
620
|
+
pass
|
|
621
|
+
return zipMap
|
|
622
|
+
|
|
623
|
+
# add multi step exec
|
|
624
|
+
def addMultiStepExec(self, steps):
|
|
625
|
+
if not self.jobParameters:
|
|
626
|
+
self.jobParameters = ""
|
|
627
|
+
self.jobParameters += "<MULTI_STEP_EXEC>" + json.dumps(steps) + "</MULTI_STEP_EXEC>"
|
|
628
|
+
|
|
629
|
+
# extract multi step exec
|
|
630
|
+
def extractMultiStepExec(self):
|
|
631
|
+
try:
|
|
632
|
+
if "<MULTI_STEP_EXEC>" in self.jobParameters and "</MULTI_STEP_EXEC>" in self.jobParameters:
|
|
633
|
+
pp_1, pp_2 = self.jobParameters.split("<MULTI_STEP_EXEC>")
|
|
634
|
+
pp_2 = pp_2.split("</MULTI_STEP_EXEC>")[0]
|
|
635
|
+
return pp_1, json.loads(pp_2)
|
|
636
|
+
except Exception:
|
|
637
|
+
pass
|
|
638
|
+
return self.jobParameters, None
|
|
639
|
+
|
|
640
|
+
# suppress execute string conversion
|
|
641
|
+
def noExecStrCnv(self):
|
|
642
|
+
return self.check_special_handling("noExecStrCnv")
|
|
643
|
+
|
|
644
|
+
# set to suppress execute string conversion
|
|
645
|
+
def setNoExecStrCnv(self):
|
|
646
|
+
self.set_special_handling("noExecStrCnv")
|
|
647
|
+
|
|
648
|
+
# in-file positional event number
|
|
649
|
+
def inFilePosEvtNum(self):
|
|
650
|
+
return self.check_special_handling("inFilePosEvtNum")
|
|
651
|
+
|
|
652
|
+
# set to use in-file positional event number
|
|
653
|
+
def setInFilePosEvtNum(self):
|
|
654
|
+
self.set_special_handling("inFilePosEvtNum")
|
|
655
|
+
|
|
656
|
+
# register event service files
|
|
657
|
+
def registerEsFiles(self):
|
|
658
|
+
return self.check_special_handling("registerEsFiles")
|
|
659
|
+
|
|
660
|
+
# set to register event service files
|
|
661
|
+
def setRegisterEsFiles(self):
|
|
662
|
+
self.set_special_handling("registerEsFiles")
|
|
663
|
+
|
|
664
|
+
# set background-able flag
|
|
665
|
+
def setBackgroundableFlag(self):
|
|
666
|
+
self.jobExecutionID = 0
|
|
667
|
+
if self.prodSourceLabel not in ["managed", "test"]:
|
|
668
|
+
return
|
|
669
|
+
try:
|
|
670
|
+
if self.inputFileBytes / self.maxWalltime > 5000:
|
|
671
|
+
return
|
|
672
|
+
except Exception:
|
|
673
|
+
return
|
|
674
|
+
try:
|
|
675
|
+
if self.coreCount <= 1:
|
|
676
|
+
return
|
|
677
|
+
except Exception:
|
|
678
|
+
return
|
|
679
|
+
if self.currentPriority > 250:
|
|
680
|
+
return
|
|
681
|
+
self.jobExecutionID = 1
|
|
682
|
+
|
|
683
|
+
# use prefetcher
|
|
684
|
+
def usePrefetcher(self):
|
|
685
|
+
return self.check_special_handling("usePrefetcher")
|
|
686
|
+
|
|
687
|
+
# set to use prefetcher
|
|
688
|
+
def setUsePrefetcher(self):
|
|
689
|
+
self.set_special_handling("usePrefetcher")
|
|
690
|
+
|
|
691
|
+
# use zip to pin
|
|
692
|
+
def useZipToPin(self):
|
|
693
|
+
return self.check_special_handling("useZipToPin")
|
|
694
|
+
|
|
695
|
+
# set to use zip to pin
|
|
696
|
+
def setUseZipToPin(self):
|
|
697
|
+
self.set_special_handling("useZipToPin")
|
|
698
|
+
|
|
699
|
+
# use secrets
|
|
700
|
+
def use_secrets(self):
|
|
701
|
+
return self.check_special_handling("useSecrets")
|
|
702
|
+
|
|
703
|
+
# set to use secrets
|
|
704
|
+
def set_use_secrets(self):
|
|
705
|
+
self.set_special_handling("useSecrets")
|
|
706
|
+
|
|
707
|
+
# not discard events
|
|
708
|
+
def notDiscardEvents(self):
|
|
709
|
+
return self.check_special_handling("notDiscardEvents")
|
|
710
|
+
|
|
711
|
+
# set not to discard events
|
|
712
|
+
def setNotDiscardEvents(self):
|
|
713
|
+
self.set_special_handling("notDiscardEvents")
|
|
714
|
+
|
|
715
|
+
# all events are done
|
|
716
|
+
def allOkEvents(self):
|
|
717
|
+
return self.check_special_handling("allOkEvents")
|
|
718
|
+
|
|
719
|
+
# set all events are done
|
|
720
|
+
def setAllOkEvents(self):
|
|
721
|
+
self.set_special_handling("allOkEvents")
|
|
722
|
+
|
|
723
|
+
# set scout job flag
|
|
724
|
+
def setScoutJobFlag(self):
|
|
725
|
+
self.set_special_handling("scoutJob")
|
|
726
|
+
|
|
727
|
+
# check if scout job
|
|
728
|
+
def isScoutJob(self):
|
|
729
|
+
return self.check_special_handling("scoutJob")
|
|
730
|
+
|
|
731
|
+
# decrement attemptNr of events only when failed
|
|
732
|
+
def decAttOnFailedES(self):
|
|
733
|
+
return self.check_special_handling("decAttOnFailedES")
|
|
734
|
+
|
|
735
|
+
# set to decrement attemptNr of events only when failed
|
|
736
|
+
def setDecAttOnFailedES(self):
|
|
737
|
+
self.set_special_handling("decAttOnFailedES")
|
|
738
|
+
|
|
739
|
+
# set fake flag to ignore in monigoring
|
|
740
|
+
def setFakeJobToIgnore(self):
|
|
741
|
+
self.set_special_handling("fakeJobToIgnore")
|
|
742
|
+
|
|
743
|
+
# remove fake flag to ignore in monigoring
|
|
744
|
+
def removeFakeJobToIgnore(self):
|
|
745
|
+
if self.specialHandling is not None:
|
|
746
|
+
items = self.specialHandling.split(",")
|
|
747
|
+
else:
|
|
748
|
+
items = []
|
|
749
|
+
if self._tagForSH["fakeJobToIgnore"] in items:
|
|
750
|
+
items.remove(self._tagForSH["fakeJobToIgnore"])
|
|
751
|
+
self.specialHandling = ",".join(items)
|
|
752
|
+
|
|
753
|
+
# set task attribute
|
|
754
|
+
def set_task_attribute(self, key, value):
|
|
755
|
+
if not isinstance(self.metadata, list):
|
|
756
|
+
self.metadata = [None, None]
|
|
757
|
+
if len(self.metadata) != 3:
|
|
758
|
+
self.metadata.append({})
|
|
759
|
+
self.metadata[2][key] = value
|
|
760
|
+
|
|
761
|
+
# get task attribute
|
|
762
|
+
def get_task_attribute(self, key):
|
|
763
|
+
try:
|
|
764
|
+
return self.metadata[2][key]
|
|
765
|
+
except Exception:
|
|
766
|
+
return None
|
|
767
|
+
|
|
768
|
+
# set input prestaging
|
|
769
|
+
def setInputPrestaging(self):
|
|
770
|
+
self.set_special_handling("inputPrestaging")
|
|
771
|
+
|
|
772
|
+
# use input prestaging
|
|
773
|
+
def useInputPrestaging(self):
|
|
774
|
+
return self.check_special_handling("inputPrestaging")
|
|
775
|
+
|
|
776
|
+
# to a dictionary
|
|
777
|
+
def to_dict_advanced(self):
|
|
778
|
+
ret = {}
|
|
779
|
+
|
|
780
|
+
# Get the job attributes
|
|
781
|
+
for a in self._attributes:
|
|
782
|
+
v = getattr(self, a)
|
|
783
|
+
if v == "NULL":
|
|
784
|
+
v = None
|
|
785
|
+
ret[a] = v
|
|
786
|
+
|
|
787
|
+
# Add the files explicitly, as they are not included under _attributes
|
|
788
|
+
a = "Files"
|
|
789
|
+
files = getattr(self, a)
|
|
790
|
+
file_stats = []
|
|
791
|
+
for f in files:
|
|
792
|
+
file_stats.append(f.to_dict())
|
|
793
|
+
ret[a] = file_stats
|
|
794
|
+
|
|
795
|
+
return ret
|
|
796
|
+
|
|
797
|
+
def to_dict(self):
|
|
798
|
+
ret = {}
|
|
799
|
+
for a in self._attributes:
|
|
800
|
+
v = getattr(self, a)
|
|
801
|
+
if isinstance(v, datetime.datetime):
|
|
802
|
+
v = str(v)
|
|
803
|
+
elif v == "NULL":
|
|
804
|
+
v = None
|
|
805
|
+
ret[a] = v
|
|
806
|
+
return ret
|
|
807
|
+
|
|
808
|
+
# check if HPO workflow flag
|
|
809
|
+
def is_hpo_workflow(self):
|
|
810
|
+
return self.check_special_handling("hpoWorkflow")
|
|
811
|
+
|
|
812
|
+
# set HPO workflow flag
|
|
813
|
+
def set_hpo_workflow(self):
|
|
814
|
+
self.set_special_handling("hpoWorkflow")
|
|
815
|
+
|
|
816
|
+
# check if looping check is disabled
|
|
817
|
+
def is_no_looping_check(self):
|
|
818
|
+
return self.check_special_handling("noLoopingCheck")
|
|
819
|
+
|
|
820
|
+
# disable looping check
|
|
821
|
+
def disable_looping_check(self):
|
|
822
|
+
self.set_special_handling("noLoopingCheck")
|
|
823
|
+
|
|
824
|
+
# check if encode job parameters
|
|
825
|
+
def to_encode_job_params(self):
|
|
826
|
+
return self.check_special_handling("encJobParams")
|
|
827
|
+
|
|
828
|
+
# encode job parameters
|
|
829
|
+
def set_encode_job_params(self):
|
|
830
|
+
self.set_special_handling("encJobParams")
|
|
831
|
+
|
|
832
|
+
# check if debug mode
|
|
833
|
+
def is_debug_mode(self):
|
|
834
|
+
if self.specialHandling is not None:
|
|
835
|
+
items = self.specialHandling.split(",")
|
|
836
|
+
return self._tagForSH["debugMode"] in items or "debug" in items
|
|
837
|
+
return False
|
|
838
|
+
|
|
839
|
+
# set debug mode
|
|
840
|
+
def set_debug_mode(self):
|
|
841
|
+
self.set_special_handling("debugMode")
|
|
842
|
+
|
|
843
|
+
# set push status changes
|
|
844
|
+
def set_push_status_changes(self):
|
|
845
|
+
self.set_special_handling("pushStatusChanges")
|
|
846
|
+
|
|
847
|
+
# check if to push status changes
|
|
848
|
+
def push_status_changes(self):
|
|
849
|
+
return push_status_changes(self.specialHandling)
|
|
850
|
+
|
|
851
|
+
# set push job
|
|
852
|
+
def set_push_job(self):
|
|
853
|
+
self.set_special_handling("pushJob")
|
|
854
|
+
|
|
855
|
+
# check if to push job
|
|
856
|
+
def is_push_job(self):
|
|
857
|
+
return self.check_special_handling("pushJob")
|
|
858
|
+
|
|
859
|
+
# set on-site merging
|
|
860
|
+
def set_on_site_merging(self):
|
|
861
|
+
self.set_special_handling("onSiteMerging")
|
|
862
|
+
|
|
863
|
+
# check if on-site merging
|
|
864
|
+
def is_on_site_merging(self):
|
|
865
|
+
return self.check_special_handling("onSiteMerging")
|
|
866
|
+
|
|
867
|
+
# get RAM for retry
|
|
868
|
+
def get_ram_for_retry(self):
|
|
869
|
+
if self.specialHandling is not None:
|
|
870
|
+
for tmpItem in self.specialHandling.split(","):
|
|
871
|
+
if tmpItem.startswith(f"{self._tagForSH['retryRam']}:"):
|
|
872
|
+
return int(tmpItem.split(":")[-1])
|
|
873
|
+
return None
|
|
874
|
+
|
|
875
|
+
# set RAM for retry
|
|
876
|
+
def set_ram_for_retry(self, val):
|
|
877
|
+
if self.specialHandling:
|
|
878
|
+
items = self.specialHandling.split(",")
|
|
879
|
+
else:
|
|
880
|
+
items = []
|
|
881
|
+
# remove old value
|
|
882
|
+
newItems = []
|
|
883
|
+
for tmpItem in items:
|
|
884
|
+
if tmpItem.startswith(f"{self._tagForSH['retryRam']}:"):
|
|
885
|
+
continue
|
|
886
|
+
newItems.append(tmpItem)
|
|
887
|
+
newItems.append(f"{self._tagForSH['retryRam']}:{val}")
|
|
888
|
+
self.specialHandling = ",".join(newItems)
|
|
889
|
+
|
|
890
|
+
# dump to json-serializable
|
|
891
|
+
def dump_to_json_serializable(self):
|
|
892
|
+
job_state = self.__getstate__()
|
|
893
|
+
file_state_list = []
|
|
894
|
+
for file_spec in job_state[-1]:
|
|
895
|
+
file_stat = file_spec.dump_to_json_serializable()
|
|
896
|
+
file_state_list.append(file_stat)
|
|
897
|
+
job_state = job_state[:-1]
|
|
898
|
+
# append files
|
|
899
|
+
job_state.append(file_state_list)
|
|
900
|
+
return job_state
|
|
901
|
+
|
|
902
|
+
# load from json-serializable
|
|
903
|
+
def load_from_json_serializable(self, job_state):
|
|
904
|
+
# initialize with empty file list
|
|
905
|
+
self.__setstate__(job_state[:-1] + [[]])
|
|
906
|
+
# add files
|
|
907
|
+
for file_stat in job_state[-1]:
|
|
908
|
+
file_spec = FileSpec()
|
|
909
|
+
file_spec.__setstate__(file_stat)
|
|
910
|
+
self.addFile(file_spec)
|
|
911
|
+
|
|
912
|
+
def load_from_dict(self, job_dict):
|
|
913
|
+
# Extract job attributes (excluding files)
|
|
914
|
+
job_attrs = []
|
|
915
|
+
for slot in self.__slots__:
|
|
916
|
+
if slot == "Files": # skip files here
|
|
917
|
+
continue
|
|
918
|
+
job_attrs.append(job_dict.get(slot, None))
|
|
919
|
+
|
|
920
|
+
# Initialize with empty file list
|
|
921
|
+
self.__setstate__(job_attrs + [[]])
|
|
922
|
+
|
|
923
|
+
# Add files
|
|
924
|
+
for file_data in job_dict.get("Files", []):
|
|
925
|
+
file_spec = FileSpec()
|
|
926
|
+
file_spec.__setstate__([file_data.get(s, None) for s in file_spec.__slots__])
|
|
927
|
+
self.addFile(file_spec)
|
|
928
|
+
|
|
929
|
+
# set input and output file types
|
|
930
|
+
def set_input_output_file_types(self) -> None:
|
|
931
|
+
"""
|
|
932
|
+
Set input and output file types based on the input and output file names
|
|
933
|
+
"""
|
|
934
|
+
in_types = set()
|
|
935
|
+
out_types = set()
|
|
936
|
+
for tmp_file in self.Files:
|
|
937
|
+
# ignore DBRelease/lib.tgz files
|
|
938
|
+
if tmp_file.dataset.startswith("ddo") or tmp_file.lfn.endswith(".lib.tgz"):
|
|
939
|
+
continue
|
|
940
|
+
# extract type while ignoring user/group/groupXY
|
|
941
|
+
tmp_items = tmp_file.dataset.split(".")
|
|
942
|
+
if (
|
|
943
|
+
len(tmp_items) > 4
|
|
944
|
+
and (not tmp_items[0] in ["", "NULL", "user", "group", "hc_test"])
|
|
945
|
+
and (not tmp_items[0].startswith("group"))
|
|
946
|
+
and not tmp_file.dataset.startswith("panda.um.")
|
|
947
|
+
):
|
|
948
|
+
tmp_type = tmp_items[4]
|
|
949
|
+
else:
|
|
950
|
+
continue
|
|
951
|
+
if tmp_file.type == "input":
|
|
952
|
+
in_types.add(tmp_type)
|
|
953
|
+
elif tmp_file.type == "output":
|
|
954
|
+
out_types.add(tmp_type)
|
|
955
|
+
# set types
|
|
956
|
+
if in_types:
|
|
957
|
+
in_types = sorted(list(in_types))
|
|
958
|
+
self.inputFileType = ",".join(in_types)[: self._limitLength["inputFileType"]]
|
|
959
|
+
if out_types:
|
|
960
|
+
out_types = sorted(list(out_types))
|
|
961
|
+
self.outputFileType = ",".join(out_types)[: self._limitLength["outputFileType"]]
|
|
962
|
+
|
|
963
|
+
# set task queued time
|
|
964
|
+
def set_task_queued_time(self, queued_time):
|
|
965
|
+
"""
|
|
966
|
+
Set task queued time in job metrics. Skip if queued_time is None
|
|
967
|
+
|
|
968
|
+
:param queued_time: task queued time in seconds since epoch
|
|
969
|
+
"""
|
|
970
|
+
if queued_time is None:
|
|
971
|
+
return
|
|
972
|
+
if self.specialHandling in [None, "", "NULL"]:
|
|
973
|
+
items = []
|
|
974
|
+
else:
|
|
975
|
+
items = self.specialHandling.split(",")
|
|
976
|
+
items.append(f"{self._tagForSH['taskQueuedTime']}={queued_time}")
|
|
977
|
+
self.specialHandling = ",".join(items)
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
# utils
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
# check if to push status changes without class instance
|
|
984
|
+
def push_status_changes(special_handling):
|
|
985
|
+
if special_handling is not None:
|
|
986
|
+
items = special_handling.split(",")
|
|
987
|
+
return JobSpec._tagForSH["pushStatusChanges"] in items
|
|
988
|
+
return False
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
# get task queued time
|
|
992
|
+
def get_task_queued_time(special_handling):
|
|
993
|
+
"""
|
|
994
|
+
Get task queued time from job metrics
|
|
995
|
+
|
|
996
|
+
:param special_handling: special handling string
|
|
997
|
+
:return: task queued time. None if unset
|
|
998
|
+
"""
|
|
999
|
+
try:
|
|
1000
|
+
if special_handling is not None:
|
|
1001
|
+
for item in special_handling.split(","):
|
|
1002
|
+
if item.startswith(f"{JobSpec._tagForSH['taskQueuedTime']}="):
|
|
1003
|
+
return datetime.datetime.fromtimestamp(float(item.split("=")[-1]), datetime.timezone.utc)
|
|
1004
|
+
except Exception:
|
|
1005
|
+
pass
|
|
1006
|
+
return None
|