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,1154 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
from pandacommon.pandalogger.PandaLogger import PandaLogger
|
|
10
|
+
from pandacommon.pandautils.PandaUtils import naive_utcnow
|
|
11
|
+
from pandacommon.pandautils.thread_utils import GenericThread
|
|
12
|
+
|
|
13
|
+
import pandaserver.taskbuffer.ErrorCode
|
|
14
|
+
from pandaserver.brokerage.SiteMapper import SiteMapper
|
|
15
|
+
from pandaserver.config import panda_config
|
|
16
|
+
from pandaserver.dataservice import DataServiceUtils
|
|
17
|
+
from pandaserver.dataservice.closer import Closer
|
|
18
|
+
from pandaserver.dataservice.DataServiceUtils import select_scope
|
|
19
|
+
from pandaserver.dataservice.ddm import rucioAPI
|
|
20
|
+
from pandaserver.dataservice.finisher import Finisher
|
|
21
|
+
from pandaserver.taskbuffer import EventServiceUtils
|
|
22
|
+
|
|
23
|
+
_logger = PandaLogger().getLogger("datasetManager")
|
|
24
|
+
|
|
25
|
+
TRANSFER_TIMEOUT_HI_PRIORITY = 2
|
|
26
|
+
TRANSFER_TIMEOUT_LO_PRIORITY = 6
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main(tbuf=None, **kwargs):
|
|
30
|
+
_logger.debug("===================== start =====================")
|
|
31
|
+
|
|
32
|
+
# memory checker
|
|
33
|
+
def _memoryCheck(str):
|
|
34
|
+
try:
|
|
35
|
+
proc_status = f"/proc/{os.getpid()}/status"
|
|
36
|
+
procfile = open(proc_status)
|
|
37
|
+
name = ""
|
|
38
|
+
vmSize = ""
|
|
39
|
+
vmRSS = ""
|
|
40
|
+
# extract Name,VmSize,VmRSS
|
|
41
|
+
for line in procfile:
|
|
42
|
+
if line.startswith("Name:"):
|
|
43
|
+
name = line.split()[-1]
|
|
44
|
+
continue
|
|
45
|
+
if line.startswith("VmSize:"):
|
|
46
|
+
vmSize = ""
|
|
47
|
+
for item in line.split()[1:]:
|
|
48
|
+
vmSize += item
|
|
49
|
+
continue
|
|
50
|
+
if line.startswith("VmRSS:"):
|
|
51
|
+
vmRSS = ""
|
|
52
|
+
for item in line.split()[1:]:
|
|
53
|
+
vmRSS += item
|
|
54
|
+
continue
|
|
55
|
+
procfile.close()
|
|
56
|
+
_logger.debug(f"MemCheck - {os.getpid()} Name={name} VSZ={vmSize} RSS={vmRSS} : {str}")
|
|
57
|
+
except Exception:
|
|
58
|
+
type, value, traceBack = sys.exc_info()
|
|
59
|
+
_logger.error(f"memoryCheck() : {type} {value}")
|
|
60
|
+
_logger.debug(f"MemCheck - {os.getpid()} unknown : {str}")
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
_memoryCheck("start")
|
|
64
|
+
|
|
65
|
+
from pandaserver.taskbuffer.TaskBuffer import taskBuffer
|
|
66
|
+
|
|
67
|
+
requester_id = GenericThread().get_full_id(__name__, sys.modules[__name__].__file__)
|
|
68
|
+
taskBuffer.init(
|
|
69
|
+
panda_config.dbhost,
|
|
70
|
+
panda_config.dbpasswd,
|
|
71
|
+
nDBConnection=1,
|
|
72
|
+
useTimeout=True,
|
|
73
|
+
requester=requester_id,
|
|
74
|
+
)
|
|
75
|
+
# else:
|
|
76
|
+
# taskBuffer = tbuf
|
|
77
|
+
|
|
78
|
+
# instantiate sitemapper
|
|
79
|
+
siteMapper = SiteMapper(taskBuffer)
|
|
80
|
+
|
|
81
|
+
# list with lock
|
|
82
|
+
class ListWithLock:
|
|
83
|
+
def __init__(self):
|
|
84
|
+
self.lock = threading.Lock()
|
|
85
|
+
self.list = []
|
|
86
|
+
|
|
87
|
+
def __contains__(self, item):
|
|
88
|
+
self.lock.acquire()
|
|
89
|
+
ret = self.list.__contains__(item)
|
|
90
|
+
self.lock.release()
|
|
91
|
+
return ret
|
|
92
|
+
|
|
93
|
+
def append(self, item):
|
|
94
|
+
appended = False
|
|
95
|
+
self.lock.acquire()
|
|
96
|
+
if item not in self.list:
|
|
97
|
+
self.list.append(item)
|
|
98
|
+
appended = True
|
|
99
|
+
self.lock.release()
|
|
100
|
+
return appended
|
|
101
|
+
|
|
102
|
+
# list of dis datasets to be deleted
|
|
103
|
+
deletedDisList = ListWithLock()
|
|
104
|
+
|
|
105
|
+
# set tobedeleted to dis dataset
|
|
106
|
+
def setTobeDeletedToDis(subDsName):
|
|
107
|
+
try:
|
|
108
|
+
# only production sub datasets
|
|
109
|
+
if subDsName.startswith("user") or subDsName.startswith("group") or re.search("_sub\d+$", subDsName) is None:
|
|
110
|
+
return
|
|
111
|
+
# get _dis names with _sub
|
|
112
|
+
disNameList = taskBuffer.getAssociatedDisDatasets(subDsName)
|
|
113
|
+
_logger.debug(f"setTobeDeletedToDis : sub:{subDsName} has dis:{str(disNameList)}")
|
|
114
|
+
# loop over all _dis datasets
|
|
115
|
+
for tmpDisName in disNameList:
|
|
116
|
+
# try to append to locked list
|
|
117
|
+
if not deletedDisList.append(tmpDisName):
|
|
118
|
+
# another thread already took care of the _dis
|
|
119
|
+
continue
|
|
120
|
+
# skip non _dis
|
|
121
|
+
if re.search("_dis\d+$", tmpDisName) is None:
|
|
122
|
+
continue
|
|
123
|
+
# get dataset
|
|
124
|
+
_logger.debug(f"setTobeDeletedToDis : try to get {tmpDisName} in DB")
|
|
125
|
+
tmpDS = taskBuffer.queryDatasetWithMap({"name": tmpDisName})
|
|
126
|
+
if tmpDS is None:
|
|
127
|
+
_logger.error(f"setTobeDeletedToDis : cannot get {tmpDisName} in DB")
|
|
128
|
+
continue
|
|
129
|
+
# check status
|
|
130
|
+
if tmpDS.status in ["tobedeleted", "deleted"]:
|
|
131
|
+
_logger.debug(f"setTobeDeletedToDis : skip {tmpDisName} since status={tmpDS.status}")
|
|
132
|
+
continue
|
|
133
|
+
# check the number of failed jobs associated to the _dis
|
|
134
|
+
if tmpDS.currentfiles == 0:
|
|
135
|
+
# all succeeded
|
|
136
|
+
tmpDS.status = "deleting"
|
|
137
|
+
excStatus = "deleted"
|
|
138
|
+
else:
|
|
139
|
+
# some failed, to reduce the lifetime
|
|
140
|
+
tmpDS.status = "shortening"
|
|
141
|
+
excStatus = "shortened"
|
|
142
|
+
# update dataset
|
|
143
|
+
retU = taskBuffer.updateDatasets(
|
|
144
|
+
[tmpDS],
|
|
145
|
+
withLock=True,
|
|
146
|
+
withCriteria="status<>:crStatus",
|
|
147
|
+
criteriaMap={":crStatus": excStatus},
|
|
148
|
+
)
|
|
149
|
+
_logger.debug(f"setTobeDeletedToDis : set {tmpDS.status} to {tmpDisName} with {str(retU)}")
|
|
150
|
+
except Exception:
|
|
151
|
+
errType, errValue = sys.exc_info()[:2]
|
|
152
|
+
_logger.error(f"setTobeDeletedToDis : {subDsName} {errType} {errValue}")
|
|
153
|
+
|
|
154
|
+
# thread pool
|
|
155
|
+
class ThreadPool:
|
|
156
|
+
def __init__(self):
|
|
157
|
+
self.lock = threading.Lock()
|
|
158
|
+
self.list = []
|
|
159
|
+
|
|
160
|
+
def add(self, obj):
|
|
161
|
+
self.lock.acquire()
|
|
162
|
+
self.list.append(obj)
|
|
163
|
+
self.lock.release()
|
|
164
|
+
|
|
165
|
+
def remove(self, obj):
|
|
166
|
+
self.lock.acquire()
|
|
167
|
+
self.list.remove(obj)
|
|
168
|
+
self.lock.release()
|
|
169
|
+
|
|
170
|
+
def join(self):
|
|
171
|
+
self.lock.acquire()
|
|
172
|
+
thrlist = tuple(self.list)
|
|
173
|
+
self.lock.release()
|
|
174
|
+
for thr in thrlist:
|
|
175
|
+
thr.join()
|
|
176
|
+
|
|
177
|
+
# thread to close dataset
|
|
178
|
+
class CloserThr(threading.Thread):
|
|
179
|
+
def __init__(self, lock, proxyLock, datasets, pool):
|
|
180
|
+
threading.Thread.__init__(self)
|
|
181
|
+
self.datasets = datasets
|
|
182
|
+
self.lock = lock
|
|
183
|
+
self.proxyLock = proxyLock
|
|
184
|
+
self.pool = pool
|
|
185
|
+
self.pool.add(self)
|
|
186
|
+
|
|
187
|
+
def run(self):
|
|
188
|
+
self.lock.acquire()
|
|
189
|
+
try:
|
|
190
|
+
# loop over all datasets
|
|
191
|
+
for vuid, name, modDate in self.datasets:
|
|
192
|
+
_logger.debug(f"Close {modDate} {name}")
|
|
193
|
+
dsExists = True
|
|
194
|
+
if name.startswith("user.") or name.startswith("group.") or name.startswith("hc_test.") or name.startswith("panda.um."):
|
|
195
|
+
dsExists = False
|
|
196
|
+
if dsExists:
|
|
197
|
+
# check if dataset exists
|
|
198
|
+
status, out = rucioAPI.get_metadata(name)
|
|
199
|
+
if status is True:
|
|
200
|
+
if out is not None:
|
|
201
|
+
try:
|
|
202
|
+
rucioAPI.close_dataset(name)
|
|
203
|
+
status = True
|
|
204
|
+
except Exception:
|
|
205
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
206
|
+
out = f"failed to freeze : {errtype} {errvalue}"
|
|
207
|
+
status = False
|
|
208
|
+
else:
|
|
209
|
+
# dataset not exist
|
|
210
|
+
status, out = True, ""
|
|
211
|
+
dsExists = False
|
|
212
|
+
else:
|
|
213
|
+
status, out = True, ""
|
|
214
|
+
if not status:
|
|
215
|
+
_logger.error(f"{name} failed to close with {out}")
|
|
216
|
+
else:
|
|
217
|
+
self.proxyLock.acquire()
|
|
218
|
+
varMap = {}
|
|
219
|
+
varMap[":vuid"] = vuid
|
|
220
|
+
varMap[":newstatus"] = "completed"
|
|
221
|
+
varMap[":oldstatus"] = "tobeclosed"
|
|
222
|
+
taskBuffer.querySQLS(
|
|
223
|
+
"UPDATE ATLAS_PANDA.Datasets SET status=:newstatus,modificationdate=CURRENT_DATE WHERE vuid=:vuid AND status=:oldstatus",
|
|
224
|
+
varMap,
|
|
225
|
+
)
|
|
226
|
+
self.proxyLock.release()
|
|
227
|
+
# set tobedeleted to dis
|
|
228
|
+
setTobeDeletedToDis(name)
|
|
229
|
+
# skip if dataset is not real
|
|
230
|
+
if not dsExists:
|
|
231
|
+
continue
|
|
232
|
+
# count # of files
|
|
233
|
+
status, out = rucioAPI.get_number_of_files(name)
|
|
234
|
+
if status is not True:
|
|
235
|
+
if status is False:
|
|
236
|
+
_logger.error(out)
|
|
237
|
+
else:
|
|
238
|
+
_logger.debug(out)
|
|
239
|
+
try:
|
|
240
|
+
nFile = int(out)
|
|
241
|
+
if nFile == 0:
|
|
242
|
+
# erase dataset
|
|
243
|
+
_logger.debug(f"erase {name}")
|
|
244
|
+
status, out = rucioAPI.erase_dataset(name)
|
|
245
|
+
_logger.debug(f"OK with {name}")
|
|
246
|
+
except Exception:
|
|
247
|
+
pass
|
|
248
|
+
except Exception:
|
|
249
|
+
pass
|
|
250
|
+
self.pool.remove(self)
|
|
251
|
+
self.lock.release()
|
|
252
|
+
|
|
253
|
+
# close datasets
|
|
254
|
+
_logger.debug("==== close datasets ====")
|
|
255
|
+
timeLimitU = naive_utcnow() - datetime.timedelta(minutes=1)
|
|
256
|
+
timeLimitL = naive_utcnow() - datetime.timedelta(days=3)
|
|
257
|
+
closeLock = threading.Semaphore(5)
|
|
258
|
+
closeProxyLock = threading.Lock()
|
|
259
|
+
closeThreadPool = ThreadPool()
|
|
260
|
+
maxRows = 100000
|
|
261
|
+
while True:
|
|
262
|
+
# lock
|
|
263
|
+
closeLock.acquire()
|
|
264
|
+
# get datasets
|
|
265
|
+
closeProxyLock.acquire()
|
|
266
|
+
varMap = {}
|
|
267
|
+
varMap[":modificationdateU"] = timeLimitU
|
|
268
|
+
varMap[":modificationdateL"] = timeLimitL
|
|
269
|
+
varMap[":type"] = "output"
|
|
270
|
+
varMap[":status"] = "tobeclosed"
|
|
271
|
+
sqlQuery = f"type=:type AND status=:status AND (modificationdate BETWEEN :modificationdateL AND :modificationdateU) AND rownum <= {maxRows}"
|
|
272
|
+
res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
|
|
273
|
+
if res is None:
|
|
274
|
+
_logger.debug(f"# of datasets to be closed: {res}")
|
|
275
|
+
else:
|
|
276
|
+
_logger.debug(f"# of datasets to be closed: {len(res)}")
|
|
277
|
+
if res is None or len(res) == 0:
|
|
278
|
+
closeProxyLock.release()
|
|
279
|
+
closeLock.release()
|
|
280
|
+
break
|
|
281
|
+
# release
|
|
282
|
+
closeProxyLock.release()
|
|
283
|
+
closeLock.release()
|
|
284
|
+
# run thread
|
|
285
|
+
iRows = 0
|
|
286
|
+
nRows = 500
|
|
287
|
+
while iRows < len(res):
|
|
288
|
+
closerThr = CloserThr(closeLock, closeProxyLock, res[iRows : iRows + nRows], closeThreadPool)
|
|
289
|
+
closerThr.start()
|
|
290
|
+
iRows += nRows
|
|
291
|
+
closeThreadPool.join()
|
|
292
|
+
if len(res) < maxRows:
|
|
293
|
+
break
|
|
294
|
+
|
|
295
|
+
# thread to freeze dataset
|
|
296
|
+
class Freezer(threading.Thread):
|
|
297
|
+
def __init__(self, lock, proxyLock, datasets, pool):
|
|
298
|
+
threading.Thread.__init__(self)
|
|
299
|
+
self.datasets = datasets
|
|
300
|
+
self.lock = lock
|
|
301
|
+
self.proxyLock = proxyLock
|
|
302
|
+
self.pool = pool
|
|
303
|
+
self.pool.add(self)
|
|
304
|
+
|
|
305
|
+
def run(self):
|
|
306
|
+
self.lock.acquire()
|
|
307
|
+
try:
|
|
308
|
+
for vuid, name, modDate in self.datasets:
|
|
309
|
+
_logger.debug(f"Freezer start {modDate} {name}")
|
|
310
|
+
self.proxyLock.acquire()
|
|
311
|
+
retF, resF = taskBuffer.querySQLS(
|
|
312
|
+
"SELECT /*+ index(tab FILESTABLE4_DESTDBLOCK_IDX) */ PandaID,status FROM ATLAS_PANDA.filesTable4 tab WHERE destinationDBlock=:destinationDBlock ",
|
|
313
|
+
{":destinationDBlock": name},
|
|
314
|
+
)
|
|
315
|
+
self.proxyLock.release()
|
|
316
|
+
if isinstance(retF, int) and retF < 0:
|
|
317
|
+
_logger.error("SQL error")
|
|
318
|
+
else:
|
|
319
|
+
allFinished = True
|
|
320
|
+
onePandaID = None
|
|
321
|
+
for tmpPandaID, tmpFileStatus in resF:
|
|
322
|
+
onePandaID = tmpPandaID
|
|
323
|
+
if tmpFileStatus not in [
|
|
324
|
+
"ready",
|
|
325
|
+
"failed",
|
|
326
|
+
"skipped",
|
|
327
|
+
"merging",
|
|
328
|
+
"finished",
|
|
329
|
+
]:
|
|
330
|
+
allFinished = False
|
|
331
|
+
break
|
|
332
|
+
# check sub datasets in the jobset for event service job
|
|
333
|
+
if allFinished:
|
|
334
|
+
self.proxyLock.acquire()
|
|
335
|
+
tmpJobs = taskBuffer.getFullJobStatus([onePandaID])
|
|
336
|
+
self.proxyLock.release()
|
|
337
|
+
if len(tmpJobs) > 0 and tmpJobs[0] is not None:
|
|
338
|
+
if EventServiceUtils.isEventServiceMerge(tmpJobs[0]):
|
|
339
|
+
self.proxyLock.acquire()
|
|
340
|
+
cThr = Closer(taskBuffer, [], tmpJobs[0])
|
|
341
|
+
allFinished = cThr.checkSubDatasetsInJobset()
|
|
342
|
+
self.proxyLock.release()
|
|
343
|
+
_logger.debug(f"closer checked sub datasets in the jobset for {name} : {allFinished}")
|
|
344
|
+
# no files in filesTable
|
|
345
|
+
if allFinished:
|
|
346
|
+
_logger.debug(f"freeze {name} ")
|
|
347
|
+
dsExists = True
|
|
348
|
+
if name.startswith("user.") or name.startswith("group.") or name.startswith("hc_test.") or name.startswith("panda.um."):
|
|
349
|
+
dsExists = False
|
|
350
|
+
if name.startswith("panda.um."):
|
|
351
|
+
self.proxyLock.acquire()
|
|
352
|
+
retMer, resMer = taskBuffer.querySQLS(
|
|
353
|
+
"SELECT /*+ index(tab FILESTABLE4_DESTDBLOCK_IDX) */ PandaID FROM ATLAS_PANDA.filesTable4 tab WHERE destinationDBlock=:destinationDBlock AND status IN (:statusM,:statusF) ",
|
|
354
|
+
{
|
|
355
|
+
":destinationDBlock": name,
|
|
356
|
+
":statusM": "merging",
|
|
357
|
+
":statusF": "failed",
|
|
358
|
+
},
|
|
359
|
+
)
|
|
360
|
+
self.proxyLock.release()
|
|
361
|
+
if resMer is not None and len(resMer) > 0:
|
|
362
|
+
mergeID = resMer[0][0]
|
|
363
|
+
# get merging jobs
|
|
364
|
+
self.proxyLock.acquire()
|
|
365
|
+
mergingJobs = taskBuffer.peekJobs(
|
|
366
|
+
[mergeID],
|
|
367
|
+
fromDefined=False,
|
|
368
|
+
fromArchived=False,
|
|
369
|
+
fromWaiting=False,
|
|
370
|
+
)
|
|
371
|
+
self.proxyLock.release()
|
|
372
|
+
mergeJob = mergingJobs[0]
|
|
373
|
+
if mergeJob is not None:
|
|
374
|
+
tmpDestDBlocks = []
|
|
375
|
+
# get destDBlock
|
|
376
|
+
for tmpFile in mergeJob.Files:
|
|
377
|
+
if tmpFile.type in ["output", "log"]:
|
|
378
|
+
if tmpFile.destinationDBlock not in tmpDestDBlocks:
|
|
379
|
+
tmpDestDBlocks.append(tmpFile.destinationDBlock)
|
|
380
|
+
# run
|
|
381
|
+
_logger.debug(f"start JEDI closer for {name} ")
|
|
382
|
+
self.proxyLock.acquire()
|
|
383
|
+
cThr = Closer(taskBuffer, tmpDestDBlocks, mergeJob)
|
|
384
|
+
cThr.run()
|
|
385
|
+
self.proxyLock.release()
|
|
386
|
+
_logger.debug(f"end JEDI closer for {name} ")
|
|
387
|
+
continue
|
|
388
|
+
else:
|
|
389
|
+
_logger.debug(f"failed to get merging job for {name} ")
|
|
390
|
+
else:
|
|
391
|
+
_logger.debug(f"failed to get merging file for {name} ")
|
|
392
|
+
status, out = True, ""
|
|
393
|
+
elif dsExists:
|
|
394
|
+
# check if dataset exists
|
|
395
|
+
status, out = rucioAPI.get_metadata(name)
|
|
396
|
+
if status is True:
|
|
397
|
+
if out is not None:
|
|
398
|
+
try:
|
|
399
|
+
rucioAPI.close_dataset(name)
|
|
400
|
+
status = True
|
|
401
|
+
except Exception:
|
|
402
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
403
|
+
out = f"failed to freeze : {errtype} {errvalue}"
|
|
404
|
+
status = False
|
|
405
|
+
else:
|
|
406
|
+
# dataset not exist
|
|
407
|
+
status, out = True, ""
|
|
408
|
+
dsExists = False
|
|
409
|
+
else:
|
|
410
|
+
status, out = True, ""
|
|
411
|
+
if not status:
|
|
412
|
+
_logger.error(f"{name} failed to freeze with {out}")
|
|
413
|
+
else:
|
|
414
|
+
self.proxyLock.acquire()
|
|
415
|
+
varMap = {}
|
|
416
|
+
varMap[":vuid"] = vuid
|
|
417
|
+
varMap[":status"] = "completed"
|
|
418
|
+
taskBuffer.querySQLS(
|
|
419
|
+
"UPDATE ATLAS_PANDA.Datasets SET status=:status,modificationdate=CURRENT_DATE WHERE vuid=:vuid",
|
|
420
|
+
varMap,
|
|
421
|
+
)
|
|
422
|
+
self.proxyLock.release()
|
|
423
|
+
if name.startswith("panda.um.") or not dsExists:
|
|
424
|
+
continue
|
|
425
|
+
# set tobedeleted to dis
|
|
426
|
+
setTobeDeletedToDis(name)
|
|
427
|
+
# count # of files
|
|
428
|
+
status, out = rucioAPI.get_number_of_files(name)
|
|
429
|
+
if status is not True:
|
|
430
|
+
if status is False:
|
|
431
|
+
_logger.error(out)
|
|
432
|
+
else:
|
|
433
|
+
_logger.debug(out)
|
|
434
|
+
try:
|
|
435
|
+
nFile = int(out)
|
|
436
|
+
_logger.debug(nFile)
|
|
437
|
+
if nFile == 0:
|
|
438
|
+
# erase dataset
|
|
439
|
+
_logger.debug(f"erase {name}")
|
|
440
|
+
status, out = rucioAPI.erase_dataset(name)
|
|
441
|
+
_logger.debug(f"OK with {name}")
|
|
442
|
+
except Exception:
|
|
443
|
+
pass
|
|
444
|
+
else:
|
|
445
|
+
_logger.debug(f"wait {name} ")
|
|
446
|
+
self.proxyLock.acquire()
|
|
447
|
+
taskBuffer.querySQLS(
|
|
448
|
+
"UPDATE ATLAS_PANDA.Datasets SET modificationdate=CURRENT_DATE WHERE vuid=:vuid",
|
|
449
|
+
{":vuid": vuid},
|
|
450
|
+
)
|
|
451
|
+
self.proxyLock.release()
|
|
452
|
+
_logger.debug(f"end {name} ")
|
|
453
|
+
except Exception:
|
|
454
|
+
errStr = traceback.format_exc()
|
|
455
|
+
_logger.error(errStr)
|
|
456
|
+
self.pool.remove(self)
|
|
457
|
+
self.lock.release()
|
|
458
|
+
|
|
459
|
+
# freeze dataset
|
|
460
|
+
_logger.debug("==== freeze datasets ====")
|
|
461
|
+
timeLimitRU = naive_utcnow() - datetime.timedelta(hours=3)
|
|
462
|
+
timeLimitRL = naive_utcnow() - datetime.timedelta(hours=12)
|
|
463
|
+
timeLimitU = naive_utcnow() - datetime.timedelta(hours=6)
|
|
464
|
+
timeLimitL = naive_utcnow() - datetime.timedelta(days=14)
|
|
465
|
+
# reset doing so that Closer can update unmerged datasets
|
|
466
|
+
sql = "SELECT name FROM ATLAS_PANDA.Datasets "
|
|
467
|
+
sql += "WHERE type=:type AND (modificationdate BETWEEN :modificationdateRL AND :modificationdateRU) AND subType=:subType AND status=:oldStatus "
|
|
468
|
+
varMap = {}
|
|
469
|
+
varMap[":modificationdateRU"] = timeLimitRU
|
|
470
|
+
varMap[":modificationdateRL"] = timeLimitRL
|
|
471
|
+
varMap[":type"] = "output"
|
|
472
|
+
varMap[":subType"] = "sub"
|
|
473
|
+
varMap[":oldStatus"] = "doing"
|
|
474
|
+
retReset, resReset = taskBuffer.querySQLS(sql, varMap)
|
|
475
|
+
sql = "UPDATE ATLAS_PANDA.Datasets SET status=:newStatus,modificationdate=:modificationdateU WHERE name=:name AND status=:oldStatus "
|
|
476
|
+
if resReset is not None:
|
|
477
|
+
for (name,) in resReset:
|
|
478
|
+
varMap = {}
|
|
479
|
+
varMap[":name"] = name
|
|
480
|
+
varMap[":oldStatus"] = "doing"
|
|
481
|
+
varMap[":newStatus"] = "running"
|
|
482
|
+
varMap[":modificationdateU"] = timeLimitU
|
|
483
|
+
_logger.debug(f"reset {name} to freeze")
|
|
484
|
+
taskBuffer.querySQLS(sql, varMap)
|
|
485
|
+
# loop for freezer
|
|
486
|
+
freezeLock = threading.Semaphore(5)
|
|
487
|
+
freezeProxyLock = threading.Lock()
|
|
488
|
+
freezeThreadPool = ThreadPool()
|
|
489
|
+
maxRows = 100000
|
|
490
|
+
while True:
|
|
491
|
+
# lock
|
|
492
|
+
freezeLock.acquire()
|
|
493
|
+
# get datasets
|
|
494
|
+
sqlQuery = (
|
|
495
|
+
"type=:type AND status IN (:status1,:status2,:status3,:status4,:status5) "
|
|
496
|
+
+ f"AND (modificationdate BETWEEN :modificationdateL AND :modificationdateU) AND subType=:subType AND rownum <= {maxRows}"
|
|
497
|
+
)
|
|
498
|
+
varMap = {}
|
|
499
|
+
varMap[":modificationdateU"] = timeLimitU
|
|
500
|
+
varMap[":modificationdateL"] = timeLimitL
|
|
501
|
+
varMap[":type"] = "output"
|
|
502
|
+
varMap[":status1"] = "running"
|
|
503
|
+
varMap[":status2"] = "created"
|
|
504
|
+
varMap[":status3"] = "defined"
|
|
505
|
+
varMap[":status4"] = "locked"
|
|
506
|
+
varMap[":status5"] = "doing"
|
|
507
|
+
varMap[":subType"] = "sub"
|
|
508
|
+
freezeProxyLock.acquire()
|
|
509
|
+
res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
|
|
510
|
+
if res is None:
|
|
511
|
+
_logger.debug(f"# of datasets to be frozen: {res}")
|
|
512
|
+
else:
|
|
513
|
+
_logger.debug(f"# of datasets to be frozen: {len(res)}")
|
|
514
|
+
if res is None or len(res) == 0:
|
|
515
|
+
freezeProxyLock.release()
|
|
516
|
+
freezeLock.release()
|
|
517
|
+
break
|
|
518
|
+
freezeProxyLock.release()
|
|
519
|
+
# release
|
|
520
|
+
freezeLock.release()
|
|
521
|
+
# run freezer
|
|
522
|
+
iRows = 0
|
|
523
|
+
nRows = 500
|
|
524
|
+
while iRows < len(res):
|
|
525
|
+
freezer = Freezer(
|
|
526
|
+
freezeLock,
|
|
527
|
+
freezeProxyLock,
|
|
528
|
+
res[iRows : iRows + nRows],
|
|
529
|
+
freezeThreadPool,
|
|
530
|
+
)
|
|
531
|
+
freezer.start()
|
|
532
|
+
iRows += nRows
|
|
533
|
+
freezeThreadPool.join()
|
|
534
|
+
if len(res) < maxRows:
|
|
535
|
+
break
|
|
536
|
+
|
|
537
|
+
# delete dis datasets
|
|
538
|
+
class EraserThr(threading.Thread):
|
|
539
|
+
def __init__(self, lock, proxyLock, datasets, pool, operationType):
|
|
540
|
+
threading.Thread.__init__(self)
|
|
541
|
+
self.datasets = datasets
|
|
542
|
+
self.lock = lock
|
|
543
|
+
self.proxyLock = proxyLock
|
|
544
|
+
self.pool = pool
|
|
545
|
+
self.pool.add(self)
|
|
546
|
+
self.operationType = operationType
|
|
547
|
+
|
|
548
|
+
def run(self):
|
|
549
|
+
self.lock.acquire()
|
|
550
|
+
try:
|
|
551
|
+
# loop over all datasets
|
|
552
|
+
for vuid, name, modDate in self.datasets:
|
|
553
|
+
# only dis datasets
|
|
554
|
+
if re.search("_dis\d+$", name) is None:
|
|
555
|
+
_logger.error(f"Eraser : non disDS {name}")
|
|
556
|
+
continue
|
|
557
|
+
# delete
|
|
558
|
+
_logger.debug(f"Eraser {self.operationType} dis {modDate} {name}")
|
|
559
|
+
# delete or shorten
|
|
560
|
+
endStatus = "deleted"
|
|
561
|
+
status, out = rucioAPI.erase_dataset(name)
|
|
562
|
+
if not status:
|
|
563
|
+
_logger.error(out)
|
|
564
|
+
continue
|
|
565
|
+
_logger.debug(f"OK with {name}")
|
|
566
|
+
# update
|
|
567
|
+
self.proxyLock.acquire()
|
|
568
|
+
varMap = {}
|
|
569
|
+
varMap[":vuid"] = vuid
|
|
570
|
+
varMap[":status"] = endStatus
|
|
571
|
+
taskBuffer.querySQLS(
|
|
572
|
+
"UPDATE ATLAS_PANDA.Datasets SET status=:status,modificationdate=CURRENT_DATE WHERE vuid=:vuid",
|
|
573
|
+
varMap,
|
|
574
|
+
)
|
|
575
|
+
self.proxyLock.release()
|
|
576
|
+
except Exception:
|
|
577
|
+
errStr = traceback.format_exc()
|
|
578
|
+
_logger.error(errStr)
|
|
579
|
+
self.pool.remove(self)
|
|
580
|
+
self.lock.release()
|
|
581
|
+
|
|
582
|
+
# delete dis datasets
|
|
583
|
+
_logger.debug("==== delete dis datasets ====")
|
|
584
|
+
timeLimitU = naive_utcnow() - datetime.timedelta(minutes=30)
|
|
585
|
+
timeLimitL = naive_utcnow() - datetime.timedelta(days=3)
|
|
586
|
+
disEraseLock = threading.Semaphore(5)
|
|
587
|
+
disEraseProxyLock = threading.Lock()
|
|
588
|
+
disEraseThreadPool = ThreadPool()
|
|
589
|
+
# maxRows = 100000
|
|
590
|
+
maxRows = 5000
|
|
591
|
+
for targetStatus in ["deleting", "shortening"]:
|
|
592
|
+
for i in range(10):
|
|
593
|
+
# lock
|
|
594
|
+
disEraseLock.acquire()
|
|
595
|
+
# get datasets
|
|
596
|
+
varMap = {}
|
|
597
|
+
varMap[":modificationdateU"] = timeLimitU
|
|
598
|
+
varMap[":modificationdateL"] = timeLimitL
|
|
599
|
+
varMap[":type"] = "dispatch"
|
|
600
|
+
varMap[":status"] = targetStatus
|
|
601
|
+
sqlQuery = f"type=:type AND status=:status AND (modificationdate BETWEEN :modificationdateL AND :modificationdateU) AND rownum <= {maxRows}"
|
|
602
|
+
disEraseProxyLock.acquire()
|
|
603
|
+
res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
|
|
604
|
+
if res is None:
|
|
605
|
+
_logger.debug(f"# of dis datasets for {targetStatus}: None")
|
|
606
|
+
else:
|
|
607
|
+
_logger.debug(f"# of dis datasets for {targetStatus}: {len(res)}")
|
|
608
|
+
if res is None or len(res) == 0:
|
|
609
|
+
disEraseProxyLock.release()
|
|
610
|
+
disEraseLock.release()
|
|
611
|
+
break
|
|
612
|
+
disEraseProxyLock.release()
|
|
613
|
+
# release
|
|
614
|
+
disEraseLock.release()
|
|
615
|
+
# run disEraser
|
|
616
|
+
iRows = 0
|
|
617
|
+
nRows = 500
|
|
618
|
+
while iRows < len(res):
|
|
619
|
+
disEraser = EraserThr(
|
|
620
|
+
disEraseLock,
|
|
621
|
+
disEraseProxyLock,
|
|
622
|
+
res[iRows : iRows + nRows],
|
|
623
|
+
disEraseThreadPool,
|
|
624
|
+
targetStatus,
|
|
625
|
+
)
|
|
626
|
+
disEraser.start()
|
|
627
|
+
iRows += nRows
|
|
628
|
+
disEraseThreadPool.join()
|
|
629
|
+
if len(res) < 100:
|
|
630
|
+
break
|
|
631
|
+
|
|
632
|
+
_memoryCheck("finisher")
|
|
633
|
+
|
|
634
|
+
# finisher thread
|
|
635
|
+
class FinisherThr(threading.Thread):
|
|
636
|
+
def __init__(self, lock, proxyLock, ids, pool, timeNow):
|
|
637
|
+
threading.Thread.__init__(self)
|
|
638
|
+
self.ids = ids
|
|
639
|
+
self.lock = lock
|
|
640
|
+
self.proxyLock = proxyLock
|
|
641
|
+
self.pool = pool
|
|
642
|
+
self.timeNow = timeNow
|
|
643
|
+
self.pool.add(self)
|
|
644
|
+
|
|
645
|
+
def run(self):
|
|
646
|
+
self.lock.acquire()
|
|
647
|
+
try:
|
|
648
|
+
# get jobs from DB
|
|
649
|
+
ids = self.ids
|
|
650
|
+
self.proxyLock.acquire()
|
|
651
|
+
jobs = taskBuffer.peekJobs(ids, fromDefined=False, fromArchived=False, fromWaiting=False)
|
|
652
|
+
self.proxyLock.release()
|
|
653
|
+
upJobs = []
|
|
654
|
+
finJobs = []
|
|
655
|
+
for job in jobs:
|
|
656
|
+
if job is None or job.jobStatus == "unknown":
|
|
657
|
+
continue
|
|
658
|
+
seList = ["dummy"]
|
|
659
|
+
tmpNucleus = siteMapper.getNucleus(job.nucleus)
|
|
660
|
+
# get SEs
|
|
661
|
+
if job.prodSourceLabel == "user" and job.destinationSE not in siteMapper.siteSpecList:
|
|
662
|
+
# using --destSE for analysis job to transfer output
|
|
663
|
+
seList = [job.destinationSE]
|
|
664
|
+
elif tmpNucleus is not None:
|
|
665
|
+
seList = [tmpNucleus.default_ddm_endpoint_out]
|
|
666
|
+
|
|
667
|
+
# get LFN list
|
|
668
|
+
lfns = []
|
|
669
|
+
guids = []
|
|
670
|
+
scopes = []
|
|
671
|
+
nTokens = 0
|
|
672
|
+
for file in job.Files:
|
|
673
|
+
# only output files are checked
|
|
674
|
+
if file.type == "output" or file.type == "log":
|
|
675
|
+
if file.status == "nooutput":
|
|
676
|
+
continue
|
|
677
|
+
if DataServiceUtils.getDistributedDestination(file.destinationDBlockToken) is not None:
|
|
678
|
+
continue
|
|
679
|
+
lfns.append(file.lfn)
|
|
680
|
+
guids.append(file.GUID)
|
|
681
|
+
scopes.append(file.scope)
|
|
682
|
+
nTokens += len(file.destinationDBlockToken.split(","))
|
|
683
|
+
# get files
|
|
684
|
+
_logger.debug(f"{job.PandaID} Cloud:{job.cloud}")
|
|
685
|
+
tmpStat, okFiles = rucioAPI.list_file_replicas(scopes, lfns, seList)
|
|
686
|
+
if not tmpStat:
|
|
687
|
+
_logger.error(f"{job.PandaID} failed to get file replicas")
|
|
688
|
+
okFiles = {}
|
|
689
|
+
# count files
|
|
690
|
+
nOkTokens = 0
|
|
691
|
+
for okLFN in okFiles:
|
|
692
|
+
okSEs = okFiles[okLFN]
|
|
693
|
+
nOkTokens += len(okSEs)
|
|
694
|
+
# check all files are ready
|
|
695
|
+
_logger.debug(f"{job.PandaID} nToken:{nTokens} nOkToken:{nOkTokens}")
|
|
696
|
+
if nTokens <= nOkTokens:
|
|
697
|
+
_logger.debug(f"{job.PandaID} Finisher : Finish")
|
|
698
|
+
for file in job.Files:
|
|
699
|
+
if file.type == "output" or file.type == "log":
|
|
700
|
+
if file.status != "nooutput":
|
|
701
|
+
file.status = "ready"
|
|
702
|
+
# append to run Finisher
|
|
703
|
+
finJobs.append(job)
|
|
704
|
+
else:
|
|
705
|
+
endTime = job.endTime
|
|
706
|
+
if endTime == "NULL":
|
|
707
|
+
endTime = job.startTime
|
|
708
|
+
# priority-dependent timeout
|
|
709
|
+
if job.currentPriority >= 800 and (job.prodSourceLabel not in ["user"]):
|
|
710
|
+
timeOutValue = TRANSFER_TIMEOUT_HI_PRIORITY
|
|
711
|
+
else:
|
|
712
|
+
timeOutValue = TRANSFER_TIMEOUT_LO_PRIORITY
|
|
713
|
+
|
|
714
|
+
timeOut = self.timeNow - datetime.timedelta(days=timeOutValue)
|
|
715
|
+
_logger.debug(f"{job.PandaID} Priority:{job.currentPriority} Limit:{str(timeOut)} End:{str(endTime)}")
|
|
716
|
+
if endTime < timeOut:
|
|
717
|
+
# timeout
|
|
718
|
+
_logger.debug(f"{job.PandaID} Finisher : Kill")
|
|
719
|
+
strMiss = ""
|
|
720
|
+
for lfn in lfns:
|
|
721
|
+
if lfn not in okFiles:
|
|
722
|
+
strMiss += f" {lfn}"
|
|
723
|
+
job.jobStatus = "failed"
|
|
724
|
+
job.taskBufferErrorCode = pandaserver.taskbuffer.ErrorCode.EC_Transfer
|
|
725
|
+
job.taskBufferErrorDiag = f"transfer timeout for {strMiss}"
|
|
726
|
+
guidMap = {}
|
|
727
|
+
for file in job.Files:
|
|
728
|
+
# set file status
|
|
729
|
+
if file.status == "transferring" or file.type in [
|
|
730
|
+
"log",
|
|
731
|
+
"output",
|
|
732
|
+
]:
|
|
733
|
+
file.status = "failed"
|
|
734
|
+
# collect GUIDs to delete files from _tid datasets
|
|
735
|
+
if file.type == "output" or file.type == "log":
|
|
736
|
+
if file.destinationDBlock not in guidMap:
|
|
737
|
+
guidMap[file.destinationDBlock] = []
|
|
738
|
+
guidMap[file.destinationDBlock].append(file.GUID)
|
|
739
|
+
else:
|
|
740
|
+
# wait
|
|
741
|
+
_logger.debug(f"{job.PandaID} Finisher : Wait")
|
|
742
|
+
for lfn in lfns:
|
|
743
|
+
if lfn not in okFiles:
|
|
744
|
+
_logger.debug(f"{job.PandaID} -> {lfn}")
|
|
745
|
+
upJobs.append(job)
|
|
746
|
+
# update
|
|
747
|
+
_logger.debug("updating ...")
|
|
748
|
+
self.proxyLock.acquire()
|
|
749
|
+
taskBuffer.updateJobs(upJobs, False)
|
|
750
|
+
self.proxyLock.release()
|
|
751
|
+
# run Finisher
|
|
752
|
+
for job in finJobs:
|
|
753
|
+
fThr = Finisher(taskBuffer, None, job)
|
|
754
|
+
fThr.run()
|
|
755
|
+
_logger.debug("done")
|
|
756
|
+
time.sleep(1)
|
|
757
|
+
except Exception:
|
|
758
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
759
|
+
errStr = f"FinisherThr failed with {errtype} {errvalue}"
|
|
760
|
+
errStr += traceback.format_exc()
|
|
761
|
+
_logger.error(errStr)
|
|
762
|
+
self.pool.remove(self)
|
|
763
|
+
self.lock.release()
|
|
764
|
+
|
|
765
|
+
# finish transferring jobs
|
|
766
|
+
_logger.debug("==== finish transferring jobs ====")
|
|
767
|
+
finisherLock = threading.Semaphore(3)
|
|
768
|
+
finisherProxyLock = threading.Lock()
|
|
769
|
+
finisherThreadPool = ThreadPool()
|
|
770
|
+
for loopIdx in ["low", "high"]:
|
|
771
|
+
timeNow = naive_utcnow()
|
|
772
|
+
if loopIdx == "high":
|
|
773
|
+
highPrioFlag = True
|
|
774
|
+
else:
|
|
775
|
+
highPrioFlag = False
|
|
776
|
+
# get jobs
|
|
777
|
+
for ii in range(1000):
|
|
778
|
+
# lock
|
|
779
|
+
finisherLock.acquire()
|
|
780
|
+
finisherProxyLock.acquire()
|
|
781
|
+
ret, res = taskBuffer.lockJobsForFinisher(timeNow, 200, highPrioFlag)
|
|
782
|
+
finisherProxyLock.release()
|
|
783
|
+
finisherLock.release()
|
|
784
|
+
if res is None:
|
|
785
|
+
_logger.debug(f"# of jobs to be finished for {loopIdx} : {res}")
|
|
786
|
+
else:
|
|
787
|
+
_logger.debug(f"# of jobs to be finished for {loopIdx} : {len(res)}")
|
|
788
|
+
if res is None or len(res) == 0:
|
|
789
|
+
break
|
|
790
|
+
# run thread
|
|
791
|
+
finThr = FinisherThr(finisherLock, finisherProxyLock, res, finisherThreadPool, timeNow)
|
|
792
|
+
finThr.start()
|
|
793
|
+
# wait
|
|
794
|
+
finisherThreadPool.join()
|
|
795
|
+
|
|
796
|
+
# activator thread
|
|
797
|
+
class ActivatorThr(threading.Thread):
|
|
798
|
+
def __init__(self, lock, proxyLock, ids, pool):
|
|
799
|
+
threading.Thread.__init__(self)
|
|
800
|
+
self.ids = ids
|
|
801
|
+
self.lock = lock
|
|
802
|
+
self.proxyLock = proxyLock
|
|
803
|
+
self.pool = pool
|
|
804
|
+
self.pool.add(self)
|
|
805
|
+
|
|
806
|
+
def run(self):
|
|
807
|
+
self.lock.acquire()
|
|
808
|
+
try:
|
|
809
|
+
# get jobs from DB
|
|
810
|
+
ids = self.ids
|
|
811
|
+
self.proxyLock.acquire()
|
|
812
|
+
jobs = taskBuffer.peekJobs(ids, fromActive=False, fromArchived=False, fromWaiting=False)
|
|
813
|
+
self.proxyLock.release()
|
|
814
|
+
actJobs = []
|
|
815
|
+
for tmpJob in jobs:
|
|
816
|
+
if tmpJob is None or tmpJob.jobStatus == "unknown":
|
|
817
|
+
continue
|
|
818
|
+
# get LFN list
|
|
819
|
+
lfns = []
|
|
820
|
+
guids = []
|
|
821
|
+
scopes = []
|
|
822
|
+
for tmpFile in tmpJob.Files:
|
|
823
|
+
# only input files are checked
|
|
824
|
+
if tmpFile.type == "input" and tmpFile.status != "ready":
|
|
825
|
+
lfns.append(tmpFile.lfn)
|
|
826
|
+
scopes.append(tmpFile.scope)
|
|
827
|
+
# get file replicas
|
|
828
|
+
_logger.debug(f"{tmpJob.PandaID} check input files at {tmpJob.computingSite}")
|
|
829
|
+
tmpStat, okFiles = rucioAPI.list_file_replicas(scopes, lfns)
|
|
830
|
+
if not tmpStat:
|
|
831
|
+
pass
|
|
832
|
+
else:
|
|
833
|
+
# check if locally available
|
|
834
|
+
siteSpec = siteMapper.getSite(tmpJob.computingSite)
|
|
835
|
+
scope_input, scope_output = select_scope(siteSpec, tmpJob.prodSourceLabel, tmpJob.job_label)
|
|
836
|
+
allOK = True
|
|
837
|
+
for tmpFile in tmpJob.Files:
|
|
838
|
+
# only input
|
|
839
|
+
if tmpFile.type == "input" and tmpFile.status != "ready":
|
|
840
|
+
# check RSEs
|
|
841
|
+
if tmpFile.lfn in okFiles:
|
|
842
|
+
for rse in okFiles[tmpFile.lfn]:
|
|
843
|
+
if (
|
|
844
|
+
siteSpec.ddm_endpoints_input[scope_input].isAssociated(rse)
|
|
845
|
+
and siteSpec.ddm_endpoints_input[scope_input].getEndPoint(rse)["is_tape"] == "N"
|
|
846
|
+
):
|
|
847
|
+
tmpFile.status = "ready"
|
|
848
|
+
break
|
|
849
|
+
# missing
|
|
850
|
+
if tmpFile.status != "ready":
|
|
851
|
+
allOK = False
|
|
852
|
+
_logger.debug(f"{tmpJob.PandaID} skip since {tmpFile.scope}:{tmpFile.lfn} is missing")
|
|
853
|
+
break
|
|
854
|
+
if not allOK:
|
|
855
|
+
continue
|
|
856
|
+
# append to run activator
|
|
857
|
+
_logger.debug(f"{tmpJob.PandaID} to activate")
|
|
858
|
+
actJobs.append(tmpJob)
|
|
859
|
+
# update
|
|
860
|
+
_logger.debug("activating ...")
|
|
861
|
+
self.proxyLock.acquire()
|
|
862
|
+
taskBuffer.activateJobs(actJobs)
|
|
863
|
+
self.proxyLock.release()
|
|
864
|
+
_logger.debug("done")
|
|
865
|
+
time.sleep(1)
|
|
866
|
+
except Exception:
|
|
867
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
868
|
+
_logger.error(f"ActivatorThr failed with {errtype} {errvalue}")
|
|
869
|
+
self.pool.remove(self)
|
|
870
|
+
self.lock.release()
|
|
871
|
+
|
|
872
|
+
_memoryCheck("activator")
|
|
873
|
+
|
|
874
|
+
# activate assigned jobs
|
|
875
|
+
_logger.debug("==== activate assigned jobs ====")
|
|
876
|
+
activatorLock = threading.Semaphore(3)
|
|
877
|
+
activatorProxyLock = threading.Lock()
|
|
878
|
+
activatorThreadPool = ThreadPool()
|
|
879
|
+
timeLimit = naive_utcnow() - datetime.timedelta(hours=1)
|
|
880
|
+
# get jobs
|
|
881
|
+
for ii in range(1000):
|
|
882
|
+
# lock
|
|
883
|
+
activatorLock.acquire()
|
|
884
|
+
activatorProxyLock.acquire()
|
|
885
|
+
ret, res = taskBuffer.lockJobsForActivator(timeLimit, 100, 800)
|
|
886
|
+
activatorProxyLock.release()
|
|
887
|
+
activatorLock.release()
|
|
888
|
+
if res is None:
|
|
889
|
+
_logger.debug(f"# of jobs to be activated for {res} ")
|
|
890
|
+
else:
|
|
891
|
+
_logger.debug(f"# of jobs to be activated for {len(res)} ")
|
|
892
|
+
if res is None or len(res) == 0:
|
|
893
|
+
break
|
|
894
|
+
# run thread
|
|
895
|
+
actThr = ActivatorThr(activatorLock, activatorProxyLock, res, activatorThreadPool)
|
|
896
|
+
actThr.start()
|
|
897
|
+
# wait
|
|
898
|
+
activatorThreadPool.join()
|
|
899
|
+
|
|
900
|
+
# activator thread with rule
|
|
901
|
+
class ActivatorWithRuleThr(threading.Thread):
|
|
902
|
+
def __init__(self, lock, proxyLock, ids, pool):
|
|
903
|
+
threading.Thread.__init__(self)
|
|
904
|
+
self.ids = ids
|
|
905
|
+
self.lock = lock
|
|
906
|
+
self.proxyLock = proxyLock
|
|
907
|
+
self.pool = pool
|
|
908
|
+
self.pool.add(self)
|
|
909
|
+
|
|
910
|
+
def run(self):
|
|
911
|
+
self.lock.acquire()
|
|
912
|
+
try:
|
|
913
|
+
# get jobs from DB
|
|
914
|
+
ids = self.ids
|
|
915
|
+
self.proxyLock.acquire()
|
|
916
|
+
jobs = taskBuffer.peekJobs(ids, fromActive=False, fromArchived=False, fromWaiting=False)
|
|
917
|
+
self.proxyLock.release()
|
|
918
|
+
actJobs = []
|
|
919
|
+
replicaMap = dict()
|
|
920
|
+
for tmpJob in jobs:
|
|
921
|
+
if tmpJob is None or tmpJob.jobStatus == "unknown":
|
|
922
|
+
continue
|
|
923
|
+
# check if locally available
|
|
924
|
+
siteSpec = siteMapper.getSite(tmpJob.computingSite)
|
|
925
|
+
scope_input, scope_output = select_scope(siteSpec, tmpJob.prodSourceLabel, tmpJob.job_label)
|
|
926
|
+
allOK = True
|
|
927
|
+
for tmpFile in tmpJob.Files:
|
|
928
|
+
# only input files are checked
|
|
929
|
+
if tmpFile.type == "input" and tmpFile.status != "ready":
|
|
930
|
+
# get replicas
|
|
931
|
+
if tmpFile.dispatchDBlock not in replicaMap:
|
|
932
|
+
tmpStat, repMap = rucioAPI.list_dataset_replicas(tmpFile.dispatchDBlock)
|
|
933
|
+
if tmpStat != 0:
|
|
934
|
+
repMap = {}
|
|
935
|
+
replicaMap[tmpFile.dispatchDBlock] = repMap
|
|
936
|
+
# check RSEs
|
|
937
|
+
for rse in replicaMap[tmpFile.dispatchDBlock]:
|
|
938
|
+
repInfo = replicaMap[tmpFile.dispatchDBlock][rse]
|
|
939
|
+
if (
|
|
940
|
+
siteSpec.ddm_endpoints_input[scope_input].isAssociated(rse)
|
|
941
|
+
and siteSpec.ddm_endpoints_input[scope_input].getEndPoint(rse)["is_tape"] == "N"
|
|
942
|
+
and repInfo[0]["total"] == repInfo[0]["found"]
|
|
943
|
+
and repInfo[0]["total"] is not None
|
|
944
|
+
and repInfo[0]["total"] > 0
|
|
945
|
+
):
|
|
946
|
+
tmpFile.status = "ready"
|
|
947
|
+
break
|
|
948
|
+
# missing
|
|
949
|
+
if tmpFile.status != "ready":
|
|
950
|
+
allOK = False
|
|
951
|
+
_logger.debug(f"{tmpJob.PandaID} skip since {tmpFile.scope}:{tmpFile.lfn} is missing with rule")
|
|
952
|
+
break
|
|
953
|
+
if not allOK:
|
|
954
|
+
continue
|
|
955
|
+
# append to run activator
|
|
956
|
+
_logger.debug(f"{tmpJob.PandaID} to activate with rule {str(replicaMap)}")
|
|
957
|
+
actJobs.append(tmpJob)
|
|
958
|
+
# update
|
|
959
|
+
_logger.debug("activating ...")
|
|
960
|
+
self.proxyLock.acquire()
|
|
961
|
+
taskBuffer.activateJobs(actJobs)
|
|
962
|
+
self.proxyLock.release()
|
|
963
|
+
_logger.debug("done")
|
|
964
|
+
time.sleep(1)
|
|
965
|
+
except Exception:
|
|
966
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
967
|
+
_logger.error(f"ActivatorThr failed with {errtype} {errvalue}")
|
|
968
|
+
self.pool.remove(self)
|
|
969
|
+
self.lock.release()
|
|
970
|
+
|
|
971
|
+
_memoryCheck("activator")
|
|
972
|
+
|
|
973
|
+
# activate assigned jobs
|
|
974
|
+
_logger.debug("==== activate assigned jobs with rule ====")
|
|
975
|
+
activatorLock = threading.Semaphore(3)
|
|
976
|
+
activatorProxyLock = threading.Lock()
|
|
977
|
+
activatorThreadPool = ThreadPool()
|
|
978
|
+
timeLimit = naive_utcnow() - datetime.timedelta(hours=1)
|
|
979
|
+
# get jobs
|
|
980
|
+
for ii in range(1000):
|
|
981
|
+
# lock
|
|
982
|
+
activatorLock.acquire()
|
|
983
|
+
activatorProxyLock.acquire()
|
|
984
|
+
ret, res = taskBuffer.lockJobsForActivator(timeLimit, 100, 0)
|
|
985
|
+
activatorProxyLock.release()
|
|
986
|
+
activatorLock.release()
|
|
987
|
+
if res is None:
|
|
988
|
+
_logger.debug(f"# of jobs to be activated with rule for {res} ")
|
|
989
|
+
else:
|
|
990
|
+
_logger.debug(f"# of jobs to be activated with rule for {len(res)} ")
|
|
991
|
+
if res is None or len(res) == 0:
|
|
992
|
+
break
|
|
993
|
+
# run thread
|
|
994
|
+
actThr = ActivatorWithRuleThr(activatorLock, activatorProxyLock, res, activatorThreadPool)
|
|
995
|
+
actThr.start()
|
|
996
|
+
# wait
|
|
997
|
+
activatorThreadPool.join()
|
|
998
|
+
|
|
999
|
+
# thread to delete sub datasets
|
|
1000
|
+
class SubDeleter(threading.Thread):
|
|
1001
|
+
def __init__(self, lock, proxyLock, datasets, pool):
|
|
1002
|
+
threading.Thread.__init__(self)
|
|
1003
|
+
self.datasets = datasets
|
|
1004
|
+
self.lock = lock
|
|
1005
|
+
self.proxyLock = proxyLock
|
|
1006
|
+
self.pool = pool
|
|
1007
|
+
self.pool.add(self)
|
|
1008
|
+
|
|
1009
|
+
def run(self):
|
|
1010
|
+
self.lock.acquire()
|
|
1011
|
+
try:
|
|
1012
|
+
for vuid, name, modDate in self.datasets:
|
|
1013
|
+
# check just in case
|
|
1014
|
+
if re.search("_sub\d+$", name) is None:
|
|
1015
|
+
_logger.debug(f"skip non sub {name}")
|
|
1016
|
+
continue
|
|
1017
|
+
_logger.debug(f"delete sub {name}")
|
|
1018
|
+
if name.startswith("user.") or name.startswith("group.") or name.startswith("hc_test."):
|
|
1019
|
+
dsExists = False
|
|
1020
|
+
else:
|
|
1021
|
+
dsExists = True
|
|
1022
|
+
# get PandaIDs
|
|
1023
|
+
self.proxyLock.acquire()
|
|
1024
|
+
retF, resF = taskBuffer.querySQLS(
|
|
1025
|
+
"SELECT /*+ index(tab FILESTABLE4_DESTDBLOCK_IDX) */ DISTINCT PandaID FROM ATLAS_PANDA.filesTable4 tab WHERE destinationDBlock=:destinationDBlock ",
|
|
1026
|
+
{":destinationDBlock": name},
|
|
1027
|
+
)
|
|
1028
|
+
self.proxyLock.release()
|
|
1029
|
+
if retF is None:
|
|
1030
|
+
_logger.error(f"SQL error for sub {name}")
|
|
1031
|
+
continue
|
|
1032
|
+
else:
|
|
1033
|
+
_logger.debug(f"sub {name} has {len(resF)} jobs")
|
|
1034
|
+
self.proxyLock.acquire()
|
|
1035
|
+
# check jobs
|
|
1036
|
+
sqlP = "SELECT jobStatus FROM ATLAS_PANDA.jobsArchived4 WHERE PandaID=:PandaID "
|
|
1037
|
+
sqlP += "UNION "
|
|
1038
|
+
sqlP += "SELECT jobStatus FROM ATLAS_PANDAARCH.jobsArchived WHERE PandaID=:PandaID AND modificationTime>CURRENT_DATE-30 "
|
|
1039
|
+
allDone = True
|
|
1040
|
+
for (pandaID,) in resF:
|
|
1041
|
+
retP, resP = taskBuffer.querySQLS(sqlP, {":PandaID": pandaID})
|
|
1042
|
+
if len(resP) == 0:
|
|
1043
|
+
_logger.debug(f"skip delete sub {name} PandaID={pandaID} not found")
|
|
1044
|
+
allDone = False
|
|
1045
|
+
break
|
|
1046
|
+
jobStatus = resP[0][0]
|
|
1047
|
+
if jobStatus not in [
|
|
1048
|
+
"finished",
|
|
1049
|
+
"failed",
|
|
1050
|
+
"cancelled",
|
|
1051
|
+
"closed",
|
|
1052
|
+
]:
|
|
1053
|
+
_logger.debug(f"skip delete sub {name} PandaID={pandaID} is active {jobStatus}")
|
|
1054
|
+
allDone = False
|
|
1055
|
+
break
|
|
1056
|
+
self.proxyLock.release()
|
|
1057
|
+
if allDone:
|
|
1058
|
+
_logger.debug(f"deleting sub {name}")
|
|
1059
|
+
try:
|
|
1060
|
+
rucioAPI.erase_dataset(name, grace_period=4)
|
|
1061
|
+
status = True
|
|
1062
|
+
except Exception:
|
|
1063
|
+
errtype, errvalue = sys.exc_info()[:2]
|
|
1064
|
+
out = f"{errtype} {errvalue}"
|
|
1065
|
+
_logger.error(f"{name} failed to erase with {out}")
|
|
1066
|
+
else:
|
|
1067
|
+
_logger.debug(f"wait sub {name}")
|
|
1068
|
+
continue
|
|
1069
|
+
# update dataset
|
|
1070
|
+
self.proxyLock.acquire()
|
|
1071
|
+
varMap = {}
|
|
1072
|
+
varMap[":vuid"] = vuid
|
|
1073
|
+
varMap[":ost1"] = "completed"
|
|
1074
|
+
varMap[":ost2"] = "cleanup"
|
|
1075
|
+
varMap[":newStatus"] = "deleted"
|
|
1076
|
+
taskBuffer.querySQLS(
|
|
1077
|
+
"UPDATE ATLAS_PANDA.Datasets SET status=:newStatus,modificationdate=CURRENT_DATE WHERE vuid=:vuid AND status IN (:ost1,:ost2) ",
|
|
1078
|
+
varMap,
|
|
1079
|
+
)
|
|
1080
|
+
self.proxyLock.release()
|
|
1081
|
+
_logger.debug(f"end {name} ")
|
|
1082
|
+
except Exception:
|
|
1083
|
+
errStr = traceback.format_exc()
|
|
1084
|
+
_logger.error(errStr)
|
|
1085
|
+
self.pool.remove(self)
|
|
1086
|
+
self.lock.release()
|
|
1087
|
+
|
|
1088
|
+
# delete sub datasets
|
|
1089
|
+
_logger.debug("==== delete sub datasets ====")
|
|
1090
|
+
timeLimitU = naive_utcnow() - datetime.timedelta(minutes=30)
|
|
1091
|
+
timeLimitL = naive_utcnow() - datetime.timedelta(days=14)
|
|
1092
|
+
subdeleteLock = threading.Semaphore(5)
|
|
1093
|
+
subdeleteProxyLock = threading.Lock()
|
|
1094
|
+
subdeleteThreadPool = ThreadPool()
|
|
1095
|
+
maxRows = 5000
|
|
1096
|
+
while True:
|
|
1097
|
+
# lock
|
|
1098
|
+
subdeleteLock.acquire()
|
|
1099
|
+
# get datasets
|
|
1100
|
+
varMap = {}
|
|
1101
|
+
varMap[":limitU"] = timeLimitU
|
|
1102
|
+
varMap[":limitL"] = timeLimitL
|
|
1103
|
+
varMap[":type"] = "output"
|
|
1104
|
+
varMap[":subtype"] = "sub"
|
|
1105
|
+
varMap[":st1"] = "completed"
|
|
1106
|
+
varMap[":st2"] = "cleanup"
|
|
1107
|
+
sqlQuery = (
|
|
1108
|
+
"type=:type AND subType=:subtype AND status IN (:st1,:st2) AND (creationdate BETWEEN :limitL AND :limitU) AND (modificationdate BETWEEN :limitL AND :limitU) AND rownum <= %s"
|
|
1109
|
+
% maxRows
|
|
1110
|
+
)
|
|
1111
|
+
subdeleteProxyLock.acquire()
|
|
1112
|
+
res = taskBuffer.getLockDatasets(sqlQuery, varMap, modTimeOffset="90/24/60")
|
|
1113
|
+
if res is None:
|
|
1114
|
+
_logger.debug(f"# of sub datasets to be deleted {res}")
|
|
1115
|
+
else:
|
|
1116
|
+
_logger.debug(f"# of sub datasets to be deleted {len(res)}")
|
|
1117
|
+
if res is None or len(res) == 0:
|
|
1118
|
+
subdeleteProxyLock.release()
|
|
1119
|
+
subdeleteLock.release()
|
|
1120
|
+
break
|
|
1121
|
+
subdeleteProxyLock.release()
|
|
1122
|
+
# release
|
|
1123
|
+
subdeleteLock.release()
|
|
1124
|
+
# run subdeleter
|
|
1125
|
+
iRows = 0
|
|
1126
|
+
nRows = 500
|
|
1127
|
+
while iRows < len(res):
|
|
1128
|
+
subdeleter = SubDeleter(
|
|
1129
|
+
subdeleteLock,
|
|
1130
|
+
subdeleteProxyLock,
|
|
1131
|
+
res[iRows : iRows + nRows],
|
|
1132
|
+
subdeleteThreadPool,
|
|
1133
|
+
)
|
|
1134
|
+
subdeleter.start()
|
|
1135
|
+
iRows += nRows
|
|
1136
|
+
subdeleteThreadPool.join()
|
|
1137
|
+
if len(res) < 100:
|
|
1138
|
+
break
|
|
1139
|
+
|
|
1140
|
+
# release memory
|
|
1141
|
+
del siteMapper
|
|
1142
|
+
del deletedDisList
|
|
1143
|
+
|
|
1144
|
+
_memoryCheck("end")
|
|
1145
|
+
|
|
1146
|
+
# stop taskBuffer if created inside this script
|
|
1147
|
+
taskBuffer.cleanup(requester=requester_id)
|
|
1148
|
+
|
|
1149
|
+
_logger.debug("===================== end =====================")
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
# run
|
|
1153
|
+
if __name__ == "__main__":
|
|
1154
|
+
main()
|