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,1687 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import json
|
|
3
|
+
import math
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from pandaserver.taskbuffer import task_split_rules
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
task specification for JEDI
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JediTaskSpec(object):
|
|
15
|
+
# attributes
|
|
16
|
+
attributes = (
|
|
17
|
+
"jediTaskID",
|
|
18
|
+
"taskName",
|
|
19
|
+
"status",
|
|
20
|
+
"userName",
|
|
21
|
+
"creationDate",
|
|
22
|
+
"modificationTime",
|
|
23
|
+
"startTime",
|
|
24
|
+
"endTime",
|
|
25
|
+
"frozenTime",
|
|
26
|
+
"prodSourceLabel",
|
|
27
|
+
"workingGroup",
|
|
28
|
+
"vo",
|
|
29
|
+
"coreCount",
|
|
30
|
+
"taskType",
|
|
31
|
+
"processingType",
|
|
32
|
+
"taskPriority",
|
|
33
|
+
"currentPriority",
|
|
34
|
+
"architecture",
|
|
35
|
+
"transUses",
|
|
36
|
+
"transHome",
|
|
37
|
+
"transPath",
|
|
38
|
+
"lockedBy",
|
|
39
|
+
"lockedTime",
|
|
40
|
+
"termCondition",
|
|
41
|
+
"splitRule",
|
|
42
|
+
"walltime",
|
|
43
|
+
"walltimeUnit",
|
|
44
|
+
"outDiskCount",
|
|
45
|
+
"outDiskUnit",
|
|
46
|
+
"workDiskCount",
|
|
47
|
+
"workDiskUnit",
|
|
48
|
+
"ramCount",
|
|
49
|
+
"ramUnit",
|
|
50
|
+
"ioIntensity",
|
|
51
|
+
"ioIntensityUnit",
|
|
52
|
+
"workQueue_ID",
|
|
53
|
+
"progress",
|
|
54
|
+
"failureRate",
|
|
55
|
+
"errorDialog",
|
|
56
|
+
"reqID",
|
|
57
|
+
"oldStatus",
|
|
58
|
+
"cloud",
|
|
59
|
+
"site",
|
|
60
|
+
"countryGroup",
|
|
61
|
+
"parent_tid",
|
|
62
|
+
"eventService",
|
|
63
|
+
"ticketID",
|
|
64
|
+
"ticketSystemType",
|
|
65
|
+
"stateChangeTime",
|
|
66
|
+
"superStatus",
|
|
67
|
+
"campaign",
|
|
68
|
+
"mergeRamCount",
|
|
69
|
+
"mergeRamUnit",
|
|
70
|
+
"mergeWalltime",
|
|
71
|
+
"mergeWalltimeUnit",
|
|
72
|
+
"throttledTime",
|
|
73
|
+
"numThrottled",
|
|
74
|
+
"mergeCoreCount",
|
|
75
|
+
"goal",
|
|
76
|
+
"assessmentTime",
|
|
77
|
+
"cpuTime",
|
|
78
|
+
"cpuTimeUnit",
|
|
79
|
+
"cpuEfficiency",
|
|
80
|
+
"baseWalltime",
|
|
81
|
+
"nucleus",
|
|
82
|
+
"baseRamCount",
|
|
83
|
+
"ttcRequested",
|
|
84
|
+
"ttcPredicted",
|
|
85
|
+
"ttcPredictionDate",
|
|
86
|
+
"rescueTime",
|
|
87
|
+
"requestType",
|
|
88
|
+
"gshare",
|
|
89
|
+
"resource_type",
|
|
90
|
+
"useJumbo",
|
|
91
|
+
"diskIO",
|
|
92
|
+
"diskIOUnit",
|
|
93
|
+
"memory_leak_core",
|
|
94
|
+
"memory_leak_x2",
|
|
95
|
+
"attemptNr",
|
|
96
|
+
"container_name",
|
|
97
|
+
"framework",
|
|
98
|
+
"activatedTime",
|
|
99
|
+
"queuedTime",
|
|
100
|
+
"actionTime",
|
|
101
|
+
)
|
|
102
|
+
# attributes which have 0 by default
|
|
103
|
+
_zeroAttrs = ()
|
|
104
|
+
# attributes to force update
|
|
105
|
+
_forceUpdateAttrs = ("lockedBy", "lockedTime")
|
|
106
|
+
# mapping between sequence and attr
|
|
107
|
+
_seqAttrMap = {}
|
|
108
|
+
# limit length
|
|
109
|
+
_limitLength = {"errorDialog": 510}
|
|
110
|
+
# attribute length
|
|
111
|
+
_attrLength = {"workingGroup": 32}
|
|
112
|
+
|
|
113
|
+
# tokens for split rule
|
|
114
|
+
splitRuleToken = task_split_rules.split_rule_dict
|
|
115
|
+
|
|
116
|
+
# enum for preprocessing
|
|
117
|
+
enum_toPreProcess = "1"
|
|
118
|
+
enum_preProcessed = "2"
|
|
119
|
+
enum_postPProcess = "3"
|
|
120
|
+
# enum for limited sites
|
|
121
|
+
enum_limitedSites = {"1": "inc", "2": "exc", "3": "incexc"}
|
|
122
|
+
# enum for scout
|
|
123
|
+
enum_noScout = "1"
|
|
124
|
+
enum_useScout = "2"
|
|
125
|
+
enum_postScout = "3"
|
|
126
|
+
# enum for dataset registration
|
|
127
|
+
enum_toRegisterDS = "1"
|
|
128
|
+
enum_registeredDS = "2"
|
|
129
|
+
# enum for IP connectivity
|
|
130
|
+
enum_ipConnectivity = {"1": "full", "2": "http"}
|
|
131
|
+
# enum for IP stack
|
|
132
|
+
enum_ipStack = {"4": "IPv4", "6": "IPv6"}
|
|
133
|
+
# enum for alternative stage-out
|
|
134
|
+
enum_altStageOut = {"1": "on", "2": "off", "3": "force"}
|
|
135
|
+
# enum for local direct access
|
|
136
|
+
enum_inputLAN = {"1": "use", "2": "only"}
|
|
137
|
+
# world cloud name
|
|
138
|
+
worldCloudName = "WORLD"
|
|
139
|
+
|
|
140
|
+
# enum for contents feeder
|
|
141
|
+
class FirstContentsFeed(enum.Enum):
|
|
142
|
+
TRUE = "1"
|
|
143
|
+
FALSE = "0"
|
|
144
|
+
|
|
145
|
+
# enum for useJumbo
|
|
146
|
+
enum_useJumbo = {"waiting": "W", "running": "R", "pending": "P", "lack": "L", "disabled": "D"}
|
|
147
|
+
|
|
148
|
+
# enum for input prestaging
|
|
149
|
+
enum_inputPreStaging = {"use": "1", "notUse": "0"}
|
|
150
|
+
|
|
151
|
+
# enum for full chain
|
|
152
|
+
class FullChain(str, enum.Enum):
|
|
153
|
+
Only = "1"
|
|
154
|
+
Require = "2"
|
|
155
|
+
Capable = "3"
|
|
156
|
+
|
|
157
|
+
# enum for order input by
|
|
158
|
+
class OrderInputBy(str, enum.Enum):
|
|
159
|
+
eventsAlignment = "1"
|
|
160
|
+
|
|
161
|
+
# constructor
|
|
162
|
+
def __init__(self):
|
|
163
|
+
# install attributes
|
|
164
|
+
for attr in self.attributes:
|
|
165
|
+
if attr in self._zeroAttrs:
|
|
166
|
+
object.__setattr__(self, attr, 0)
|
|
167
|
+
else:
|
|
168
|
+
object.__setattr__(self, attr, None)
|
|
169
|
+
# map of changed attributes
|
|
170
|
+
object.__setattr__(self, "_changedAttrs", {})
|
|
171
|
+
# template to generate job parameters
|
|
172
|
+
object.__setattr__(self, "jobParamsTemplate", "")
|
|
173
|
+
# associated datasets
|
|
174
|
+
object.__setattr__(self, "datasetSpecList", [])
|
|
175
|
+
# original error dialog
|
|
176
|
+
object.__setattr__(self, "origErrorDialog", None)
|
|
177
|
+
# original user name
|
|
178
|
+
object.__setattr__(self, "origUserName", None)
|
|
179
|
+
|
|
180
|
+
# override __setattr__ to collect the changed attributes
|
|
181
|
+
def __setattr__(self, name, value):
|
|
182
|
+
oldVal = getattr(self, name)
|
|
183
|
+
if name in self._limitLength and value is not None:
|
|
184
|
+
# keep original dialog
|
|
185
|
+
if name == "errorDialog":
|
|
186
|
+
object.__setattr__(self, "origErrorDialog", value)
|
|
187
|
+
value = value[: self._limitLength[name]]
|
|
188
|
+
object.__setattr__(self, name, value)
|
|
189
|
+
newVal = getattr(self, name)
|
|
190
|
+
# collect changed attributes
|
|
191
|
+
if oldVal != newVal or name in self._forceUpdateAttrs:
|
|
192
|
+
self._changedAttrs[name] = value
|
|
193
|
+
|
|
194
|
+
# copy old attributes
|
|
195
|
+
def copyAttributes(self, oldTaskSpec):
|
|
196
|
+
for attr in self.attributes + ("jobParamsTemplate",):
|
|
197
|
+
if "Time" in attr:
|
|
198
|
+
continue
|
|
199
|
+
if "Date" in attr:
|
|
200
|
+
continue
|
|
201
|
+
if attr in ["progress", "failureRate", "errorDialog", "status", "oldStatus", "lockedBy"]:
|
|
202
|
+
continue
|
|
203
|
+
self.__setattr__(attr, getattr(oldTaskSpec, attr))
|
|
204
|
+
|
|
205
|
+
# reset changed attribute list
|
|
206
|
+
def resetChangedList(self):
|
|
207
|
+
object.__setattr__(self, "_changedAttrs", {})
|
|
208
|
+
|
|
209
|
+
# reset changed attribute
|
|
210
|
+
def resetChangedAttr(self, name):
|
|
211
|
+
try:
|
|
212
|
+
del self._changedAttrs[name]
|
|
213
|
+
except Exception:
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
# reserve old attributes
|
|
217
|
+
def reserve_old_attributes(self):
|
|
218
|
+
for attName in ["ramCount", "walltime", "cpuTime", "startTime", "cpuTimeUnit", "outDiskCount", "workDiskCount", "ioIntensity", "diskIO"]:
|
|
219
|
+
self.resetChangedAttr(attName)
|
|
220
|
+
|
|
221
|
+
# force update
|
|
222
|
+
def forceUpdate(self, name):
|
|
223
|
+
if name in self.attributes:
|
|
224
|
+
self._changedAttrs[name] = getattr(self, name)
|
|
225
|
+
|
|
226
|
+
# return map of values
|
|
227
|
+
def valuesMap(self, useSeq=False, onlyChanged=False):
|
|
228
|
+
ret = {}
|
|
229
|
+
for attr in self.attributes:
|
|
230
|
+
# use sequence
|
|
231
|
+
if useSeq and attr in self._seqAttrMap:
|
|
232
|
+
continue
|
|
233
|
+
# only changed attributes
|
|
234
|
+
if onlyChanged:
|
|
235
|
+
if attr not in self._changedAttrs:
|
|
236
|
+
continue
|
|
237
|
+
val = getattr(self, attr)
|
|
238
|
+
if val is None:
|
|
239
|
+
if attr in self._zeroAttrs:
|
|
240
|
+
val = 0
|
|
241
|
+
else:
|
|
242
|
+
val = None
|
|
243
|
+
# truncate too long values
|
|
244
|
+
if attr in self._limitLength:
|
|
245
|
+
if val is not None:
|
|
246
|
+
val = val[: self._limitLength[attr]]
|
|
247
|
+
ret[f":{attr}"] = val
|
|
248
|
+
return ret
|
|
249
|
+
|
|
250
|
+
# pack tuple into TaskSpec
|
|
251
|
+
def pack(self, values):
|
|
252
|
+
for i in range(len(self.attributes)):
|
|
253
|
+
attr = self.attributes[i]
|
|
254
|
+
val = values[i]
|
|
255
|
+
object.__setattr__(self, attr, val)
|
|
256
|
+
|
|
257
|
+
# return column names for INSERT
|
|
258
|
+
def columnNames(cls, prefix=None):
|
|
259
|
+
ret = ""
|
|
260
|
+
for attr in cls.attributes:
|
|
261
|
+
if prefix is not None:
|
|
262
|
+
ret += f"{prefix}."
|
|
263
|
+
ret += f"{attr},"
|
|
264
|
+
ret = ret[:-1]
|
|
265
|
+
return ret
|
|
266
|
+
|
|
267
|
+
columnNames = classmethod(columnNames)
|
|
268
|
+
|
|
269
|
+
# return expression of bind variables for INSERT
|
|
270
|
+
def bindValuesExpression(cls, useSeq=True):
|
|
271
|
+
ret = "VALUES("
|
|
272
|
+
for attr in cls.attributes:
|
|
273
|
+
if useSeq and attr in cls._seqAttrMap:
|
|
274
|
+
ret += f"{cls._seqAttrMap[attr]},"
|
|
275
|
+
else:
|
|
276
|
+
ret += f":{attr},"
|
|
277
|
+
ret = ret[:-1]
|
|
278
|
+
ret += ")"
|
|
279
|
+
return ret
|
|
280
|
+
|
|
281
|
+
bindValuesExpression = classmethod(bindValuesExpression)
|
|
282
|
+
|
|
283
|
+
# return an expression of bind variables for UPDATE to update only changed attributes
|
|
284
|
+
def bindUpdateChangesExpression(self):
|
|
285
|
+
ret = ""
|
|
286
|
+
for attr in self.attributes:
|
|
287
|
+
if attr in self._changedAttrs:
|
|
288
|
+
ret += f"{attr}=:{attr},"
|
|
289
|
+
ret = ret[:-1]
|
|
290
|
+
ret += " "
|
|
291
|
+
return ret
|
|
292
|
+
|
|
293
|
+
# check split rule
|
|
294
|
+
def check_split_rule(self, key):
|
|
295
|
+
if self.splitRule is not None:
|
|
296
|
+
if re.search(self.splitRuleToken[key] + r"=(\d+)", self.splitRule):
|
|
297
|
+
return True
|
|
298
|
+
return False
|
|
299
|
+
|
|
300
|
+
# get the max size per job if defined
|
|
301
|
+
def getMaxSizePerJob(self):
|
|
302
|
+
if self.splitRule is not None:
|
|
303
|
+
tmpMatch = re.search(self.splitRuleToken["nGBPerJob"] + "=(\d+)", self.splitRule)
|
|
304
|
+
if tmpMatch is not None:
|
|
305
|
+
nGBPerJob = int(tmpMatch.group(1)) * 1024 * 1024 * 1024
|
|
306
|
+
return nGBPerJob
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
# remove nGBPerJob
|
|
310
|
+
def removeMaxSizePerJob(self):
|
|
311
|
+
self.removeSplitRule(self.splitRuleToken["nGBPerJob"])
|
|
312
|
+
|
|
313
|
+
# get the max size per merge job if defined
|
|
314
|
+
def getMaxSizePerMergeJob(self):
|
|
315
|
+
if self.splitRule is not None:
|
|
316
|
+
tmpMatch = re.search(self.splitRuleToken["nGBPerMergeJob"] + "=(\d+)", self.splitRule)
|
|
317
|
+
if tmpMatch is not None:
|
|
318
|
+
nGBPerJob = int(tmpMatch.group(1)) * 1024 * 1024 * 1024
|
|
319
|
+
return nGBPerJob
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
# get the maxnumber of files per job if defined
|
|
323
|
+
def getMaxNumFilesPerJob(self):
|
|
324
|
+
if self.splitRule is not None:
|
|
325
|
+
tmpMatch = re.search(self.splitRuleToken["nMaxFilesPerJob"] + "=(\d+)", self.splitRule)
|
|
326
|
+
if tmpMatch is not None:
|
|
327
|
+
return int(tmpMatch.group(1))
|
|
328
|
+
return None
|
|
329
|
+
|
|
330
|
+
# set MaxNumFilesPerJob
|
|
331
|
+
def setMaxNumFilesPerJob(self, value):
|
|
332
|
+
self.setSplitRule("nMaxFilesPerJob", value)
|
|
333
|
+
|
|
334
|
+
# get the maxnumber of files per merge job if defined
|
|
335
|
+
def getMaxNumFilesPerMergeJob(self):
|
|
336
|
+
if self.splitRule is not None:
|
|
337
|
+
tmpMatch = re.search(self.splitRuleToken["nMaxFilesPerMergeJob"] + "=(\d+)", self.splitRule)
|
|
338
|
+
if tmpMatch is not None:
|
|
339
|
+
return int(tmpMatch.group(1))
|
|
340
|
+
return 50
|
|
341
|
+
|
|
342
|
+
# get the number of events per merge job if defined
|
|
343
|
+
def getNumEventsPerMergeJob(self):
|
|
344
|
+
if self.splitRule is not None:
|
|
345
|
+
tmpMatch = re.search(self.splitRuleToken["nEventsPerMergeJob"] + "=(\d+)", self.splitRule)
|
|
346
|
+
if tmpMatch is not None:
|
|
347
|
+
return int(tmpMatch.group(1))
|
|
348
|
+
return None
|
|
349
|
+
|
|
350
|
+
# check if using jumbo
|
|
351
|
+
def usingJumboJobs(self):
|
|
352
|
+
if self.useJumbo in self.enum_useJumbo.values() and self.useJumbo != self.enum_useJumbo["disabled"]:
|
|
353
|
+
return True
|
|
354
|
+
return False
|
|
355
|
+
|
|
356
|
+
# get the number of jumbo jobs if defined
|
|
357
|
+
def getNumJumboJobs(self):
|
|
358
|
+
if self.usingJumboJobs() and self.splitRule is not None:
|
|
359
|
+
tmpMatch = re.search(self.splitRuleToken["nJumboJobs"] + "=(\d+)", self.splitRule)
|
|
360
|
+
if tmpMatch is not None:
|
|
361
|
+
return int(tmpMatch.group(1))
|
|
362
|
+
return None
|
|
363
|
+
|
|
364
|
+
# get the max number of jumbo jobs per site if defined
|
|
365
|
+
def getMaxJumboPerSite(self):
|
|
366
|
+
if self.usingJumboJobs() and self.splitRule is not None:
|
|
367
|
+
tmpMatch = re.search(self.splitRuleToken["maxJumboPerSite"] + "=(\d+)", self.splitRule)
|
|
368
|
+
if tmpMatch is not None:
|
|
369
|
+
return int(tmpMatch.group(1))
|
|
370
|
+
return 1
|
|
371
|
+
|
|
372
|
+
# get the number of sites per job
|
|
373
|
+
def getNumSitesPerJob(self):
|
|
374
|
+
if not self.useEventService():
|
|
375
|
+
return 1
|
|
376
|
+
if self.splitRule is not None:
|
|
377
|
+
tmpMatch = re.search(self.splitRuleToken["nSitesPerJob"] + "=(\d+)", self.splitRule)
|
|
378
|
+
if tmpMatch is not None:
|
|
379
|
+
return int(tmpMatch.group(1))
|
|
380
|
+
return 1
|
|
381
|
+
|
|
382
|
+
# get the number of files per job if defined
|
|
383
|
+
def getNumFilesPerJob(self):
|
|
384
|
+
if self.splitRule is not None:
|
|
385
|
+
tmpMatch = re.search(self.splitRuleToken["nFilesPerJob"] + "=(\d+)", self.splitRule)
|
|
386
|
+
if tmpMatch is not None:
|
|
387
|
+
n = int(tmpMatch.group(1))
|
|
388
|
+
if self.dynamicNumEvents():
|
|
389
|
+
inn = self.get_num_events_per_input()
|
|
390
|
+
dyn = self.get_min_granularity()
|
|
391
|
+
if inn and dyn and inn > dyn:
|
|
392
|
+
n *= inn // dyn
|
|
393
|
+
return n
|
|
394
|
+
return None
|
|
395
|
+
|
|
396
|
+
# remove nFilesPerJob
|
|
397
|
+
def removeNumFilesPerJob(self):
|
|
398
|
+
self.removeSplitRule(self.splitRuleToken["nFilesPerJob"])
|
|
399
|
+
|
|
400
|
+
# get the number of files per merge job if defined
|
|
401
|
+
def getNumFilesPerMergeJob(self):
|
|
402
|
+
if self.splitRule is not None:
|
|
403
|
+
tmpMatch = re.search(self.splitRuleToken["nFilesPerMergeJob"] + "=(\d+)", self.splitRule)
|
|
404
|
+
if tmpMatch is not None:
|
|
405
|
+
return int(tmpMatch.group(1))
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
# get the number of events per job if defined
|
|
409
|
+
def getNumEventsPerJob(self):
|
|
410
|
+
if self.splitRule is not None:
|
|
411
|
+
tmpMatch = re.search(self.splitRuleToken["nEventsPerJob"] + "=(\d+)", self.splitRule)
|
|
412
|
+
if tmpMatch is not None:
|
|
413
|
+
return int(tmpMatch.group(1))
|
|
414
|
+
return None
|
|
415
|
+
|
|
416
|
+
# get offset for random seed
|
|
417
|
+
def getRndmSeedOffset(self):
|
|
418
|
+
if self.splitRule is not None:
|
|
419
|
+
tmpMatch = re.search(self.splitRuleToken["randomSeed"] + "=(\d+)", self.splitRule)
|
|
420
|
+
if tmpMatch is not None:
|
|
421
|
+
return int(tmpMatch.group(1))
|
|
422
|
+
return 0
|
|
423
|
+
|
|
424
|
+
# get offset for first event
|
|
425
|
+
def getFirstEventOffset(self):
|
|
426
|
+
if self.splitRule is not None:
|
|
427
|
+
tmpMatch = re.search(self.splitRuleToken["firstEvent"] + "=(\d+)", self.splitRule)
|
|
428
|
+
if tmpMatch is not None:
|
|
429
|
+
return int(tmpMatch.group(1))
|
|
430
|
+
return 0
|
|
431
|
+
|
|
432
|
+
# grouping with boundaryID
|
|
433
|
+
def useGroupWithBoundaryID(self):
|
|
434
|
+
if self.splitRule is not None:
|
|
435
|
+
tmpMatch = re.search(self.splitRuleToken["groupBoundaryID"] + "=(\d+)", self.splitRule)
|
|
436
|
+
if tmpMatch is not None:
|
|
437
|
+
gbID = int(tmpMatch.group(1))
|
|
438
|
+
# 1 : input - can split, output - free
|
|
439
|
+
# 2 : input - can split, output - mapped with provenanceID
|
|
440
|
+
# 3 : input - cannot split, output - free
|
|
441
|
+
# 4 : input - cannot split, output - mapped with provenanceID
|
|
442
|
+
#
|
|
443
|
+
# * rule for master
|
|
444
|
+
# 1 : can split. one boundayID per sub chunk
|
|
445
|
+
# 2 : cannot split. one boundayID per sub chunk
|
|
446
|
+
# 3 : cannot split. multiple boundayIDs per sub chunk
|
|
447
|
+
#
|
|
448
|
+
# * rule for secodary
|
|
449
|
+
# 1 : must have same boundayID. cannot split
|
|
450
|
+
#
|
|
451
|
+
retMap = {}
|
|
452
|
+
if gbID in [1, 2]:
|
|
453
|
+
retMap["inSplit"] = 1
|
|
454
|
+
else:
|
|
455
|
+
retMap["inSplit"] = 2
|
|
456
|
+
if gbID in [1, 3]:
|
|
457
|
+
retMap["outMap"] = False
|
|
458
|
+
else:
|
|
459
|
+
retMap["outMap"] = True
|
|
460
|
+
retMap["secSplit"] = None
|
|
461
|
+
return retMap
|
|
462
|
+
return None
|
|
463
|
+
|
|
464
|
+
# use build
|
|
465
|
+
def useBuild(self):
|
|
466
|
+
return self.check_split_rule("useBuild")
|
|
467
|
+
|
|
468
|
+
# use sjob cloning
|
|
469
|
+
def useJobCloning(self):
|
|
470
|
+
return self.check_split_rule("useJobCloning")
|
|
471
|
+
|
|
472
|
+
# get job cloning type
|
|
473
|
+
def getJobCloningType(self):
|
|
474
|
+
if self.splitRule is not None:
|
|
475
|
+
tmpMatch = re.search(self.splitRuleToken["useJobCloning"] + "=(\d+)", self.splitRule)
|
|
476
|
+
if tmpMatch is not None:
|
|
477
|
+
return tmpMatch.group(1)
|
|
478
|
+
return ""
|
|
479
|
+
|
|
480
|
+
# reuse secondary on demand
|
|
481
|
+
def reuseSecOnDemand(self):
|
|
482
|
+
return self.check_split_rule("reuseSecOnDemand")
|
|
483
|
+
|
|
484
|
+
# not wait for completion of parent
|
|
485
|
+
def noWaitParent(self):
|
|
486
|
+
return self.check_split_rule("noWaitParent")
|
|
487
|
+
|
|
488
|
+
# check splitRule if not wait for completion of parent
|
|
489
|
+
def noWaitParentSL(cls, splitRule):
|
|
490
|
+
if splitRule is not None:
|
|
491
|
+
tmpMatch = re.search(cls.splitRuleToken["noWaitParent"] + "=(\d+)", splitRule)
|
|
492
|
+
if tmpMatch is not None:
|
|
493
|
+
return True
|
|
494
|
+
return False
|
|
495
|
+
|
|
496
|
+
noWaitParentSL = classmethod(noWaitParentSL)
|
|
497
|
+
|
|
498
|
+
# use only limited sites
|
|
499
|
+
def useLimitedSites(self):
|
|
500
|
+
return self.check_split_rule("limitedSites")
|
|
501
|
+
|
|
502
|
+
# set limited sites
|
|
503
|
+
def setLimitedSites(self, policy):
|
|
504
|
+
tag = None
|
|
505
|
+
for tmpIdx, tmpPolicy in self.enum_limitedSites.items():
|
|
506
|
+
if policy == tmpPolicy:
|
|
507
|
+
tag = tmpIdx
|
|
508
|
+
break
|
|
509
|
+
# not found
|
|
510
|
+
if tag is None:
|
|
511
|
+
return
|
|
512
|
+
# set
|
|
513
|
+
if self.splitRule is None:
|
|
514
|
+
# new
|
|
515
|
+
self.splitRule = self.splitRuleToken["limitedSites"] + "=" + tag
|
|
516
|
+
else:
|
|
517
|
+
tmpMatch = re.search(self.splitRuleToken["limitedSites"] + "=(\d+)", self.splitRule)
|
|
518
|
+
if tmpMatch is None:
|
|
519
|
+
# append
|
|
520
|
+
self.splitRule += "," + self.splitRuleToken["limitedSites"] + "=" + tag
|
|
521
|
+
else:
|
|
522
|
+
# replace
|
|
523
|
+
self.splitRule = re.sub(self.splitRuleToken["limitedSites"] + "=(\d+)", self.splitRuleToken["limitedSites"] + "=" + tag, self.splitRule)
|
|
524
|
+
|
|
525
|
+
# use local IO
|
|
526
|
+
def useLocalIO(self):
|
|
527
|
+
if self.splitRule is not None:
|
|
528
|
+
tmpMatch = re.search(self.splitRuleToken["useLocalIO"] + "=(\d+)", self.splitRule)
|
|
529
|
+
if tmpMatch is not None and int(tmpMatch.group(1)):
|
|
530
|
+
return True
|
|
531
|
+
return False
|
|
532
|
+
|
|
533
|
+
# use Event Service
|
|
534
|
+
def useEventService(self, siteSpec=None):
|
|
535
|
+
if self.eventService in [1, 2]:
|
|
536
|
+
# check site if ES is disabled
|
|
537
|
+
if self.switchEStoNormal() and siteSpec is not None and siteSpec.getJobSeed() in ["all"]:
|
|
538
|
+
return False
|
|
539
|
+
return True
|
|
540
|
+
return False
|
|
541
|
+
|
|
542
|
+
# get the number of events per worker for Event Service
|
|
543
|
+
def getNumEventsPerWorker(self):
|
|
544
|
+
if self.splitRule is not None:
|
|
545
|
+
tmpMatch = re.search(self.splitRuleToken["nEventsPerWorker"] + "=(\d+)", self.splitRule)
|
|
546
|
+
if tmpMatch is not None:
|
|
547
|
+
return int(tmpMatch.group(1))
|
|
548
|
+
return None
|
|
549
|
+
|
|
550
|
+
# get the number of event service consumers
|
|
551
|
+
def getNumEventServiceConsumer(self):
|
|
552
|
+
if not self.useEventService():
|
|
553
|
+
return None
|
|
554
|
+
if self.splitRule is not None:
|
|
555
|
+
tmpMatch = re.search(self.splitRuleToken["nEsConsumers"] + "=(\d+)", self.splitRule)
|
|
556
|
+
if tmpMatch is not None:
|
|
557
|
+
return int(tmpMatch.group(1))
|
|
558
|
+
return None
|
|
559
|
+
|
|
560
|
+
# disable automatic retry
|
|
561
|
+
def disableAutoRetry(self):
|
|
562
|
+
return self.check_split_rule("disableAutoRetry")
|
|
563
|
+
|
|
564
|
+
# disable reassign
|
|
565
|
+
def disableReassign(self):
|
|
566
|
+
return self.check_split_rule("disableReassign")
|
|
567
|
+
|
|
568
|
+
# allow empty input
|
|
569
|
+
def allowEmptyInput(self):
|
|
570
|
+
return self.check_split_rule("allowEmptyInput")
|
|
571
|
+
|
|
572
|
+
# use PFN list
|
|
573
|
+
def useListPFN(self):
|
|
574
|
+
return self.check_split_rule("pfnList")
|
|
575
|
+
|
|
576
|
+
# set preprocessing
|
|
577
|
+
def setPrePro(self):
|
|
578
|
+
if self.splitRule is None:
|
|
579
|
+
# new
|
|
580
|
+
self.splitRule = self.splitRuleToken["usePrePro"] + "=" + self.enum_toPreProcess
|
|
581
|
+
else:
|
|
582
|
+
# append
|
|
583
|
+
self.splitRule += "," + self.splitRuleToken["usePrePro"] + "=" + self.enum_toPreProcess
|
|
584
|
+
|
|
585
|
+
# use preprocessing
|
|
586
|
+
def usePrePro(self):
|
|
587
|
+
if self.splitRule is not None:
|
|
588
|
+
tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
|
|
589
|
+
if tmpMatch is not None and tmpMatch.group(1) == self.enum_toPreProcess:
|
|
590
|
+
return True
|
|
591
|
+
return False
|
|
592
|
+
|
|
593
|
+
# set preprocessed
|
|
594
|
+
def setPreProcessed(self):
|
|
595
|
+
if self.splitRule is None:
|
|
596
|
+
# new
|
|
597
|
+
self.splitRule = self.splitRuleToken["usePrePro"] + "=" + self.enum_preProcessed
|
|
598
|
+
else:
|
|
599
|
+
tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
|
|
600
|
+
if tmpMatch is None:
|
|
601
|
+
# append
|
|
602
|
+
self.splitRule += "," + self.splitRuleToken["usePrePro"] + "=" + self.enum_preProcessed
|
|
603
|
+
else:
|
|
604
|
+
# replace
|
|
605
|
+
self.splitRule = re.sub(
|
|
606
|
+
self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRuleToken["usePrePro"] + "=" + self.enum_preProcessed, self.splitRule
|
|
607
|
+
)
|
|
608
|
+
return
|
|
609
|
+
|
|
610
|
+
# check preprocessed
|
|
611
|
+
def checkPreProcessed(self):
|
|
612
|
+
if self.splitRule is not None:
|
|
613
|
+
tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
|
|
614
|
+
if tmpMatch is not None and tmpMatch.group(1) == self.enum_preProcessed:
|
|
615
|
+
return True
|
|
616
|
+
return False
|
|
617
|
+
|
|
618
|
+
# set post preprocess
|
|
619
|
+
def setPostPreProcess(self):
|
|
620
|
+
if self.splitRule is None:
|
|
621
|
+
# new
|
|
622
|
+
self.splitRule = self.splitRuleToken["usePrePro"] + "=" + self.enum_postPProcess
|
|
623
|
+
else:
|
|
624
|
+
tmpMatch = re.search(self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRule)
|
|
625
|
+
if tmpMatch is None:
|
|
626
|
+
# append
|
|
627
|
+
self.splitRule += "," + self.splitRuleToken["usePrePro"] + "=" + self.enum_postPProcess
|
|
628
|
+
else:
|
|
629
|
+
# replace
|
|
630
|
+
self.splitRule = re.sub(
|
|
631
|
+
self.splitRuleToken["usePrePro"] + "=(\d+)", self.splitRuleToken["usePrePro"] + "=" + self.enum_postPProcess, self.splitRule
|
|
632
|
+
)
|
|
633
|
+
return
|
|
634
|
+
|
|
635
|
+
# instantiate template datasets
|
|
636
|
+
def instantiateTmpl(self):
|
|
637
|
+
return self.check_split_rule("instantiateTmpl")
|
|
638
|
+
|
|
639
|
+
# instantiate template datasets at site
|
|
640
|
+
def instantiateTmplSite(self):
|
|
641
|
+
return self.check_split_rule("instantiateTmplSite")
|
|
642
|
+
|
|
643
|
+
# merge output
|
|
644
|
+
def mergeOutput(self):
|
|
645
|
+
return self.check_split_rule("mergeOutput")
|
|
646
|
+
|
|
647
|
+
# use random seed
|
|
648
|
+
def useRandomSeed(self):
|
|
649
|
+
return self.check_split_rule("randomSeed")
|
|
650
|
+
|
|
651
|
+
# get the size of workDisk in bytes
|
|
652
|
+
def getWorkDiskSize(self):
|
|
653
|
+
safetyMargin = 300 * 1024 * 1024
|
|
654
|
+
tmpSize = self.workDiskCount
|
|
655
|
+
if tmpSize is None:
|
|
656
|
+
return 0
|
|
657
|
+
if self.workDiskUnit == "GB":
|
|
658
|
+
tmpSize = tmpSize * 1024 * 1024 * 1024
|
|
659
|
+
elif self.workDiskUnit == "MB":
|
|
660
|
+
tmpSize = tmpSize * 1024 * 1024
|
|
661
|
+
elif self.workDiskUnit == "kB":
|
|
662
|
+
tmpSize = tmpSize * 1024
|
|
663
|
+
tmpSize += safetyMargin
|
|
664
|
+
return tmpSize
|
|
665
|
+
|
|
666
|
+
# get the size of outDisk in bytes
|
|
667
|
+
def getOutDiskSize(self):
|
|
668
|
+
tmpSize = self.outDiskCount
|
|
669
|
+
if tmpSize is None or tmpSize < 0:
|
|
670
|
+
return 0
|
|
671
|
+
if self.outDiskUnit is not None:
|
|
672
|
+
if self.outDiskUnit.startswith("GB"):
|
|
673
|
+
tmpSize = tmpSize * 1024 * 1024 * 1024
|
|
674
|
+
elif self.outDiskUnit.startswith("MB"):
|
|
675
|
+
tmpSize = tmpSize * 1024 * 1024
|
|
676
|
+
elif self.outDiskUnit.startswith("kB"):
|
|
677
|
+
tmpSize = tmpSize * 1024
|
|
678
|
+
return tmpSize
|
|
679
|
+
|
|
680
|
+
# output scales with the number of events
|
|
681
|
+
def outputScaleWithEvents(self):
|
|
682
|
+
if self.outDiskUnit is not None and "PerEvent" in self.outDiskUnit:
|
|
683
|
+
return True
|
|
684
|
+
return False
|
|
685
|
+
|
|
686
|
+
# return list of status to update contents
|
|
687
|
+
def statusToUpdateContents(cls):
|
|
688
|
+
return ["defined"]
|
|
689
|
+
|
|
690
|
+
statusToUpdateContents = classmethod(statusToUpdateContents)
|
|
691
|
+
|
|
692
|
+
# set task status on hold
|
|
693
|
+
def setOnHold(self):
|
|
694
|
+
# change status
|
|
695
|
+
if self.status in ["ready", "running", "merging", "scouting", "defined", "topreprocess", "preprocessing", "registered", "prepared", "rerefine"]:
|
|
696
|
+
self.oldStatus = self.status
|
|
697
|
+
self.status = "pending"
|
|
698
|
+
|
|
699
|
+
# return list of status to reject external changes
|
|
700
|
+
def statusToRejectExtChange(cls):
|
|
701
|
+
return ["finished", "done", "prepared", "broken", "tobroken", "aborted", "toabort", "aborting", "failed", "passed"]
|
|
702
|
+
|
|
703
|
+
statusToRejectExtChange = classmethod(statusToRejectExtChange)
|
|
704
|
+
|
|
705
|
+
# return list of status for retry
|
|
706
|
+
def statusToRetry(cls):
|
|
707
|
+
return ["finished", "failed", "aborted", "exhausted"]
|
|
708
|
+
|
|
709
|
+
statusToRetry = classmethod(statusToRetry)
|
|
710
|
+
|
|
711
|
+
# return list of status for incexec
|
|
712
|
+
def statusToIncexec(cls):
|
|
713
|
+
return ["done"] + cls.statusToRetry()
|
|
714
|
+
|
|
715
|
+
statusToIncexec = classmethod(statusToIncexec)
|
|
716
|
+
|
|
717
|
+
# return list of status for reassign
|
|
718
|
+
def statusToReassign(cls):
|
|
719
|
+
return ["registered", "defined", "ready", "running", "scouting", "scouted", "pending", "assigning", "exhausted"]
|
|
720
|
+
|
|
721
|
+
statusToReassign = classmethod(statusToReassign)
|
|
722
|
+
|
|
723
|
+
# return list of status for Job Generator
|
|
724
|
+
def statusForJobGenerator(cls):
|
|
725
|
+
return ["ready", "running", "scouting", "topreprocess", "preprocessing"]
|
|
726
|
+
|
|
727
|
+
statusForJobGenerator = classmethod(statusForJobGenerator)
|
|
728
|
+
|
|
729
|
+
# return list of status to not pause
|
|
730
|
+
def statusNotToPause(cls):
|
|
731
|
+
return ["finished", "failed", "done", "aborted", "broken", "paused"]
|
|
732
|
+
|
|
733
|
+
statusNotToPause = classmethod(statusNotToPause)
|
|
734
|
+
|
|
735
|
+
# return mapping of command and status
|
|
736
|
+
def commandStatusMap(cls):
|
|
737
|
+
return {
|
|
738
|
+
"kill": {"doing": "aborting", "done": "toabort"},
|
|
739
|
+
"finish": {"doing": "finishing", "done": "passed"},
|
|
740
|
+
"retry": {"doing": "toretry", "done": "ready"},
|
|
741
|
+
"incexec": {"doing": "toincexec", "done": "rerefine"},
|
|
742
|
+
"reassign": {"doing": "toreassign", "done": "reassigning"},
|
|
743
|
+
"pause": {"doing": "paused", "done": "dummy"},
|
|
744
|
+
"resume": {"doing": "dummy", "done": "dummy"},
|
|
745
|
+
"avalanche": {"doing": "dummy", "done": "dummy"},
|
|
746
|
+
"release": {"doing": "dummy", "done": "dummy"},
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
commandStatusMap = classmethod(commandStatusMap)
|
|
750
|
+
|
|
751
|
+
# qualifiers for retry command
|
|
752
|
+
def get_retry_command_qualifiers(
|
|
753
|
+
cls,
|
|
754
|
+
no_child_retry: bool = False,
|
|
755
|
+
discard_events: bool = False,
|
|
756
|
+
disable_staging_mode: bool = False,
|
|
757
|
+
keep_gshare_priority: bool = False,
|
|
758
|
+
ignore_hard_exhausted: bool = False,
|
|
759
|
+
) -> list:
|
|
760
|
+
"""
|
|
761
|
+
Get the list of qualifiers for the retry command.
|
|
762
|
+
:param no_child_retry: If True, retry will not be attempted on child tasks.
|
|
763
|
+
:param discard_events: If True, events will be discarded.
|
|
764
|
+
:param disable_staging_mode: If True, staging mode will be disabled.
|
|
765
|
+
:param keep_gshare_priority: If True, current gshare and priority will be kept.
|
|
766
|
+
:param ignore_hard_exhausted: If True, the limits for hard exhausted will be ignored.
|
|
767
|
+
:return: A list of qualifiers.
|
|
768
|
+
"""
|
|
769
|
+
qualifiers = []
|
|
770
|
+
if no_child_retry:
|
|
771
|
+
qualifiers.append("sole")
|
|
772
|
+
if discard_events:
|
|
773
|
+
qualifiers.append("discard")
|
|
774
|
+
if disable_staging_mode:
|
|
775
|
+
qualifiers.append("staged")
|
|
776
|
+
if keep_gshare_priority:
|
|
777
|
+
qualifiers.append("keep")
|
|
778
|
+
if ignore_hard_exhausted:
|
|
779
|
+
qualifiers.append("transcend")
|
|
780
|
+
return qualifiers
|
|
781
|
+
|
|
782
|
+
get_retry_command_qualifiers = classmethod(get_retry_command_qualifiers)
|
|
783
|
+
|
|
784
|
+
# set error dialog
|
|
785
|
+
def setErrDiag(self, diag, append=False, prepend=False):
|
|
786
|
+
# check if message can be encoded with UTF-8
|
|
787
|
+
if diag:
|
|
788
|
+
try:
|
|
789
|
+
diag.encode()
|
|
790
|
+
except UnicodeEncodeError:
|
|
791
|
+
# remove non-ascii chars
|
|
792
|
+
diag = re.sub(r"[^\x00-\x7F]+", "<non-ASCII char>", diag)
|
|
793
|
+
# set error dialog
|
|
794
|
+
if append is True and self.errorDialog is not None:
|
|
795
|
+
if not prepend:
|
|
796
|
+
self.errorDialog = f"{self.errorDialog} {diag}"
|
|
797
|
+
else:
|
|
798
|
+
self.errorDialog = f"{diag} {self.errorDialog}"
|
|
799
|
+
elif append is None:
|
|
800
|
+
# keep old log
|
|
801
|
+
if self.errorDialog is None:
|
|
802
|
+
self.errorDialog = diag
|
|
803
|
+
else:
|
|
804
|
+
self.errorDialog = diag
|
|
805
|
+
|
|
806
|
+
# use loadXML
|
|
807
|
+
def useLoadXML(self):
|
|
808
|
+
return self.check_split_rule("loadXML")
|
|
809
|
+
|
|
810
|
+
# make VOMS FQANs
|
|
811
|
+
def makeFQANs(self):
|
|
812
|
+
# no working group
|
|
813
|
+
if self.workingGroup is not None:
|
|
814
|
+
fqan = f"/{self.vo}/{self.workingGroup}/Role=production"
|
|
815
|
+
else:
|
|
816
|
+
if self.vo is not None:
|
|
817
|
+
fqan = f"/{self.vo}/Role=NULL"
|
|
818
|
+
else:
|
|
819
|
+
return []
|
|
820
|
+
# return
|
|
821
|
+
return [fqan]
|
|
822
|
+
|
|
823
|
+
# set split rule
|
|
824
|
+
def setSplitRule(self, ruleName, ruleValue):
|
|
825
|
+
if self.splitRule is None:
|
|
826
|
+
# new
|
|
827
|
+
self.splitRule = self.splitRuleToken[ruleName] + "=" + ruleValue
|
|
828
|
+
else:
|
|
829
|
+
tmpMatch = re.search(self.splitRuleToken[ruleName] + "=(\d+)", self.splitRule)
|
|
830
|
+
if tmpMatch is None:
|
|
831
|
+
# append
|
|
832
|
+
self.splitRule += "," + self.splitRuleToken[ruleName] + "=" + ruleValue
|
|
833
|
+
else:
|
|
834
|
+
# replace
|
|
835
|
+
self.splitRule = re.sub(self.splitRuleToken[ruleName] + "=(\d+)", self.splitRuleToken[ruleName] + "=" + ruleValue, self.splitRule)
|
|
836
|
+
|
|
837
|
+
# remove split rule
|
|
838
|
+
def removeSplitRule(self, ruleName):
|
|
839
|
+
if self.splitRule is not None:
|
|
840
|
+
items = self.splitRule.split(",")
|
|
841
|
+
newItems = []
|
|
842
|
+
for item in items:
|
|
843
|
+
# remove rule
|
|
844
|
+
tmpRuleName = item.split("=")[0]
|
|
845
|
+
if ruleName == tmpRuleName:
|
|
846
|
+
continue
|
|
847
|
+
newItems.append(item)
|
|
848
|
+
self.splitRule = ",".join(newItems)
|
|
849
|
+
|
|
850
|
+
# set to use scout
|
|
851
|
+
def setUseScout(self, useFlag):
|
|
852
|
+
if useFlag:
|
|
853
|
+
self.setSplitRule("useScout", self.enum_useScout)
|
|
854
|
+
else:
|
|
855
|
+
self.setSplitRule("useScout", self.enum_noScout)
|
|
856
|
+
|
|
857
|
+
# set post scout
|
|
858
|
+
def setPostScout(self):
|
|
859
|
+
self.setSplitRule("useScout", self.enum_postScout)
|
|
860
|
+
|
|
861
|
+
# use scout
|
|
862
|
+
def useScout(self, splitRule=None):
|
|
863
|
+
if splitRule is None:
|
|
864
|
+
splitRule = self.splitRule
|
|
865
|
+
if splitRule is not None:
|
|
866
|
+
tmpMatch = re.search(self.splitRuleToken["useScout"] + "=(\d+)", splitRule)
|
|
867
|
+
if tmpMatch is not None and tmpMatch.group(1) == self.enum_useScout:
|
|
868
|
+
return True
|
|
869
|
+
return False
|
|
870
|
+
|
|
871
|
+
# use exhausted
|
|
872
|
+
def useExhausted(self):
|
|
873
|
+
return self.check_split_rule("useExhausted")
|
|
874
|
+
|
|
875
|
+
# use real number of events
|
|
876
|
+
def useRealNumEvents(self):
|
|
877
|
+
return self.check_split_rule("useRealNumEvents")
|
|
878
|
+
|
|
879
|
+
# use input LFN as source for output LFN
|
|
880
|
+
def useFileAsSourceLFN(self):
|
|
881
|
+
return self.check_split_rule("useFileAsSourceLFN")
|
|
882
|
+
|
|
883
|
+
# post scout
|
|
884
|
+
def isPostScout(self):
|
|
885
|
+
if self.splitRule is not None:
|
|
886
|
+
tmpMatch = re.search(self.splitRuleToken["useScout"] + "=(\d+)", self.splitRule)
|
|
887
|
+
if tmpMatch is not None and tmpMatch.group(1) == self.enum_postScout:
|
|
888
|
+
return True
|
|
889
|
+
return False
|
|
890
|
+
|
|
891
|
+
# wait until input shows up
|
|
892
|
+
def waitInput(self):
|
|
893
|
+
return self.check_split_rule("waitInput")
|
|
894
|
+
|
|
895
|
+
# input prestaging
|
|
896
|
+
def inputPreStaging(self):
|
|
897
|
+
if self.splitRule is not None:
|
|
898
|
+
tmpMatch = re.search(self.splitRuleToken["inputPreStaging"] + "=" + self.enum_inputPreStaging["use"], self.splitRule)
|
|
899
|
+
if tmpMatch is not None:
|
|
900
|
+
return True
|
|
901
|
+
return False
|
|
902
|
+
|
|
903
|
+
# set DDM backend
|
|
904
|
+
def setDdmBackEnd(self, backEnd):
|
|
905
|
+
if self.splitRule is None:
|
|
906
|
+
# new
|
|
907
|
+
self.splitRule = self.splitRuleToken["ddmBackEnd"] + "=" + backEnd
|
|
908
|
+
else:
|
|
909
|
+
tmpMatch = re.search(self.splitRuleToken["ddmBackEnd"] + "=([^,$]+)", self.splitRule)
|
|
910
|
+
if tmpMatch is None:
|
|
911
|
+
# append
|
|
912
|
+
self.splitRule += "," + self.splitRuleToken["ddmBackEnd"] + "=" + backEnd
|
|
913
|
+
else:
|
|
914
|
+
# replace
|
|
915
|
+
self.splitRule = re.sub(self.splitRuleToken["ddmBackEnd"] + "=([^,$]+)", self.splitRuleToken["ddmBackEnd"] + "=" + backEnd, self.splitRule)
|
|
916
|
+
|
|
917
|
+
# get DDM backend
|
|
918
|
+
def getDdmBackEnd(self):
|
|
919
|
+
if self.splitRule is not None:
|
|
920
|
+
tmpMatch = re.search(self.splitRuleToken["ddmBackEnd"] + "=([^,$]+)", self.splitRule)
|
|
921
|
+
if tmpMatch is not None:
|
|
922
|
+
return tmpMatch.group(1)
|
|
923
|
+
return None
|
|
924
|
+
|
|
925
|
+
# get field number to add middle name to LFN
|
|
926
|
+
def getFieldNumToLFN(self):
|
|
927
|
+
try:
|
|
928
|
+
if self.splitRule is not None:
|
|
929
|
+
tmpMatch = re.search(self.splitRuleToken["addNthFieldToLFN"] + "=([,\d]+)", self.splitRule)
|
|
930
|
+
if tmpMatch is not None:
|
|
931
|
+
tmpList = tmpMatch.group(1).split(",")
|
|
932
|
+
try:
|
|
933
|
+
tmpList.remove("")
|
|
934
|
+
except Exception:
|
|
935
|
+
pass
|
|
936
|
+
return list(map(int, tmpList))
|
|
937
|
+
except Exception:
|
|
938
|
+
pass
|
|
939
|
+
return None
|
|
940
|
+
|
|
941
|
+
# get required success rate for scout jobs
|
|
942
|
+
def getScoutSuccessRate(self):
|
|
943
|
+
if self.splitRule is not None:
|
|
944
|
+
tmpMatch = re.search(self.splitRuleToken["scoutSuccessRate"] + "=(\d+)", self.splitRule)
|
|
945
|
+
if tmpMatch is not None:
|
|
946
|
+
return int(tmpMatch.group(1))
|
|
947
|
+
return None
|
|
948
|
+
|
|
949
|
+
# get T1 weight
|
|
950
|
+
def getT1Weight(self):
|
|
951
|
+
if self.splitRule is not None:
|
|
952
|
+
tmpMatch = re.search(self.splitRuleToken["t1Weight"] + "=(-*\d+)", self.splitRule)
|
|
953
|
+
if tmpMatch is not None:
|
|
954
|
+
return int(tmpMatch.group(1))
|
|
955
|
+
return 0
|
|
956
|
+
|
|
957
|
+
# respect Lumiblock boundaries
|
|
958
|
+
def respectLumiblock(self):
|
|
959
|
+
return self.check_split_rule("respectLB")
|
|
960
|
+
|
|
961
|
+
# release files per Lumiblock
|
|
962
|
+
def releasePerLumiblock(self):
|
|
963
|
+
return self.check_split_rule("releasePerLB")
|
|
964
|
+
|
|
965
|
+
# order by Lumiblock numbers
|
|
966
|
+
def orderByLB(self):
|
|
967
|
+
return self.check_split_rule("orderByLB")
|
|
968
|
+
|
|
969
|
+
# respect split rule
|
|
970
|
+
def respectSplitRule(self):
|
|
971
|
+
return self.check_split_rule("respectSplitRule")
|
|
972
|
+
|
|
973
|
+
# allow partial finish
|
|
974
|
+
def allowPartialFinish(self):
|
|
975
|
+
return self.check_split_rule("allowPartialFinish")
|
|
976
|
+
|
|
977
|
+
# check if datasets should be registered
|
|
978
|
+
def toRegisterDatasets(self):
|
|
979
|
+
if self.splitRule is not None:
|
|
980
|
+
tmpMatch = re.search(self.splitRuleToken["registerDatasets"] + "=(\d+)", self.splitRule)
|
|
981
|
+
if tmpMatch is not None and tmpMatch.group(1) == self.enum_toRegisterDS:
|
|
982
|
+
return True
|
|
983
|
+
return False
|
|
984
|
+
|
|
985
|
+
# datasets were registered
|
|
986
|
+
def registeredDatasets(self):
|
|
987
|
+
self.setSplitRule("registerDatasets", self.enum_registeredDS)
|
|
988
|
+
|
|
989
|
+
# set datasets to be registered
|
|
990
|
+
def setToRegisterDatasets(self):
|
|
991
|
+
self.setSplitRule("registerDatasets", self.enum_toRegisterDS)
|
|
992
|
+
|
|
993
|
+
# get the max number of attempts for ES events
|
|
994
|
+
def getMaxAttemptES(self):
|
|
995
|
+
if self.splitRule is not None:
|
|
996
|
+
tmpMatch = re.search(self.splitRuleToken["maxAttemptES"] + "=(\d+)", self.splitRule)
|
|
997
|
+
if tmpMatch is not None:
|
|
998
|
+
return int(tmpMatch.group(1))
|
|
999
|
+
return None
|
|
1000
|
+
|
|
1001
|
+
# get the max number of attempts for ES jobs
|
|
1002
|
+
def getMaxAttemptEsJob(self):
|
|
1003
|
+
if self.splitRule is not None:
|
|
1004
|
+
tmpMatch = re.search(self.splitRuleToken["maxAttemptEsJob"] + "=(\d+)", self.splitRule)
|
|
1005
|
+
if tmpMatch is not None:
|
|
1006
|
+
return int(tmpMatch.group(1))
|
|
1007
|
+
return self.getMaxAttemptES()
|
|
1008
|
+
|
|
1009
|
+
# check attribute length
|
|
1010
|
+
def checkAttrLength(self):
|
|
1011
|
+
for attrName, attrLength in self._attrLength.items():
|
|
1012
|
+
attrVal = getattr(self, attrName)
|
|
1013
|
+
if attrVal is None:
|
|
1014
|
+
continue
|
|
1015
|
+
if len(attrVal) > attrLength:
|
|
1016
|
+
setattr(self, attrName, None)
|
|
1017
|
+
self.errorDialog = f"{attrName} is too long (actual: {len(attrVal)}, maximum: {attrLength})"
|
|
1018
|
+
return False
|
|
1019
|
+
return True
|
|
1020
|
+
|
|
1021
|
+
# set IP connectivity and stack
|
|
1022
|
+
def setIpConnectivity(self, value):
|
|
1023
|
+
if not value:
|
|
1024
|
+
return
|
|
1025
|
+
values = value.split("#")
|
|
1026
|
+
if not values:
|
|
1027
|
+
return
|
|
1028
|
+
ipConnectivity = values[0]
|
|
1029
|
+
if ipConnectivity in self.enum_ipConnectivity.values():
|
|
1030
|
+
for tmpKey, tmpVal in self.enum_ipConnectivity.items():
|
|
1031
|
+
if ipConnectivity == tmpVal:
|
|
1032
|
+
self.setSplitRule("ipConnectivity", tmpKey)
|
|
1033
|
+
break
|
|
1034
|
+
if len(values) > 1:
|
|
1035
|
+
ipStack = values[1]
|
|
1036
|
+
if ipStack in self.enum_ipStack.values():
|
|
1037
|
+
for tmpKey, tmpVal in self.enum_ipStack.items():
|
|
1038
|
+
if ipStack == tmpVal:
|
|
1039
|
+
self.setSplitRule("ipStack", tmpKey)
|
|
1040
|
+
break
|
|
1041
|
+
|
|
1042
|
+
# get IP connectivity
|
|
1043
|
+
def getIpConnectivity(self):
|
|
1044
|
+
if self.splitRule is not None:
|
|
1045
|
+
tmpMatch = re.search(self.splitRuleToken["ipConnectivity"] + "=(\d+)", self.splitRule)
|
|
1046
|
+
if tmpMatch is not None:
|
|
1047
|
+
return self.enum_ipConnectivity[tmpMatch.group(1)]
|
|
1048
|
+
return None
|
|
1049
|
+
|
|
1050
|
+
# get IP connectivity
|
|
1051
|
+
def getIpStack(self):
|
|
1052
|
+
if self.splitRule is not None:
|
|
1053
|
+
tmpMatch = re.search(self.splitRuleToken["ipStack"] + "=(\d+)", self.splitRule)
|
|
1054
|
+
if tmpMatch is not None:
|
|
1055
|
+
return self.enum_ipStack[tmpMatch.group(1)]
|
|
1056
|
+
return None
|
|
1057
|
+
|
|
1058
|
+
# use HS06 for walltime estimation
|
|
1059
|
+
def useHS06(self):
|
|
1060
|
+
return self.cpuTimeUnit in ["HS06sPerEvent", "HS06sPerEventFixed", "mHS06sPerEvent", "mHS06sPerEventFixed"]
|
|
1061
|
+
|
|
1062
|
+
# get CPU time in sec
|
|
1063
|
+
def getCpuTime(self):
|
|
1064
|
+
if not self.useHS06():
|
|
1065
|
+
return None
|
|
1066
|
+
try:
|
|
1067
|
+
if self.cpuTimeUnit.startswith("m"):
|
|
1068
|
+
return float(self.cpuTime) / 1000.0
|
|
1069
|
+
except Exception:
|
|
1070
|
+
pass
|
|
1071
|
+
return self.cpuTime
|
|
1072
|
+
|
|
1073
|
+
# RAM scales with nCores
|
|
1074
|
+
def ramPerCore(self):
|
|
1075
|
+
return self.ramUnit in ["MBPerCore", "MBPerCoreFixed"]
|
|
1076
|
+
|
|
1077
|
+
# run until input is closed
|
|
1078
|
+
def runUntilClosed(self):
|
|
1079
|
+
return self.check_split_rule("runUntilClosed")
|
|
1080
|
+
|
|
1081
|
+
# stay output on site
|
|
1082
|
+
def stayOutputOnSite(self):
|
|
1083
|
+
return self.check_split_rule("stayOutputOnSite")
|
|
1084
|
+
|
|
1085
|
+
# fail when goal unreached
|
|
1086
|
+
def failGoalUnreached(self):
|
|
1087
|
+
return self.check_split_rule("failGoalUnreached")
|
|
1088
|
+
|
|
1089
|
+
# unset fail when goal unreached
|
|
1090
|
+
def unsetFailGoalUnreached(self):
|
|
1091
|
+
self.removeSplitRule(self.splitRuleToken["failGoalUnreached"])
|
|
1092
|
+
|
|
1093
|
+
# switch ES to normal when jobs land at normal sites
|
|
1094
|
+
def switchEStoNormal(self):
|
|
1095
|
+
return self.check_split_rule("switchEStoNormal")
|
|
1096
|
+
|
|
1097
|
+
# use world cloud
|
|
1098
|
+
def useWorldCloud(self):
|
|
1099
|
+
return self.cloud == self.worldCloudName
|
|
1100
|
+
|
|
1101
|
+
# dynamic number of events
|
|
1102
|
+
def dynamicNumEvents(self):
|
|
1103
|
+
if self.splitRule is not None:
|
|
1104
|
+
tmpMatch = re.search(self.splitRuleToken["dynamicNumEvents"] + "=(\d+)", self.splitRule)
|
|
1105
|
+
if tmpMatch is not None:
|
|
1106
|
+
return True
|
|
1107
|
+
return False
|
|
1108
|
+
|
|
1109
|
+
# get min granularity for dynamic number of events
|
|
1110
|
+
def get_min_granularity(self):
|
|
1111
|
+
if self.splitRule is not None:
|
|
1112
|
+
tmpMatch = re.search(self.splitRuleToken["dynamicNumEvents"] + "=(\d+)", self.splitRule)
|
|
1113
|
+
if tmpMatch is not None:
|
|
1114
|
+
return int(tmpMatch.group(1))
|
|
1115
|
+
return None
|
|
1116
|
+
|
|
1117
|
+
# set alternative stage-out
|
|
1118
|
+
def setAltStageOut(self, value):
|
|
1119
|
+
if value in self.enum_altStageOut.values():
|
|
1120
|
+
for tmpKey, tmpVal in self.enum_altStageOut.items():
|
|
1121
|
+
if value == tmpVal:
|
|
1122
|
+
self.setSplitRule("altStageOut", tmpKey)
|
|
1123
|
+
break
|
|
1124
|
+
|
|
1125
|
+
# get alternative stage-out
|
|
1126
|
+
def getAltStageOut(self):
|
|
1127
|
+
if self.splitRule is not None:
|
|
1128
|
+
tmpMatch = re.search(self.splitRuleToken["altStageOut"] + "=(\d+)", self.splitRule)
|
|
1129
|
+
if tmpMatch is not None:
|
|
1130
|
+
return self.enum_altStageOut[tmpMatch.group(1)]
|
|
1131
|
+
return None
|
|
1132
|
+
|
|
1133
|
+
# allow WAN for input access
|
|
1134
|
+
def allowInputWAN(self):
|
|
1135
|
+
return self.check_split_rule("allowInputWAN")
|
|
1136
|
+
|
|
1137
|
+
# set mode for input LAN access
|
|
1138
|
+
def setAllowInputLAN(self, value):
|
|
1139
|
+
if value in self.enum_inputLAN.values():
|
|
1140
|
+
for tmpKey, tmpVal in self.enum_inputLAN.items():
|
|
1141
|
+
if value == tmpVal:
|
|
1142
|
+
self.setSplitRule("allowInputLAN", tmpKey)
|
|
1143
|
+
break
|
|
1144
|
+
|
|
1145
|
+
# check if LAN is used for input access
|
|
1146
|
+
def allowInputLAN(self):
|
|
1147
|
+
if self.splitRule is not None:
|
|
1148
|
+
tmpMatch = re.search(self.splitRuleToken["allowInputLAN"] + "=(\d+)", self.splitRule)
|
|
1149
|
+
if tmpMatch is not None:
|
|
1150
|
+
return self.enum_inputLAN[tmpMatch.group(1)]
|
|
1151
|
+
return None
|
|
1152
|
+
|
|
1153
|
+
# put log files to OS
|
|
1154
|
+
def putLogToOS(self):
|
|
1155
|
+
return self.check_split_rule("putLogToOS")
|
|
1156
|
+
|
|
1157
|
+
# merge ES on Object Store
|
|
1158
|
+
def mergeEsOnOS(self):
|
|
1159
|
+
return self.check_split_rule("mergeEsOnOS")
|
|
1160
|
+
|
|
1161
|
+
# write input to file
|
|
1162
|
+
def writeInputToFile(self):
|
|
1163
|
+
return self.check_split_rule("writeInputToFile")
|
|
1164
|
+
|
|
1165
|
+
# ignore missing input datasets
|
|
1166
|
+
def ignoreMissingInDS(self):
|
|
1167
|
+
return self.check_split_rule("ignoreMissingInDS")
|
|
1168
|
+
|
|
1169
|
+
# suppress execute string conversion
|
|
1170
|
+
def noExecStrCnv(self):
|
|
1171
|
+
return self.check_split_rule("noExecStrCnv")
|
|
1172
|
+
|
|
1173
|
+
# in-file positional event number
|
|
1174
|
+
def inFilePosEvtNum(self):
|
|
1175
|
+
return self.check_split_rule("inFilePosEvtNum")
|
|
1176
|
+
|
|
1177
|
+
# register event service files
|
|
1178
|
+
def registerEsFiles(self):
|
|
1179
|
+
return self.check_split_rule("registerEsFiles")
|
|
1180
|
+
|
|
1181
|
+
# disable auto finish
|
|
1182
|
+
def disableAutoFinish(self):
|
|
1183
|
+
return self.check_split_rule("disableAutoFinish")
|
|
1184
|
+
|
|
1185
|
+
# reset refined attributes which may confuse they system
|
|
1186
|
+
def resetRefinedAttrs(self):
|
|
1187
|
+
self.resetChangedAttr("splitRule")
|
|
1188
|
+
self.resetChangedAttr("eventService")
|
|
1189
|
+
self.reserve_old_attributes()
|
|
1190
|
+
|
|
1191
|
+
# resurrect consumers
|
|
1192
|
+
def resurrectConsumers(self):
|
|
1193
|
+
return self.check_split_rule("resurrectConsumers")
|
|
1194
|
+
|
|
1195
|
+
# use prefetcher
|
|
1196
|
+
def usePrefetcher(self):
|
|
1197
|
+
return self.check_split_rule("usePrefetcher")
|
|
1198
|
+
|
|
1199
|
+
# no input pooling
|
|
1200
|
+
def noInputPooling(self):
|
|
1201
|
+
return self.check_split_rule("noInputPooling")
|
|
1202
|
+
|
|
1203
|
+
# get num of input chunks to wait
|
|
1204
|
+
def nChunksToWait(self):
|
|
1205
|
+
if self.splitRule is not None:
|
|
1206
|
+
tmpMatch = re.search(self.splitRuleToken["nChunksToWait"] + "=(\d+)", self.splitRule)
|
|
1207
|
+
if tmpMatch is not None:
|
|
1208
|
+
return int(tmpMatch.group(1))
|
|
1209
|
+
return None
|
|
1210
|
+
|
|
1211
|
+
# get max walltime
|
|
1212
|
+
def getMaxWalltime(self):
|
|
1213
|
+
if self.splitRule is not None:
|
|
1214
|
+
tmpMatch = re.search(self.splitRuleToken["maxWalltime"] + "=(\d+)", self.splitRule)
|
|
1215
|
+
if tmpMatch is not None:
|
|
1216
|
+
return int(tmpMatch.group(1)) * 60 * 60
|
|
1217
|
+
return None
|
|
1218
|
+
|
|
1219
|
+
# set max walltime
|
|
1220
|
+
def set_max_walltime(self, value):
|
|
1221
|
+
self.setSplitRule("maxWalltime", str(value))
|
|
1222
|
+
|
|
1223
|
+
# get target size of the largest output to reset NG
|
|
1224
|
+
def getTgtMaxOutputForNG(self):
|
|
1225
|
+
if self.splitRule is not None:
|
|
1226
|
+
tmpMatch = re.search(self.splitRuleToken["tgtMaxOutputForNG"] + "=(\d+)", self.splitRule)
|
|
1227
|
+
if tmpMatch is not None:
|
|
1228
|
+
return int(tmpMatch.group(1))
|
|
1229
|
+
return None
|
|
1230
|
+
|
|
1231
|
+
# not discard events
|
|
1232
|
+
def notDiscardEvents(self):
|
|
1233
|
+
return self.check_split_rule("notDiscardEvents")
|
|
1234
|
+
|
|
1235
|
+
# get min CPU efficiency
|
|
1236
|
+
def getMinCpuEfficiency(self):
|
|
1237
|
+
if self.splitRule is not None:
|
|
1238
|
+
tmpMatch = re.search(self.splitRuleToken["minCpuEfficiency"] + "=(\d+)", self.splitRule)
|
|
1239
|
+
if tmpMatch is not None:
|
|
1240
|
+
return int(tmpMatch.group(1))
|
|
1241
|
+
return None
|
|
1242
|
+
|
|
1243
|
+
# decrement attemptNr of events only when failed
|
|
1244
|
+
def decAttOnFailedES(self):
|
|
1245
|
+
return self.check_split_rule("decAttOnFailedES")
|
|
1246
|
+
|
|
1247
|
+
# use zip files to pin input files
|
|
1248
|
+
def useZipToPin(self):
|
|
1249
|
+
return self.check_split_rule("useZipToPin")
|
|
1250
|
+
|
|
1251
|
+
# architecture:
|
|
1252
|
+
# old format: sw_platform<@base_platform><#host_cpu_spec><&host_gpu_spec>
|
|
1253
|
+
# host_cpu_spec: architecture<-vendor<-instruction_set>>
|
|
1254
|
+
# host_gpu_spec: vendor<-model>
|
|
1255
|
+
#
|
|
1256
|
+
# new format: a json dict with keys of sw_platform, base_platform, cpu_specs, and gpu_spec
|
|
1257
|
+
# cpu_specs: a list of json dicts with keys of arch, vendor, and instr
|
|
1258
|
+
# gpu_spec: a json dict with keys of vendor and model
|
|
1259
|
+
|
|
1260
|
+
# reformat architecture into JSON
|
|
1261
|
+
def reformat_architecture(self):
|
|
1262
|
+
if self.architecture is None:
|
|
1263
|
+
return
|
|
1264
|
+
encoded_platform = ""
|
|
1265
|
+
try:
|
|
1266
|
+
# JSON format without encoded platform
|
|
1267
|
+
tmp_dict = json.loads(self.architecture)
|
|
1268
|
+
# skip if encoded platform is not present
|
|
1269
|
+
if "encoded_platform" not in tmp_dict:
|
|
1270
|
+
return
|
|
1271
|
+
encoded_platform = tmp_dict["encoded_platform"]
|
|
1272
|
+
except Exception:
|
|
1273
|
+
pass
|
|
1274
|
+
# convert to new format
|
|
1275
|
+
new_dict = {}
|
|
1276
|
+
val = self.get_sw_platform()
|
|
1277
|
+
if val:
|
|
1278
|
+
new_dict["sw_platform"] = val
|
|
1279
|
+
val = self.get_base_platform(encoded_platform)
|
|
1280
|
+
if val:
|
|
1281
|
+
new_dict["base_platform"] = val
|
|
1282
|
+
val = self.get_host_cpu_spec(encoded_platform)
|
|
1283
|
+
if val:
|
|
1284
|
+
# remove wildcard entries and empty specs
|
|
1285
|
+
l = [x for x in [{k: v for k, v in d.items() if v != "*"} for d in val] if x]
|
|
1286
|
+
if l:
|
|
1287
|
+
new_dict["cpu_specs"] = l
|
|
1288
|
+
val = self.get_host_gpu_spec()
|
|
1289
|
+
if val:
|
|
1290
|
+
# remove wildcard entries and empty specs
|
|
1291
|
+
l = {k: v for k, v in val.items() if v != "*"}
|
|
1292
|
+
if l:
|
|
1293
|
+
new_dict["gpu_spec"] = l
|
|
1294
|
+
self.architecture = json.dumps(new_dict)
|
|
1295
|
+
|
|
1296
|
+
# get SW platform
|
|
1297
|
+
def get_sw_platform(self):
|
|
1298
|
+
try:
|
|
1299
|
+
d = json.loads(self.architecture)
|
|
1300
|
+
return d.get("sw_platform", "")
|
|
1301
|
+
except Exception:
|
|
1302
|
+
pass
|
|
1303
|
+
if self.architecture is not None:
|
|
1304
|
+
m = re.search("^([^@#&]*)", self.architecture)
|
|
1305
|
+
if m:
|
|
1306
|
+
return m.group(1)
|
|
1307
|
+
return self.architecture
|
|
1308
|
+
|
|
1309
|
+
# get base platform
|
|
1310
|
+
def get_base_platform(self, encoded_platform=None):
|
|
1311
|
+
try:
|
|
1312
|
+
d = json.loads(self.architecture)
|
|
1313
|
+
val = d.get("base_platform", None)
|
|
1314
|
+
if val is not None or encoded_platform is None:
|
|
1315
|
+
return val
|
|
1316
|
+
except Exception:
|
|
1317
|
+
pass
|
|
1318
|
+
if encoded_platform:
|
|
1319
|
+
architecture = encoded_platform
|
|
1320
|
+
else:
|
|
1321
|
+
architecture = self.architecture
|
|
1322
|
+
if architecture is None or "@" not in architecture:
|
|
1323
|
+
return None
|
|
1324
|
+
m = re.search("@([^#&]*)", architecture)
|
|
1325
|
+
img = m.group(1)
|
|
1326
|
+
if img == "":
|
|
1327
|
+
img = None
|
|
1328
|
+
return img
|
|
1329
|
+
|
|
1330
|
+
# get platforms
|
|
1331
|
+
def get_platforms(self):
|
|
1332
|
+
if self.architecture is not None:
|
|
1333
|
+
platform = self.get_sw_platform()
|
|
1334
|
+
base = self.get_base_platform()
|
|
1335
|
+
if base:
|
|
1336
|
+
platform += "@" + base
|
|
1337
|
+
return platform
|
|
1338
|
+
return self.architecture
|
|
1339
|
+
|
|
1340
|
+
# get host CPU spec
|
|
1341
|
+
def get_host_cpu_spec(self, encoded_platform=None):
|
|
1342
|
+
try:
|
|
1343
|
+
d = json.loads(self.architecture)
|
|
1344
|
+
specs = d.get("cpu_specs", None)
|
|
1345
|
+
if not specs and encoded_platform is None:
|
|
1346
|
+
return None
|
|
1347
|
+
else:
|
|
1348
|
+
for spec in specs:
|
|
1349
|
+
spec.setdefault("vendor", "*")
|
|
1350
|
+
spec.setdefault("instr", "*")
|
|
1351
|
+
return specs
|
|
1352
|
+
except Exception:
|
|
1353
|
+
pass
|
|
1354
|
+
if encoded_platform:
|
|
1355
|
+
architecture = encoded_platform
|
|
1356
|
+
else:
|
|
1357
|
+
architecture = self.architecture
|
|
1358
|
+
try:
|
|
1359
|
+
if not architecture:
|
|
1360
|
+
return None
|
|
1361
|
+
if "#" not in architecture:
|
|
1362
|
+
if re.search(r"^[\^@&]", architecture):
|
|
1363
|
+
return None
|
|
1364
|
+
arch = architecture.split("-")[0]
|
|
1365
|
+
if arch:
|
|
1366
|
+
return [{"arch": arch, "vendor": "*", "instr": "*"}]
|
|
1367
|
+
return None
|
|
1368
|
+
m = re.search(r"#([^\^@&]*)", architecture)
|
|
1369
|
+
spec_strs = m.group(1)
|
|
1370
|
+
if not spec_strs:
|
|
1371
|
+
return None
|
|
1372
|
+
# remove ()
|
|
1373
|
+
if spec_strs.startswith("("):
|
|
1374
|
+
spec_strs = spec_strs[1:]
|
|
1375
|
+
if spec_strs.endswith(")"):
|
|
1376
|
+
spec_strs = spec_strs[:-1]
|
|
1377
|
+
specs = []
|
|
1378
|
+
for spec_str in spec_strs.split("|"):
|
|
1379
|
+
spec_str += "-*" * (2 - spec_str.count("-"))
|
|
1380
|
+
if "-" not in spec_str:
|
|
1381
|
+
spec_str += "-*"
|
|
1382
|
+
items = spec_str.split("-")
|
|
1383
|
+
spec = {"arch": items[0], "vendor": items[1], "instr": items[2]}
|
|
1384
|
+
specs.append(spec)
|
|
1385
|
+
return specs
|
|
1386
|
+
except Exception:
|
|
1387
|
+
return None
|
|
1388
|
+
|
|
1389
|
+
def get_host_cpu_preference(self):
|
|
1390
|
+
try:
|
|
1391
|
+
d = json.loads(self.architecture)
|
|
1392
|
+
cpu_pref = d.get("cpu_pref", None)
|
|
1393
|
+
return cpu_pref
|
|
1394
|
+
except Exception:
|
|
1395
|
+
return None
|
|
1396
|
+
|
|
1397
|
+
# get host GPU spec
|
|
1398
|
+
def get_host_gpu_spec(self):
|
|
1399
|
+
try:
|
|
1400
|
+
d = json.loads(self.architecture)
|
|
1401
|
+
spec = d.get("gpu_spec", None)
|
|
1402
|
+
spec.setdefault("vendor", "*")
|
|
1403
|
+
spec.setdefault("model", "*")
|
|
1404
|
+
return spec
|
|
1405
|
+
except Exception:
|
|
1406
|
+
pass
|
|
1407
|
+
try:
|
|
1408
|
+
if self.architecture is None or "&" not in self.architecture:
|
|
1409
|
+
return None
|
|
1410
|
+
m = re.search(r"&([^\^@#]*)", self.architecture)
|
|
1411
|
+
spec_str = m.group(1)
|
|
1412
|
+
if not spec_str:
|
|
1413
|
+
return None
|
|
1414
|
+
spec_str += "-*" * (1 - spec_str.count("-"))
|
|
1415
|
+
items = spec_str.split("-")
|
|
1416
|
+
spec = {"vendor": items[0], "model": items[1]}
|
|
1417
|
+
return spec
|
|
1418
|
+
except Exception:
|
|
1419
|
+
return None
|
|
1420
|
+
|
|
1421
|
+
# HPO workflow
|
|
1422
|
+
def is_hpo_workflow(self):
|
|
1423
|
+
return self.check_split_rule("hpoWorkflow")
|
|
1424
|
+
|
|
1425
|
+
# debug mode
|
|
1426
|
+
def is_debug_mode(self):
|
|
1427
|
+
return self.check_split_rule("debugMode")
|
|
1428
|
+
|
|
1429
|
+
# multi-step execution
|
|
1430
|
+
def is_multi_step_exec(self):
|
|
1431
|
+
return self.check_split_rule("multiStepExec")
|
|
1432
|
+
|
|
1433
|
+
# get max number of jobs
|
|
1434
|
+
def get_max_num_jobs(self):
|
|
1435
|
+
if self.splitRule is not None:
|
|
1436
|
+
tmpMatch = re.search(self.splitRuleToken["maxNumJobs"] + "=(\d+)", self.splitRule)
|
|
1437
|
+
if tmpMatch is not None:
|
|
1438
|
+
return int(tmpMatch.group(1))
|
|
1439
|
+
return None
|
|
1440
|
+
|
|
1441
|
+
# get total number of jobs
|
|
1442
|
+
def get_total_num_jobs(self):
|
|
1443
|
+
if self.splitRule is not None:
|
|
1444
|
+
tmpMatch = re.search(self.splitRuleToken["totNumJobs"] + "=(\d+)", self.splitRule)
|
|
1445
|
+
if tmpMatch is not None:
|
|
1446
|
+
return int(tmpMatch.group(1))
|
|
1447
|
+
return None
|
|
1448
|
+
|
|
1449
|
+
# use only tags for fat container
|
|
1450
|
+
def use_only_tags_fc(self):
|
|
1451
|
+
return self.check_split_rule("onlyTagsForFC")
|
|
1452
|
+
|
|
1453
|
+
# avoid VP
|
|
1454
|
+
def avoid_vp(self):
|
|
1455
|
+
return self.check_split_rule("avoidVP")
|
|
1456
|
+
|
|
1457
|
+
# set first contents feed
|
|
1458
|
+
def set_first_contents_feed(self, is_first):
|
|
1459
|
+
if is_first:
|
|
1460
|
+
self.setSplitRule("firstContentsFeed", self.FirstContentsFeed.TRUE.value)
|
|
1461
|
+
else:
|
|
1462
|
+
self.setSplitRule("firstContentsFeed", self.FirstContentsFeed.FALSE.value)
|
|
1463
|
+
|
|
1464
|
+
# check if first contents feed
|
|
1465
|
+
def is_first_contents_feed(self):
|
|
1466
|
+
if self.splitRule is not None:
|
|
1467
|
+
tmpMatch = re.search(self.splitRuleToken["firstContentsFeed"] + "=(\d+)", self.splitRule)
|
|
1468
|
+
if tmpMatch is not None and tmpMatch.group(1) == self.FirstContentsFeed.TRUE.value:
|
|
1469
|
+
return True
|
|
1470
|
+
return False
|
|
1471
|
+
|
|
1472
|
+
# check if work is segmented
|
|
1473
|
+
def is_work_segmented(self):
|
|
1474
|
+
return self.check_split_rule("segmentedWork")
|
|
1475
|
+
|
|
1476
|
+
# check if looping check is disabled
|
|
1477
|
+
def no_looping_check(self):
|
|
1478
|
+
return self.check_split_rule("noLoopingCheck")
|
|
1479
|
+
|
|
1480
|
+
# encode job parameters
|
|
1481
|
+
def encode_job_params(self):
|
|
1482
|
+
return self.check_split_rule("encJobParams")
|
|
1483
|
+
|
|
1484
|
+
# get original error dialog
|
|
1485
|
+
def get_original_error_dialog(self):
|
|
1486
|
+
if not self.origErrorDialog:
|
|
1487
|
+
return ""
|
|
1488
|
+
# remove log URL
|
|
1489
|
+
tmpStr = re.sub("<a href.+</a> : ", "", self.origErrorDialog)
|
|
1490
|
+
return tmpStr.split(". ")[-1]
|
|
1491
|
+
|
|
1492
|
+
# check if secrets are used
|
|
1493
|
+
def use_secrets(self):
|
|
1494
|
+
return self.check_split_rule("useSecrets")
|
|
1495
|
+
|
|
1496
|
+
# get max core count
|
|
1497
|
+
def get_max_core_count(self):
|
|
1498
|
+
if self.splitRule is not None:
|
|
1499
|
+
tmpMatch = re.search(self.splitRuleToken["maxCoreCount"] + "=(\d+)", self.splitRule)
|
|
1500
|
+
if tmpMatch is not None:
|
|
1501
|
+
return int(tmpMatch.group(1))
|
|
1502
|
+
return None
|
|
1503
|
+
|
|
1504
|
+
# push status changes
|
|
1505
|
+
def push_status_changes(self):
|
|
1506
|
+
return push_status_changes(self.splitRule)
|
|
1507
|
+
|
|
1508
|
+
# use cloud as VO
|
|
1509
|
+
def cloud_as_vo(self):
|
|
1510
|
+
return self.check_split_rule("cloudAsVO")
|
|
1511
|
+
|
|
1512
|
+
# push job
|
|
1513
|
+
def push_job(self):
|
|
1514
|
+
return self.check_split_rule("pushJob")
|
|
1515
|
+
|
|
1516
|
+
# fine-grained process
|
|
1517
|
+
def is_fine_grained_process(self):
|
|
1518
|
+
return self.check_split_rule("fineGrainedProc")
|
|
1519
|
+
|
|
1520
|
+
# on site merging
|
|
1521
|
+
def on_site_merging(self):
|
|
1522
|
+
return self.check_split_rule("onSiteMerging")
|
|
1523
|
+
|
|
1524
|
+
# set full chain flag
|
|
1525
|
+
def set_full_chain(self, mode):
|
|
1526
|
+
var = None
|
|
1527
|
+
if mode == "only":
|
|
1528
|
+
var = self.FullChain.Only
|
|
1529
|
+
elif mode == "require":
|
|
1530
|
+
var = self.FullChain.Require
|
|
1531
|
+
elif mode == "capable":
|
|
1532
|
+
var = self.FullChain.Capable
|
|
1533
|
+
if var:
|
|
1534
|
+
self.setSplitRule("fullChain", var)
|
|
1535
|
+
|
|
1536
|
+
# get full chain flag
|
|
1537
|
+
def get_full_chain(self):
|
|
1538
|
+
if self.splitRule:
|
|
1539
|
+
tmpMatch = re.search(self.splitRuleToken["fullChain"] + r"=(\d+)", self.splitRule)
|
|
1540
|
+
if tmpMatch:
|
|
1541
|
+
return tmpMatch.group(1)
|
|
1542
|
+
return None
|
|
1543
|
+
|
|
1544
|
+
# check full chain with mode
|
|
1545
|
+
def check_full_chain_with_mode(self, mode):
|
|
1546
|
+
task_flag = self.get_full_chain()
|
|
1547
|
+
if mode == "only":
|
|
1548
|
+
if task_flag == self.FullChain.Only:
|
|
1549
|
+
return True
|
|
1550
|
+
elif mode == "require":
|
|
1551
|
+
if task_flag == self.FullChain.Require:
|
|
1552
|
+
return True
|
|
1553
|
+
elif mode == "capable":
|
|
1554
|
+
if task_flag == self.FullChain.Capable:
|
|
1555
|
+
return True
|
|
1556
|
+
return False
|
|
1557
|
+
|
|
1558
|
+
# check full chain with nucleus
|
|
1559
|
+
def check_full_chain_with_nucleus(self, nucleus):
|
|
1560
|
+
if self.get_full_chain() and nucleus.get_bare_nucleus_mode():
|
|
1561
|
+
return True
|
|
1562
|
+
return False
|
|
1563
|
+
|
|
1564
|
+
# get RAM for retry
|
|
1565
|
+
def get_ram_for_retry(self, current_ram):
|
|
1566
|
+
if not self.splitRule:
|
|
1567
|
+
return None
|
|
1568
|
+
tmpMatch = re.search(self.splitRuleToken["retryRamOffset"] + r"=(\d+)", self.splitRule)
|
|
1569
|
+
if not tmpMatch:
|
|
1570
|
+
return None
|
|
1571
|
+
offset = int(tmpMatch.group(1))
|
|
1572
|
+
tmpMatch = re.search(self.splitRuleToken["retryRamStep"] + r"=(\d+)", self.splitRule)
|
|
1573
|
+
if tmpMatch:
|
|
1574
|
+
step = int(tmpMatch.group(1))
|
|
1575
|
+
else:
|
|
1576
|
+
step = 0
|
|
1577
|
+
tmpMatch = re.search(self.splitRuleToken["retryRamMax"] + r"=(\d+)", self.splitRule)
|
|
1578
|
+
if tmpMatch:
|
|
1579
|
+
max_ram = int(tmpMatch.group(1))
|
|
1580
|
+
else:
|
|
1581
|
+
max_ram = None
|
|
1582
|
+
if not current_ram:
|
|
1583
|
+
return current_ram
|
|
1584
|
+
if current_ram < offset:
|
|
1585
|
+
if max_ram is None:
|
|
1586
|
+
return offset
|
|
1587
|
+
else:
|
|
1588
|
+
return min(offset, max_ram)
|
|
1589
|
+
if not step:
|
|
1590
|
+
return current_ram
|
|
1591
|
+
if max_ram is None:
|
|
1592
|
+
return offset + math.ceil((current_ram - offset) / step) * step
|
|
1593
|
+
else:
|
|
1594
|
+
return min(offset + math.ceil((current_ram - offset) / step) * step, max_ram)
|
|
1595
|
+
|
|
1596
|
+
# get number of events per input
|
|
1597
|
+
def get_num_events_per_input(self):
|
|
1598
|
+
if self.splitRule is not None:
|
|
1599
|
+
tmpMatch = re.search(self.splitRuleToken["nEventsPerInput"] + "=(\d+)", self.splitRule)
|
|
1600
|
+
if tmpMatch is not None:
|
|
1601
|
+
return int(tmpMatch.group(1))
|
|
1602
|
+
return None
|
|
1603
|
+
|
|
1604
|
+
def get_max_events_per_job(self):
|
|
1605
|
+
if self.splitRule is not None:
|
|
1606
|
+
tmpMatch = re.search(self.splitRuleToken["maxEventsPerJob"] + "=(\d+)", self.splitRule)
|
|
1607
|
+
if tmpMatch is not None:
|
|
1608
|
+
return int(tmpMatch.group(1))
|
|
1609
|
+
return None
|
|
1610
|
+
|
|
1611
|
+
# set order input by
|
|
1612
|
+
def set_order_input_by(self, mode):
|
|
1613
|
+
var = None
|
|
1614
|
+
if mode == "eventsAlignment":
|
|
1615
|
+
var = self.OrderInputBy.eventsAlignment
|
|
1616
|
+
if var:
|
|
1617
|
+
self.setSplitRule("orderInputBy", var)
|
|
1618
|
+
|
|
1619
|
+
# get full chain flag
|
|
1620
|
+
def order_input_by(self):
|
|
1621
|
+
if self.splitRule:
|
|
1622
|
+
tmpMatch = re.search(self.splitRuleToken["orderInputBy"] + r"=(\d+)", self.splitRule)
|
|
1623
|
+
if tmpMatch:
|
|
1624
|
+
if tmpMatch.group(1) == self.OrderInputBy.eventsAlignment:
|
|
1625
|
+
return "eventsAlignment"
|
|
1626
|
+
return None
|
|
1627
|
+
|
|
1628
|
+
# check if intermediate task
|
|
1629
|
+
def is_intermediate_task(self):
|
|
1630
|
+
return self.check_split_rule("intermediateTask")
|
|
1631
|
+
|
|
1632
|
+
# check if message driven
|
|
1633
|
+
def is_msg_driven(self):
|
|
1634
|
+
return is_msg_driven(self.splitRule)
|
|
1635
|
+
|
|
1636
|
+
# check if incomplete input datasets are allowed
|
|
1637
|
+
def allow_incomplete_input(self):
|
|
1638
|
+
return self.check_split_rule("allowIncompleteInDS")
|
|
1639
|
+
|
|
1640
|
+
# check if workflow holdup
|
|
1641
|
+
def is_workflow_holdup(self):
|
|
1642
|
+
return self.check_split_rule("workflowHoldup")
|
|
1643
|
+
|
|
1644
|
+
# set workflow holdup
|
|
1645
|
+
def set_workflow_holdup(self, value: bool):
|
|
1646
|
+
if value:
|
|
1647
|
+
self.setSplitRule("workflowHoldup", "1")
|
|
1648
|
+
else:
|
|
1649
|
+
self.removeSplitRule(self.splitRuleToken["workflowHoldup"])
|
|
1650
|
+
|
|
1651
|
+
# get queued time
|
|
1652
|
+
def get_queued_time(self):
|
|
1653
|
+
"""
|
|
1654
|
+
Get queued time in timestamp
|
|
1655
|
+
:return: queued time in timestamp. None if not set
|
|
1656
|
+
"""
|
|
1657
|
+
if self.queuedTime is None:
|
|
1658
|
+
return None
|
|
1659
|
+
return self.queuedTime.timestamp()
|
|
1660
|
+
|
|
1661
|
+
|
|
1662
|
+
# utils
|
|
1663
|
+
|
|
1664
|
+
|
|
1665
|
+
# check split rule with positive integer
|
|
1666
|
+
def check_split_rule_positive_int(key, split_rule):
|
|
1667
|
+
if not split_rule:
|
|
1668
|
+
return False
|
|
1669
|
+
tmpMatch = re.search(JediTaskSpec.splitRuleToken[key] + r"=(\d+)", split_rule)
|
|
1670
|
+
if not tmpMatch or int(tmpMatch.group(1)) <= 0:
|
|
1671
|
+
return False
|
|
1672
|
+
return True
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
# check if push status changes without class instance
|
|
1676
|
+
def push_status_changes(split_rule):
|
|
1677
|
+
return check_split_rule_positive_int("pushStatusChanges", split_rule)
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
# check if message driven without class instance
|
|
1681
|
+
def is_msg_driven(split_rule):
|
|
1682
|
+
return check_split_rule_positive_int("messageDriven", split_rule)
|
|
1683
|
+
|
|
1684
|
+
|
|
1685
|
+
# check if auto pause is disabled
|
|
1686
|
+
def is_auto_pause_disabled(split_rule):
|
|
1687
|
+
return not check_split_rule_positive_int("noAutoPause", split_rule)
|