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,227 @@
|
|
|
1
|
+
"""
|
|
2
|
+
work queue specification
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from pandaserver.taskbuffer.GlobalShares import Share
|
|
9
|
+
|
|
10
|
+
RESOURCE = "Resource"
|
|
11
|
+
ACTIVE_FUNCTIONS = [RESOURCE]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WorkQueue(object):
|
|
15
|
+
# attributes
|
|
16
|
+
_attributes = (
|
|
17
|
+
"queue_id",
|
|
18
|
+
"queue_name",
|
|
19
|
+
"queue_type",
|
|
20
|
+
"VO",
|
|
21
|
+
"queue_share",
|
|
22
|
+
"queue_order",
|
|
23
|
+
"criteria",
|
|
24
|
+
"variables",
|
|
25
|
+
"partitionID",
|
|
26
|
+
"stretchable",
|
|
27
|
+
"status",
|
|
28
|
+
"queue_function",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# parameters for selection criteria
|
|
32
|
+
_paramsForSelection = ("prodSourceLabel", "workingGroup", "processingType", "coreCount", "site", "eventService", "splitRule", "campaign")
|
|
33
|
+
|
|
34
|
+
# correspondence with Global Shares attributes and parameters
|
|
35
|
+
_attributes_gs_conversion_dic = {"name": "queue_name", "value": "queue_share", "prodsourcelabel": "queue_type", "queue_id": "queue_id", "vo": "VO"}
|
|
36
|
+
|
|
37
|
+
_params_gs_conversion_dic = {
|
|
38
|
+
"prodsourcelabel": "prodSourceLabel",
|
|
39
|
+
"workinggroup": "workingGroup",
|
|
40
|
+
"campaign": "campaign",
|
|
41
|
+
"processingtype": "processingType",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def __init__(self):
|
|
45
|
+
"""
|
|
46
|
+
Constructor
|
|
47
|
+
"""
|
|
48
|
+
# install attributes
|
|
49
|
+
for attr in self._attributes:
|
|
50
|
+
setattr(self, attr, None)
|
|
51
|
+
|
|
52
|
+
# global share is by default false
|
|
53
|
+
self.is_global_share = False
|
|
54
|
+
# throttled is set to True by default. Some Global Shares will overwrite it to False
|
|
55
|
+
self.throttled = True
|
|
56
|
+
|
|
57
|
+
def __str__(self):
|
|
58
|
+
"""
|
|
59
|
+
String representation of a workqueue
|
|
60
|
+
:return: string with the representation of the work queue
|
|
61
|
+
"""
|
|
62
|
+
return str(self.queue_name)
|
|
63
|
+
|
|
64
|
+
def dump(self):
|
|
65
|
+
"""
|
|
66
|
+
Creates a human-friendly string with the work queue information
|
|
67
|
+
:return: string representation of the work queue
|
|
68
|
+
"""
|
|
69
|
+
dump_str = f"id:{self.queue_id} order:{self.queue_order} name:{self.queue_name} share:{self.queue_share} "
|
|
70
|
+
|
|
71
|
+
# normal queue
|
|
72
|
+
if self.is_global_share:
|
|
73
|
+
dump_str += f"gs_name:{self.queue_name} (global share)"
|
|
74
|
+
else:
|
|
75
|
+
dump_str += f"criteria:{self.criteria} var:{str(self.variables)} eval:{self.evalString}"
|
|
76
|
+
|
|
77
|
+
return dump_str
|
|
78
|
+
|
|
79
|
+
def getID(self):
|
|
80
|
+
"""
|
|
81
|
+
get ID
|
|
82
|
+
:return: returns a list with the ID of the work queue
|
|
83
|
+
"""
|
|
84
|
+
return self.queue_id
|
|
85
|
+
|
|
86
|
+
def pack(self, values):
|
|
87
|
+
"""
|
|
88
|
+
Packs tuple into the object
|
|
89
|
+
:param values: list with the values in the order declared in the attributes section
|
|
90
|
+
:return: nothing
|
|
91
|
+
"""
|
|
92
|
+
for i, attr in enumerate(self._attributes):
|
|
93
|
+
val = values[i]
|
|
94
|
+
setattr(self, attr, val)
|
|
95
|
+
|
|
96
|
+
# disallow negative share
|
|
97
|
+
if self.queue_share is not None and self.queue_share < 0:
|
|
98
|
+
self.queue_share = 0
|
|
99
|
+
|
|
100
|
+
# convert variables string to a map of bind-variables
|
|
101
|
+
tmp_map = {}
|
|
102
|
+
try:
|
|
103
|
+
for item in self.variables.split(","):
|
|
104
|
+
# look for key: value
|
|
105
|
+
item = item.strip()
|
|
106
|
+
items = item.split(":")
|
|
107
|
+
if len(items) != 2:
|
|
108
|
+
continue
|
|
109
|
+
# add
|
|
110
|
+
tmp_map[f":{items[0]}"] = items[1]
|
|
111
|
+
except Exception:
|
|
112
|
+
pass
|
|
113
|
+
# assign map
|
|
114
|
+
self.variables = tmp_map
|
|
115
|
+
# make a python statement for eval
|
|
116
|
+
if self.criteria in ["", None]:
|
|
117
|
+
# catch all
|
|
118
|
+
self.evalString = "True"
|
|
119
|
+
else:
|
|
120
|
+
tmp_eval_str = self.criteria
|
|
121
|
+
# replace IN/OR/AND to in/or/and
|
|
122
|
+
tmp_eval_str = re.sub(" IN ", " in ", tmp_eval_str, re.I)
|
|
123
|
+
tmp_eval_str = re.sub(" OR ", " or ", tmp_eval_str, re.I)
|
|
124
|
+
tmp_eval_str = re.sub(" AND ", " and ", tmp_eval_str, re.I)
|
|
125
|
+
# replace = to ==
|
|
126
|
+
tmp_eval_str = tmp_eval_str.replace("=", "==")
|
|
127
|
+
# replace LIKE
|
|
128
|
+
tmp_eval_str = re.sub("(?P<var>[^ \(]+)\s+LIKE\s+(?P<pat>[^ \(]+)", "re.search(\g<pat>,\g<var>,re.I) is not None", tmp_eval_str, re.I)
|
|
129
|
+
# NULL
|
|
130
|
+
tmp_eval_str = re.sub(" IS NULL", "==None", tmp_eval_str)
|
|
131
|
+
tmp_eval_str = re.sub(" IS NOT NULL", "!=None", tmp_eval_str)
|
|
132
|
+
# replace NOT to not
|
|
133
|
+
tmp_eval_str = re.sub(" NOT ", " not ", tmp_eval_str, re.I)
|
|
134
|
+
# fomat cases
|
|
135
|
+
for tmp_param in self._paramsForSelection:
|
|
136
|
+
tmp_eval_str = re.sub(tmp_param, tmp_param, tmp_eval_str, re.I)
|
|
137
|
+
# replace bind-variables
|
|
138
|
+
for tmp_key, tmp_val in self.variables.items():
|
|
139
|
+
if "%" in tmp_val:
|
|
140
|
+
# wildcard
|
|
141
|
+
tmp_val = tmp_val.replace("%", ".*")
|
|
142
|
+
tmp_val = f"'^{tmp_val}$'"
|
|
143
|
+
else:
|
|
144
|
+
# normal variable
|
|
145
|
+
tmp_val = f"'{tmp_val}'"
|
|
146
|
+
tmp_eval_str = tmp_eval_str.replace(tmp_key, tmp_val)
|
|
147
|
+
# assign
|
|
148
|
+
self.evalString = tmp_eval_str
|
|
149
|
+
|
|
150
|
+
def pack_gs(self, gshare):
|
|
151
|
+
"""
|
|
152
|
+
Packs tuple into the object
|
|
153
|
+
:param gshare: global share
|
|
154
|
+
:return: nothing
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
# the object becomes a global share wq
|
|
158
|
+
self.is_global_share = True
|
|
159
|
+
try:
|
|
160
|
+
tmp_map = {}
|
|
161
|
+
for i, attr in enumerate(gshare._attributes):
|
|
162
|
+
# global share attributes can be mapped to a wq attribute(1), to a wq param(2), or to none of both
|
|
163
|
+
# 1. if the gs attribute is mapped to a wq attribute, do a get and a set
|
|
164
|
+
if attr in self._attributes_gs_conversion_dic:
|
|
165
|
+
attr_wq = self._attributes_gs_conversion_dic[attr]
|
|
166
|
+
val = getattr(gshare, attr)
|
|
167
|
+
setattr(self, attr_wq, val)
|
|
168
|
+
# 2. if the gs attribute is mapped to a wq param, add it to the bind variables dictionary
|
|
169
|
+
# Probably we don't need this, we just care about matching the gs name
|
|
170
|
+
if attr in self._params_gs_conversion_dic:
|
|
171
|
+
param_wq = self._params_gs_conversion_dic[attr]
|
|
172
|
+
val = getattr(gshare, attr)
|
|
173
|
+
tmp_map[f":{param_wq}"] = val
|
|
174
|
+
|
|
175
|
+
# 3. Special case for throttled. This is defined additionally, since it's not present in WQs
|
|
176
|
+
if attr == "throttled" and gshare.throttled == "N":
|
|
177
|
+
self.throttled = False
|
|
178
|
+
|
|
179
|
+
self.variables = tmp_map
|
|
180
|
+
except Exception:
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
# evaluate in python
|
|
184
|
+
def evaluate(self, param_map):
|
|
185
|
+
# only active queues are evaluated
|
|
186
|
+
if self.isActive():
|
|
187
|
+
# normal queue
|
|
188
|
+
# expand parameters to local namespace
|
|
189
|
+
for tmp_param_key, tmp_param_val in param_map.items():
|
|
190
|
+
if isinstance(tmp_param_val, str):
|
|
191
|
+
# add quotes for string
|
|
192
|
+
exec(f'{tmp_param_key}="{tmp_param_val}"', globals())
|
|
193
|
+
else:
|
|
194
|
+
exec(f"{tmp_param_key}={tmp_param_val}", globals())
|
|
195
|
+
# add default parameters if missing
|
|
196
|
+
for tmp_param in self._paramsForSelection:
|
|
197
|
+
if tmp_param not in param_map:
|
|
198
|
+
exec(f"{tmp_param}=None", globals())
|
|
199
|
+
# evaluate
|
|
200
|
+
exec(f"ret_var = {self.evalString}", globals())
|
|
201
|
+
return self, ret_var
|
|
202
|
+
|
|
203
|
+
# return False
|
|
204
|
+
return self, False
|
|
205
|
+
|
|
206
|
+
# check if active
|
|
207
|
+
def isActive(self):
|
|
208
|
+
if self.status != "inactive": # and self.queue_function in ACTIVE_FUNCTIONS:
|
|
209
|
+
return True
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
# check if its eligible after global share alignment
|
|
213
|
+
def isAligned(self):
|
|
214
|
+
if self.queue_function == RESOURCE or self.is_global_share:
|
|
215
|
+
return True
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
# return column names for INSERT
|
|
219
|
+
def column_names(cls):
|
|
220
|
+
ret = ""
|
|
221
|
+
for attr in cls._attributes:
|
|
222
|
+
if ret != "":
|
|
223
|
+
ret += ","
|
|
224
|
+
ret += attr
|
|
225
|
+
return ret
|
|
226
|
+
|
|
227
|
+
column_names = classmethod(column_names)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mapper to map task/job to a work queue
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from .WorkQueue import WorkQueue
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WorkQueueMapper:
|
|
12
|
+
# constructor
|
|
13
|
+
def __init__(self):
|
|
14
|
+
# Initialize maps
|
|
15
|
+
self.work_queue_map = {}
|
|
16
|
+
self.work_queue_global_dic_by_name = {}
|
|
17
|
+
self.work_queue_global_dic_by_id = {}
|
|
18
|
+
|
|
19
|
+
def getSqlQuery(self):
|
|
20
|
+
"""
|
|
21
|
+
Generates the SQL to get all work queues
|
|
22
|
+
"""
|
|
23
|
+
sql = f"SELECT {WorkQueue.column_names()} FROM ATLAS_PANDA.JEDI_Work_Queue"
|
|
24
|
+
|
|
25
|
+
return sql
|
|
26
|
+
|
|
27
|
+
def makeMap(self, work_queues, global_leave_shares):
|
|
28
|
+
"""
|
|
29
|
+
Creates the mapping with work queues and global shares
|
|
30
|
+
:param work_queues: work queues
|
|
31
|
+
:param global_leave_shares: global leave shares
|
|
32
|
+
:return
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# 1. add all workqueues to the map
|
|
36
|
+
for wq in work_queues:
|
|
37
|
+
# pack
|
|
38
|
+
work_queue = WorkQueue()
|
|
39
|
+
work_queue.pack(wq)
|
|
40
|
+
|
|
41
|
+
# skip inactive queues
|
|
42
|
+
if not work_queue.isActive():
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
# add VO
|
|
46
|
+
if work_queue.VO not in self.work_queue_map:
|
|
47
|
+
self.work_queue_map[work_queue.VO] = {}
|
|
48
|
+
|
|
49
|
+
# add type
|
|
50
|
+
if work_queue.queue_type not in self.work_queue_map[work_queue.VO]:
|
|
51
|
+
self.work_queue_map[work_queue.VO][work_queue.queue_type] = []
|
|
52
|
+
|
|
53
|
+
self.work_queue_map[work_queue.VO][work_queue.queue_type].append(work_queue)
|
|
54
|
+
self.work_queue_global_dic_by_name[work_queue.queue_name] = work_queue
|
|
55
|
+
self.work_queue_global_dic_by_id[work_queue.queue_id] = work_queue
|
|
56
|
+
|
|
57
|
+
# sort the queue list by order
|
|
58
|
+
for vo in self.work_queue_map:
|
|
59
|
+
for type in self.work_queue_map[vo]:
|
|
60
|
+
# make ordered map
|
|
61
|
+
ordered_map = {}
|
|
62
|
+
queue_map = self.work_queue_map[vo][type]
|
|
63
|
+
for wq in queue_map:
|
|
64
|
+
if wq.queue_order not in ordered_map:
|
|
65
|
+
ordered_map[wq.queue_order] = []
|
|
66
|
+
# append
|
|
67
|
+
ordered_map[wq.queue_order].append(wq)
|
|
68
|
+
# make sorted list
|
|
69
|
+
ordered_list = list(ordered_map.keys())
|
|
70
|
+
ordered_list.sort(key=lambda x: (x is None, x))
|
|
71
|
+
new_list = []
|
|
72
|
+
for order_val in ordered_list:
|
|
73
|
+
new_list += ordered_map[order_val]
|
|
74
|
+
# set new list
|
|
75
|
+
self.work_queue_map[vo][type] = new_list
|
|
76
|
+
|
|
77
|
+
# 2. add all the global shares
|
|
78
|
+
for gs in global_leave_shares:
|
|
79
|
+
work_queue_gs = WorkQueue()
|
|
80
|
+
work_queue_gs.pack_gs(gs)
|
|
81
|
+
|
|
82
|
+
if work_queue_gs.VO is None:
|
|
83
|
+
vo = "atlas"
|
|
84
|
+
else:
|
|
85
|
+
vo = work_queue_gs.VO
|
|
86
|
+
|
|
87
|
+
if vo not in self.work_queue_map:
|
|
88
|
+
self.work_queue_map[vo] = {}
|
|
89
|
+
|
|
90
|
+
if work_queue_gs.queue_type not in self.work_queue_map[vo]:
|
|
91
|
+
self.work_queue_map[vo][work_queue_gs.queue_type] = []
|
|
92
|
+
|
|
93
|
+
self.work_queue_map[vo][work_queue_gs.queue_type].append(work_queue_gs)
|
|
94
|
+
self.work_queue_global_dic_by_name[work_queue_gs.queue_name] = work_queue_gs
|
|
95
|
+
self.work_queue_global_dic_by_id[work_queue_gs.queue_id] = work_queue_gs
|
|
96
|
+
|
|
97
|
+
# return
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
def dump(self):
|
|
101
|
+
"""
|
|
102
|
+
Creates a human-friendly string showing the work queue mappings
|
|
103
|
+
:return: string representation of the work queue mappings
|
|
104
|
+
"""
|
|
105
|
+
dump_str = "WorkQueue mapping\n"
|
|
106
|
+
for VO in self.work_queue_map:
|
|
107
|
+
dump_str += f" VO={VO}\n"
|
|
108
|
+
for type in self.work_queue_map[VO]:
|
|
109
|
+
dump_str += f" type={type}\n"
|
|
110
|
+
for workQueue in self.work_queue_map[VO][type]:
|
|
111
|
+
dump_str += f" {workQueue.dump()}\n"
|
|
112
|
+
# return
|
|
113
|
+
return dump_str
|
|
114
|
+
|
|
115
|
+
def getQueueWithSelParams(self, vo, type, **param_map):
|
|
116
|
+
"""
|
|
117
|
+
Used for tagging of work queues in task refiner. Get a work queue based on the selection parameters
|
|
118
|
+
:param vo: vo
|
|
119
|
+
:param type: type (in practice equivalent to prodsourcelabel)
|
|
120
|
+
:param param_map: parameter selection map
|
|
121
|
+
:return: work queue object and explanation in case no queue was found
|
|
122
|
+
"""
|
|
123
|
+
ret_str = ""
|
|
124
|
+
if vo not in self.work_queue_map:
|
|
125
|
+
ret_str = f"queues for vo={vo} are undefined"
|
|
126
|
+
elif type not in self.work_queue_map[vo]:
|
|
127
|
+
# check type
|
|
128
|
+
ret_str = f"queues for type={type} are undefined in vo={vo}"
|
|
129
|
+
else:
|
|
130
|
+
for wq in self.work_queue_map[vo][type]:
|
|
131
|
+
# don't return global share IDs for work queues
|
|
132
|
+
if wq.is_global_share:
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# evaluate
|
|
136
|
+
try:
|
|
137
|
+
ret_queue, result = wq.evaluate(param_map)
|
|
138
|
+
if result:
|
|
139
|
+
return ret_queue, ret_str
|
|
140
|
+
except Exception:
|
|
141
|
+
ret_str += f"{wq.queue_name},"
|
|
142
|
+
|
|
143
|
+
ret_str = ret_str[:-1]
|
|
144
|
+
if ret_str != "":
|
|
145
|
+
new_ret_str = f"eval with VO={vo} "
|
|
146
|
+
for tmp_param_key, tmp_param_val in param_map.items():
|
|
147
|
+
new_ret_str += "{0}={1} failed for {0}".format(tmp_param_key, tmp_param_val, ret_str)
|
|
148
|
+
ret_str = new_ret_str
|
|
149
|
+
|
|
150
|
+
# no queue matched to selection parameters
|
|
151
|
+
return None, ret_str
|
|
152
|
+
|
|
153
|
+
def getQueueByName(self, vo, type, queue_name):
|
|
154
|
+
"""
|
|
155
|
+
# get queue by name
|
|
156
|
+
:param queue_name: name of the queue
|
|
157
|
+
:param vo: vo
|
|
158
|
+
:param type: type
|
|
159
|
+
:return: queue object or None if not found
|
|
160
|
+
"""
|
|
161
|
+
if vo in self.work_queue_map and type in self.work_queue_map[vo]:
|
|
162
|
+
for wq in self.work_queue_map[vo][type]:
|
|
163
|
+
if wq.queue_name == queue_name:
|
|
164
|
+
return wq
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
# get queue with ID
|
|
168
|
+
def getQueueWithIDGshare(self, queue_id, gshare_name):
|
|
169
|
+
# 1. Check for a Resource queue
|
|
170
|
+
if queue_id in self.work_queue_global_dic_by_id and self.work_queue_global_dic_by_id[queue_id].queue_function == "Resource":
|
|
171
|
+
return self.work_queue_global_dic_by_id[queue_id]
|
|
172
|
+
|
|
173
|
+
# 2. If it wasn't a resource queue, return the global share work queue
|
|
174
|
+
if gshare_name in self.work_queue_global_dic_by_name:
|
|
175
|
+
return self.work_queue_global_dic_by_name[gshare_name]
|
|
176
|
+
|
|
177
|
+
# not found
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
# get queue list with VO and type
|
|
181
|
+
def getAlignedQueueList(self, vo, queue_type):
|
|
182
|
+
"""
|
|
183
|
+
NOTE: Returns ONLY resource queues and global shares (old non-resource queues are skipped)
|
|
184
|
+
"""
|
|
185
|
+
ret_list = []
|
|
186
|
+
|
|
187
|
+
if vo in self.work_queue_map:
|
|
188
|
+
# if queue type was specified
|
|
189
|
+
if queue_type not in ["", None, "any"]:
|
|
190
|
+
for map_queue_type in self.work_queue_map[vo]:
|
|
191
|
+
if re.match(map_queue_type, queue_type):
|
|
192
|
+
for tmp_wq in self.work_queue_map[vo][map_queue_type]:
|
|
193
|
+
if tmp_wq.isAligned():
|
|
194
|
+
ret_list.append(tmp_wq)
|
|
195
|
+
|
|
196
|
+
# include all queue types
|
|
197
|
+
else:
|
|
198
|
+
for tmp_type, tmp_wq_list in self.work_queue_map[vo].items():
|
|
199
|
+
for tmp_wq in tmp_wq_list:
|
|
200
|
+
if tmp_wq.isAligned():
|
|
201
|
+
ret_list.append(tmp_wq)
|
|
202
|
+
|
|
203
|
+
return ret_list
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
worker specification
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WorkerSpec(object):
|
|
10
|
+
# attributes
|
|
11
|
+
_attributes = (
|
|
12
|
+
"harvesterID",
|
|
13
|
+
"workerID",
|
|
14
|
+
"batchID",
|
|
15
|
+
"queueName",
|
|
16
|
+
"status",
|
|
17
|
+
"computingSite",
|
|
18
|
+
"nCore",
|
|
19
|
+
"nodeID",
|
|
20
|
+
"submitTime",
|
|
21
|
+
"startTime",
|
|
22
|
+
"endTime",
|
|
23
|
+
"lastUpdate",
|
|
24
|
+
"stdOut",
|
|
25
|
+
"stdErr",
|
|
26
|
+
"batchLog",
|
|
27
|
+
"jdl",
|
|
28
|
+
"resourceType",
|
|
29
|
+
"nativeExitCode",
|
|
30
|
+
"nativeStatus",
|
|
31
|
+
"diagMessage",
|
|
32
|
+
"nJobs",
|
|
33
|
+
"computingElement",
|
|
34
|
+
"submissionHost",
|
|
35
|
+
"harvesterHost",
|
|
36
|
+
"errorCode",
|
|
37
|
+
"jobType",
|
|
38
|
+
"minRamCount",
|
|
39
|
+
)
|
|
40
|
+
# slots
|
|
41
|
+
__slots__ = _attributes + ("_changedAttrs",)
|
|
42
|
+
# attributes which have 0 by default
|
|
43
|
+
_zeroAttrs = ()
|
|
44
|
+
# catchall resouce type
|
|
45
|
+
RT_catchall = "ANY"
|
|
46
|
+
|
|
47
|
+
# constructor
|
|
48
|
+
def __init__(self):
|
|
49
|
+
# install attributes
|
|
50
|
+
for attr in self._attributes:
|
|
51
|
+
object.__setattr__(self, attr, None)
|
|
52
|
+
# map of changed attributes
|
|
53
|
+
object.__setattr__(self, "_changedAttrs", {})
|
|
54
|
+
|
|
55
|
+
# override __setattr__ to collect the changed attributes
|
|
56
|
+
def __setattr__(self, name, value):
|
|
57
|
+
oldVal = getattr(self, name)
|
|
58
|
+
# convert string to datetime
|
|
59
|
+
if isinstance(value, str) and value.startswith("datetime/"):
|
|
60
|
+
value = datetime.datetime.strptime(value.split("/")[-1], "%Y-%m-%d %H:%M:%S.%f")
|
|
61
|
+
object.__setattr__(self, name, value)
|
|
62
|
+
# collect changed attributes
|
|
63
|
+
if oldVal != value:
|
|
64
|
+
self._changedAttrs[name] = value
|
|
65
|
+
|
|
66
|
+
# reset changed attribute list
|
|
67
|
+
def resetChangedList(self):
|
|
68
|
+
self._oldPandaID = self.PandaID
|
|
69
|
+
object.__setattr__(self, "_changedAttrs", {})
|
|
70
|
+
|
|
71
|
+
# return map of values
|
|
72
|
+
def valuesMap(self, onlyChanged=False):
|
|
73
|
+
ret = {}
|
|
74
|
+
for attr in self._attributes:
|
|
75
|
+
if onlyChanged and attr not in self._changedAttrs:
|
|
76
|
+
continue
|
|
77
|
+
val = getattr(self, attr)
|
|
78
|
+
if val is None:
|
|
79
|
+
if attr in self._zeroAttrs:
|
|
80
|
+
val = 0
|
|
81
|
+
ret[f":{attr}"] = val
|
|
82
|
+
return ret
|
|
83
|
+
|
|
84
|
+
# pack tuple into FileSpec
|
|
85
|
+
def pack(self, values):
|
|
86
|
+
for i in range(len(self._attributes)):
|
|
87
|
+
attr = self._attributes[i]
|
|
88
|
+
val = values[i]
|
|
89
|
+
object.__setattr__(self, attr, val)
|
|
90
|
+
|
|
91
|
+
# return column names for INSERT
|
|
92
|
+
def columnNames(cls, prefix=None):
|
|
93
|
+
ret = ""
|
|
94
|
+
for attr in cls._attributes:
|
|
95
|
+
if prefix is not None:
|
|
96
|
+
ret += f"{prefix}."
|
|
97
|
+
ret += f"{attr},"
|
|
98
|
+
ret = ret[:-1]
|
|
99
|
+
return ret
|
|
100
|
+
|
|
101
|
+
columnNames = classmethod(columnNames)
|
|
102
|
+
|
|
103
|
+
# return expression of bind variables for INSERT
|
|
104
|
+
def bindValuesExpression(cls):
|
|
105
|
+
from pandaserver.config import panda_config
|
|
106
|
+
|
|
107
|
+
ret = "VALUES("
|
|
108
|
+
for attr in cls._attributes:
|
|
109
|
+
ret += f":{attr},"
|
|
110
|
+
ret = ret[:-1]
|
|
111
|
+
ret += ")"
|
|
112
|
+
return ret
|
|
113
|
+
|
|
114
|
+
bindValuesExpression = classmethod(bindValuesExpression)
|
|
115
|
+
|
|
116
|
+
# return an expression of bind variables for UPDATE to update only changed attributes
|
|
117
|
+
def bindUpdateChangesExpression(self):
|
|
118
|
+
ret = ""
|
|
119
|
+
for attr in self._attributes:
|
|
120
|
+
if attr not in self._changedAttrs:
|
|
121
|
+
continue
|
|
122
|
+
ret += "{0}=:{0},".format(attr)
|
|
123
|
+
ret = ret[:-1]
|
|
124
|
+
return ret
|
|
125
|
+
|
|
126
|
+
# return state values to be pickled
|
|
127
|
+
def __getstate__(self):
|
|
128
|
+
state = []
|
|
129
|
+
for attr in self._attributes:
|
|
130
|
+
val = getattr(self, attr)
|
|
131
|
+
state.append(val)
|
|
132
|
+
state.append(self._changedAttrs)
|
|
133
|
+
return state
|
|
134
|
+
|
|
135
|
+
# restore state from the unpickled state values
|
|
136
|
+
def __setstate__(self, state):
|
|
137
|
+
i = 0
|
|
138
|
+
for attr in self._attributes:
|
|
139
|
+
if i >= len(state) - 1:
|
|
140
|
+
break
|
|
141
|
+
object.__setattr__(self, attr, state[i])
|
|
142
|
+
i += 1
|
|
143
|
+
object.__setattr__(self, "_changedAttrs", state[-1])
|