parsl 2024.3.18__py3-none-any.whl → 2025.1.13__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.
- parsl/__init__.py +9 -10
- parsl/addresses.py +26 -6
- parsl/app/app.py +7 -8
- parsl/app/bash.py +15 -8
- parsl/app/errors.py +10 -13
- parsl/app/futures.py +8 -10
- parsl/app/python.py +2 -1
- parsl/benchmark/perf.py +2 -1
- parsl/concurrent/__init__.py +2 -2
- parsl/config.py +53 -10
- parsl/configs/ASPIRE1.py +6 -5
- parsl/configs/Azure.py +9 -8
- parsl/configs/bridges.py +6 -4
- parsl/configs/cc_in2p3.py +3 -3
- parsl/configs/ec2.py +3 -1
- parsl/configs/expanse.py +4 -3
- parsl/configs/frontera.py +3 -4
- parsl/configs/htex_local.py +3 -4
- parsl/configs/illinoiscluster.py +3 -1
- parsl/configs/improv.py +34 -0
- parsl/configs/kubernetes.py +4 -3
- parsl/configs/local_threads.py +5 -1
- parsl/configs/midway.py +5 -3
- parsl/configs/osg.py +4 -2
- parsl/configs/polaris.py +4 -2
- parsl/configs/stampede2.py +6 -5
- parsl/configs/summit.py +3 -3
- parsl/configs/toss3_llnl.py +4 -3
- parsl/configs/vineex_local.py +6 -4
- parsl/configs/wqex_local.py +5 -3
- parsl/curvezmq.py +4 -0
- parsl/data_provider/data_manager.py +4 -3
- parsl/data_provider/file_noop.py +1 -2
- parsl/data_provider/files.py +3 -3
- parsl/data_provider/ftp.py +1 -3
- parsl/data_provider/globus.py +7 -6
- parsl/data_provider/http.py +2 -2
- parsl/data_provider/rsync.py +1 -1
- parsl/data_provider/staging.py +2 -2
- parsl/data_provider/zip.py +135 -0
- parsl/dataflow/dependency_resolvers.py +115 -0
- parsl/dataflow/dflow.py +259 -223
- parsl/dataflow/errors.py +3 -5
- parsl/dataflow/futures.py +27 -14
- parsl/dataflow/memoization.py +5 -5
- parsl/dataflow/rundirs.py +5 -6
- parsl/dataflow/taskrecord.py +4 -5
- parsl/executors/__init__.py +4 -2
- parsl/executors/base.py +45 -15
- parsl/executors/errors.py +13 -0
- parsl/executors/execute_task.py +37 -0
- parsl/executors/flux/execute_parsl_task.py +3 -3
- parsl/executors/flux/executor.py +18 -19
- parsl/executors/flux/flux_instance_manager.py +26 -27
- parsl/executors/high_throughput/errors.py +43 -3
- parsl/executors/high_throughput/executor.py +307 -285
- parsl/executors/high_throughput/interchange.py +137 -168
- parsl/executors/high_throughput/manager_record.py +4 -0
- parsl/executors/high_throughput/manager_selector.py +55 -0
- parsl/executors/high_throughput/monitoring_info.py +2 -1
- parsl/executors/high_throughput/mpi_executor.py +113 -0
- parsl/executors/high_throughput/mpi_prefix_composer.py +10 -11
- parsl/executors/high_throughput/mpi_resource_management.py +6 -17
- parsl/executors/high_throughput/probe.py +9 -7
- parsl/executors/high_throughput/process_worker_pool.py +77 -75
- parsl/executors/high_throughput/zmq_pipes.py +81 -23
- parsl/executors/radical/executor.py +130 -79
- parsl/executors/radical/rpex_resources.py +17 -15
- parsl/executors/radical/rpex_worker.py +4 -3
- parsl/executors/status_handling.py +157 -51
- parsl/executors/taskvine/__init__.py +1 -1
- parsl/executors/taskvine/errors.py +1 -1
- parsl/executors/taskvine/exec_parsl_function.py +2 -2
- parsl/executors/taskvine/executor.py +38 -55
- parsl/executors/taskvine/factory.py +1 -1
- parsl/executors/taskvine/factory_config.py +1 -1
- parsl/executors/taskvine/manager.py +17 -13
- parsl/executors/taskvine/manager_config.py +7 -2
- parsl/executors/threads.py +6 -6
- parsl/executors/workqueue/errors.py +1 -1
- parsl/executors/workqueue/exec_parsl_function.py +6 -5
- parsl/executors/workqueue/executor.py +64 -63
- parsl/executors/workqueue/parsl_coprocess.py +1 -1
- parsl/jobs/error_handlers.py +2 -2
- parsl/jobs/job_status_poller.py +28 -112
- parsl/jobs/states.py +7 -2
- parsl/jobs/strategy.py +43 -31
- parsl/launchers/__init__.py +12 -3
- parsl/launchers/errors.py +1 -1
- parsl/launchers/launchers.py +0 -6
- parsl/log_utils.py +1 -2
- parsl/monitoring/db_manager.py +55 -93
- parsl/monitoring/errors.py +6 -0
- parsl/monitoring/monitoring.py +85 -311
- parsl/monitoring/queries/pandas.py +1 -2
- parsl/monitoring/radios/base.py +13 -0
- parsl/monitoring/radios/filesystem.py +52 -0
- parsl/monitoring/radios/htex.py +57 -0
- parsl/monitoring/radios/multiprocessing.py +17 -0
- parsl/monitoring/radios/udp.py +56 -0
- parsl/monitoring/radios/zmq.py +17 -0
- parsl/monitoring/remote.py +33 -37
- parsl/monitoring/router.py +212 -0
- parsl/monitoring/types.py +5 -6
- parsl/monitoring/visualization/app.py +4 -2
- parsl/monitoring/visualization/models.py +0 -1
- parsl/monitoring/visualization/plots/default/workflow_plots.py +8 -4
- parsl/monitoring/visualization/plots/default/workflow_resource_plots.py +1 -0
- parsl/monitoring/visualization/utils.py +0 -1
- parsl/monitoring/visualization/views.py +16 -9
- parsl/multiprocessing.py +0 -1
- parsl/process_loggers.py +1 -2
- parsl/providers/__init__.py +8 -17
- parsl/providers/aws/aws.py +2 -3
- parsl/providers/azure/azure.py +4 -5
- parsl/providers/base.py +2 -18
- parsl/providers/cluster_provider.py +3 -9
- parsl/providers/condor/condor.py +7 -17
- parsl/providers/errors.py +2 -2
- parsl/providers/googlecloud/googlecloud.py +2 -1
- parsl/providers/grid_engine/grid_engine.py +5 -14
- parsl/providers/kubernetes/kube.py +80 -40
- parsl/providers/local/local.py +13 -26
- parsl/providers/lsf/lsf.py +5 -23
- parsl/providers/pbspro/pbspro.py +5 -17
- parsl/providers/slurm/slurm.py +81 -39
- parsl/providers/torque/torque.py +3 -14
- parsl/serialize/__init__.py +8 -3
- parsl/serialize/base.py +1 -2
- parsl/serialize/concretes.py +5 -4
- parsl/serialize/facade.py +3 -3
- parsl/serialize/proxystore.py +3 -2
- parsl/tests/__init__.py +1 -1
- parsl/tests/configs/azure_single_node.py +4 -5
- parsl/tests/configs/bridges.py +3 -2
- parsl/tests/configs/cc_in2p3.py +1 -3
- parsl/tests/configs/comet.py +2 -1
- parsl/tests/configs/ec2_single_node.py +1 -2
- parsl/tests/configs/ec2_spot.py +1 -2
- parsl/tests/configs/flux_local.py +11 -0
- parsl/tests/configs/frontera.py +2 -3
- parsl/tests/configs/htex_local.py +3 -5
- parsl/tests/configs/htex_local_alternate.py +11 -15
- parsl/tests/configs/htex_local_intask_staging.py +5 -9
- parsl/tests/configs/htex_local_rsync_staging.py +4 -8
- parsl/tests/configs/local_radical.py +1 -3
- parsl/tests/configs/local_radical_mpi.py +2 -2
- parsl/tests/configs/local_threads_checkpoint_periodic.py +8 -10
- parsl/tests/configs/local_threads_monitoring.py +0 -1
- parsl/tests/configs/midway.py +2 -2
- parsl/tests/configs/nscc_singapore.py +3 -3
- parsl/tests/configs/osg_htex.py +1 -1
- parsl/tests/configs/petrelkube.py +3 -2
- parsl/tests/configs/slurm_local.py +24 -0
- parsl/tests/configs/summit.py +1 -0
- parsl/tests/configs/taskvine_ex.py +4 -7
- parsl/tests/configs/user_opts.py +0 -7
- parsl/tests/configs/workqueue_ex.py +4 -6
- parsl/tests/conftest.py +27 -13
- parsl/tests/integration/test_stress/test_python_simple.py +3 -4
- parsl/tests/integration/test_stress/test_python_threads.py +3 -5
- parsl/tests/manual_tests/htex_local.py +4 -6
- parsl/tests/manual_tests/test_basic.py +1 -0
- parsl/tests/manual_tests/test_log_filter.py +3 -1
- parsl/tests/manual_tests/test_memory_limits.py +6 -8
- parsl/tests/manual_tests/test_regression_220.py +2 -1
- parsl/tests/manual_tests/test_udp_simple.py +4 -4
- parsl/tests/manual_tests/test_worker_count.py +3 -2
- parsl/tests/scaling_tests/htex_local.py +2 -4
- parsl/tests/scaling_tests/test_scale.py +0 -9
- parsl/tests/scaling_tests/vineex_condor.py +1 -2
- parsl/tests/scaling_tests/vineex_local.py +1 -2
- parsl/tests/site_tests/site_config_selector.py +1 -6
- parsl/tests/site_tests/test_provider.py +4 -2
- parsl/tests/site_tests/test_site.py +2 -0
- parsl/tests/sites/test_affinity.py +7 -7
- parsl/tests/sites/test_dynamic_executor.py +3 -4
- parsl/tests/sites/test_ec2.py +3 -2
- parsl/tests/sites/test_worker_info.py +4 -5
- parsl/tests/test_aalst_patterns.py +0 -1
- parsl/tests/test_bash_apps/test_apptimeout.py +2 -2
- parsl/tests/test_bash_apps/test_basic.py +10 -4
- parsl/tests/test_bash_apps/test_error_codes.py +5 -7
- parsl/tests/test_bash_apps/test_inputs_default.py +25 -0
- parsl/tests/test_bash_apps/test_kwarg_storage.py +1 -1
- parsl/tests/test_bash_apps/test_memoize.py +2 -8
- parsl/tests/test_bash_apps/test_memoize_ignore_args.py +9 -14
- parsl/tests/test_bash_apps/test_memoize_ignore_args_regr.py +9 -14
- parsl/tests/test_bash_apps/test_multiline.py +1 -1
- parsl/tests/test_bash_apps/test_pipeline.py +1 -1
- parsl/tests/test_bash_apps/test_std_uri.py +123 -0
- parsl/tests/test_bash_apps/test_stdout.py +33 -8
- parsl/tests/test_callables.py +2 -2
- parsl/tests/test_checkpointing/test_periodic.py +21 -39
- parsl/tests/test_checkpointing/test_python_checkpoint_1.py +1 -0
- parsl/tests/test_checkpointing/test_python_checkpoint_2.py +2 -2
- parsl/tests/test_checkpointing/test_python_checkpoint_3.py +0 -1
- parsl/tests/test_checkpointing/test_regression_239.py +1 -1
- parsl/tests/test_checkpointing/test_task_exit.py +2 -3
- parsl/tests/test_docs/test_from_slides.py +5 -2
- parsl/tests/test_docs/test_kwargs.py +4 -1
- parsl/tests/test_docs/test_tutorial_1.py +1 -2
- parsl/tests/test_docs/test_workflow1.py +2 -2
- parsl/tests/test_docs/test_workflow2.py +0 -1
- parsl/tests/test_error_handling/test_rand_fail.py +2 -2
- parsl/tests/test_error_handling/test_resource_spec.py +10 -12
- parsl/tests/test_error_handling/test_retries.py +6 -16
- parsl/tests/test_error_handling/test_retry_handler.py +1 -0
- parsl/tests/test_error_handling/test_retry_handler_failure.py +2 -1
- parsl/tests/test_error_handling/test_serialization_fail.py +1 -1
- parsl/tests/test_error_handling/test_wrap_with_logs.py +1 -0
- parsl/tests/test_execute_task.py +29 -0
- parsl/tests/test_flux.py +1 -1
- parsl/tests/test_htex/test_basic.py +2 -3
- parsl/tests/test_htex/test_block_manager_selector_unit.py +20 -0
- parsl/tests/test_htex/test_command_client_timeout.py +66 -0
- parsl/tests/test_htex/test_connected_blocks.py +3 -2
- parsl/tests/test_htex/test_cpu_affinity_explicit.py +6 -10
- parsl/tests/test_htex/test_disconnected_blocks.py +6 -5
- parsl/tests/test_htex/test_disconnected_blocks_failing_provider.py +71 -0
- parsl/tests/test_htex/test_drain.py +11 -10
- parsl/tests/test_htex/test_htex.py +51 -25
- parsl/tests/test_htex/test_manager_failure.py +0 -1
- parsl/tests/test_htex/test_manager_selector_by_block.py +51 -0
- parsl/tests/test_htex/test_managers_command.py +36 -0
- parsl/tests/test_htex/test_missing_worker.py +2 -12
- parsl/tests/test_htex/test_multiple_disconnected_blocks.py +9 -9
- parsl/tests/test_htex/test_resource_spec_validation.py +45 -0
- parsl/tests/test_htex/test_zmq_binding.py +29 -8
- parsl/tests/test_monitoring/test_app_names.py +5 -5
- parsl/tests/test_monitoring/test_basic.py +73 -25
- parsl/tests/test_monitoring/test_db_locks.py +6 -4
- parsl/tests/test_monitoring/test_fuzz_zmq.py +19 -8
- parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py +80 -0
- parsl/tests/test_monitoring/test_incomplete_futures.py +5 -4
- parsl/tests/test_monitoring/test_memoization_representation.py +4 -2
- parsl/tests/test_monitoring/test_stdouterr.py +134 -0
- parsl/tests/test_monitoring/test_viz_colouring.py +1 -0
- parsl/tests/test_mpi_apps/test_bad_mpi_config.py +33 -26
- parsl/tests/test_mpi_apps/test_mpi_mode_enabled.py +28 -11
- parsl/tests/test_mpi_apps/test_mpi_prefix.py +4 -4
- parsl/tests/test_mpi_apps/test_mpi_scheduler.py +7 -2
- parsl/tests/test_mpi_apps/test_mpiex.py +64 -0
- parsl/tests/test_mpi_apps/test_resource_spec.py +42 -49
- parsl/tests/test_providers/test_kubernetes_provider.py +102 -0
- parsl/tests/test_providers/test_local_provider.py +3 -132
- parsl/tests/test_providers/test_pbspro_template.py +2 -3
- parsl/tests/test_providers/test_slurm_template.py +2 -3
- parsl/tests/test_providers/test_submiterror_deprecation.py +2 -1
- parsl/tests/test_python_apps/test_context_manager.py +128 -0
- parsl/tests/test_python_apps/test_dep_standard_futures.py +2 -1
- parsl/tests/test_python_apps/test_dependencies_deep.py +59 -0
- parsl/tests/test_python_apps/test_fail.py +0 -25
- parsl/tests/test_python_apps/test_futures.py +2 -1
- parsl/tests/test_python_apps/test_inputs_default.py +22 -0
- parsl/tests/test_python_apps/test_join.py +0 -1
- parsl/tests/test_python_apps/test_lifted.py +11 -7
- parsl/tests/test_python_apps/test_memoize_bad_id_for_memo.py +1 -0
- parsl/tests/test_python_apps/test_outputs.py +1 -1
- parsl/tests/test_python_apps/test_pluggable_future_resolution.py +161 -0
- parsl/tests/test_radical/test_mpi_funcs.py +1 -2
- parsl/tests/test_regression/test_1480.py +2 -1
- parsl/tests/test_regression/test_1653.py +2 -1
- parsl/tests/test_regression/test_226.py +1 -0
- parsl/tests/test_regression/test_2652.py +1 -0
- parsl/tests/test_regression/test_69a.py +0 -1
- parsl/tests/test_regression/test_854.py +4 -2
- parsl/tests/test_regression/test_97_parallelism_0.py +1 -2
- parsl/tests/test_regression/test_98.py +0 -1
- parsl/tests/test_scaling/test_block_error_handler.py +9 -4
- parsl/tests/test_scaling/test_regression_1621.py +11 -15
- parsl/tests/test_scaling/test_regression_3568_scaledown_vs_MISSING.py +84 -0
- parsl/tests/test_scaling/test_regression_3696_oscillation.py +103 -0
- parsl/tests/test_scaling/test_scale_down.py +2 -5
- parsl/tests/test_scaling/test_scale_down_htex_auto_scale.py +5 -8
- parsl/tests/test_scaling/test_scale_down_htex_unregistered.py +71 -0
- parsl/tests/test_scaling/test_shutdown_scalein.py +73 -0
- parsl/tests/test_scaling/test_worker_interchange_bad_messages_3262.py +90 -0
- parsl/tests/test_serialization/test_2555_caching_deserializer.py +1 -1
- parsl/tests/test_serialization/test_3495_deserialize_managerlost.py +47 -0
- parsl/tests/test_serialization/test_basic.py +2 -1
- parsl/tests/test_serialization/test_htex_code_cache.py +3 -4
- parsl/tests/test_serialization/test_pack_resource_spec.py +2 -1
- parsl/tests/test_serialization/test_proxystore_configured.py +10 -6
- parsl/tests/test_serialization/test_proxystore_impl.py +5 -3
- parsl/tests/test_shutdown/test_kill_monitoring.py +64 -0
- parsl/tests/test_staging/staging_provider.py +2 -2
- parsl/tests/test_staging/test_1316.py +3 -4
- parsl/tests/test_staging/test_docs_1.py +2 -1
- parsl/tests/test_staging/test_docs_2.py +2 -1
- parsl/tests/test_staging/test_elaborate_noop_file.py +2 -3
- parsl/tests/{test_data → test_staging}/test_file.py +6 -6
- parsl/tests/{test_data → test_staging}/test_output_chain_filenames.py +3 -0
- parsl/tests/test_staging/test_staging_ftp.py +1 -0
- parsl/tests/test_staging/test_staging_https.py +5 -2
- parsl/tests/test_staging/test_staging_stdout.py +64 -0
- parsl/tests/test_staging/test_zip_in.py +39 -0
- parsl/tests/test_staging/test_zip_out.py +110 -0
- parsl/tests/test_staging/test_zip_to_zip.py +41 -0
- parsl/tests/test_summary.py +2 -2
- parsl/tests/test_thread_parallelism.py +0 -1
- parsl/tests/test_threads/test_configs.py +1 -2
- parsl/tests/test_threads/test_lazy_errors.py +2 -2
- parsl/tests/test_utils/test_execute_wait.py +35 -0
- parsl/tests/test_utils/test_sanitize_dns.py +76 -0
- parsl/tests/unit/test_address.py +20 -0
- parsl/tests/unit/test_file.py +99 -0
- parsl/tests/unit/test_usage_tracking.py +66 -0
- parsl/usage_tracking/api.py +65 -0
- parsl/usage_tracking/levels.py +6 -0
- parsl/usage_tracking/usage.py +104 -62
- parsl/utils.py +137 -4
- parsl/version.py +1 -1
- {parsl-2024.3.18.data → parsl-2025.1.13.data}/scripts/exec_parsl_function.py +6 -5
- parsl-2025.1.13.data/scripts/interchange.py +649 -0
- {parsl-2024.3.18.data → parsl-2025.1.13.data}/scripts/process_worker_pool.py +77 -75
- parsl-2025.1.13.dist-info/METADATA +96 -0
- parsl-2025.1.13.dist-info/RECORD +462 -0
- {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/WHEEL +1 -1
- parsl/channels/__init__.py +0 -7
- parsl/channels/base.py +0 -141
- parsl/channels/errors.py +0 -113
- parsl/channels/local/local.py +0 -164
- parsl/channels/oauth_ssh/oauth_ssh.py +0 -110
- parsl/channels/ssh/ssh.py +0 -276
- parsl/channels/ssh_il/__init__.py +0 -0
- parsl/channels/ssh_il/ssh_il.py +0 -74
- parsl/configs/ad_hoc.py +0 -35
- parsl/executors/radical/rpex_master.py +0 -42
- parsl/monitoring/radios.py +0 -175
- parsl/providers/ad_hoc/__init__.py +0 -0
- parsl/providers/ad_hoc/ad_hoc.py +0 -248
- parsl/providers/cobalt/__init__.py +0 -0
- parsl/providers/cobalt/cobalt.py +0 -236
- parsl/providers/cobalt/template.py +0 -17
- parsl/tests/configs/ad_hoc_cluster_htex.py +0 -35
- parsl/tests/configs/cooley_htex.py +0 -37
- parsl/tests/configs/htex_ad_hoc_cluster.py +0 -28
- parsl/tests/configs/local_adhoc.py +0 -18
- parsl/tests/configs/swan_htex.py +0 -43
- parsl/tests/configs/theta.py +0 -37
- parsl/tests/integration/test_channels/__init__.py +0 -0
- parsl/tests/integration/test_channels/test_channels.py +0 -17
- parsl/tests/integration/test_channels/test_local_channel.py +0 -42
- parsl/tests/integration/test_channels/test_scp_1.py +0 -45
- parsl/tests/integration/test_channels/test_ssh_1.py +0 -40
- parsl/tests/integration/test_channels/test_ssh_errors.py +0 -46
- parsl/tests/integration/test_channels/test_ssh_file_transport.py +0 -41
- parsl/tests/integration/test_channels/test_ssh_interactive.py +0 -24
- parsl/tests/manual_tests/test_ad_hoc_htex.py +0 -48
- parsl/tests/manual_tests/test_fan_in_out_htex_remote.py +0 -88
- parsl/tests/manual_tests/test_oauth_ssh.py +0 -13
- parsl/tests/sites/test_local_adhoc.py +0 -61
- parsl/tests/test_channels/__init__.py +0 -0
- parsl/tests/test_channels/test_large_output.py +0 -22
- parsl/tests/test_data/__init__.py +0 -0
- parsl/tests/test_mpi_apps/test_mpi_mode_disabled.py +0 -51
- parsl/tests/test_providers/test_cobalt_deprecation_warning.py +0 -16
- parsl-2024.3.18.dist-info/METADATA +0 -98
- parsl-2024.3.18.dist-info/RECORD +0 -449
- parsl/{channels/local → monitoring/radios}/__init__.py +0 -0
- parsl/{channels/oauth_ssh → tests/test_shutdown}/__init__.py +0 -0
- parsl/tests/{test_data → test_staging}/test_file_apps.py +0 -0
- parsl/tests/{test_data → test_staging}/test_file_staging.py +0 -0
- parsl/{channels/ssh → tests/unit}/__init__.py +0 -0
- {parsl-2024.3.18.data → parsl-2025.1.13.data}/scripts/parsl_coprocess.py +1 -1
- {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/LICENSE +0 -0
- {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/entry_points.txt +0 -0
- {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/top_level.txt +0 -0
@@ -1,19 +1,20 @@
|
|
1
|
-
import parsl
|
2
|
-
import pytest
|
3
1
|
import time
|
4
2
|
|
5
|
-
|
6
|
-
from parsl.channels import LocalChannel
|
7
|
-
from parsl.launchers import SimpleLauncher
|
3
|
+
import pytest
|
8
4
|
|
5
|
+
import parsl
|
9
6
|
from parsl.config import Config
|
10
7
|
from parsl.executors import HighThroughputExecutor
|
8
|
+
from parsl.launchers import SimpleLauncher
|
9
|
+
from parsl.providers import LocalProvider
|
11
10
|
|
12
11
|
# this constant is used to scale some durations that happen
|
13
12
|
# based around the expected drain period: the drain period
|
14
13
|
# is TIME_CONST seconds, and the single executed task will
|
15
14
|
# last twice that many number of seconds.
|
16
|
-
TIME_CONST =
|
15
|
+
TIME_CONST = 4
|
16
|
+
|
17
|
+
CONNECTED_MANAGERS_POLL_MS = 100
|
17
18
|
|
18
19
|
|
19
20
|
def local_config():
|
@@ -26,7 +27,6 @@ def local_config():
|
|
26
27
|
cores_per_worker=1,
|
27
28
|
encrypted=True,
|
28
29
|
provider=LocalProvider(
|
29
|
-
channel=LocalChannel(),
|
30
30
|
init_blocks=1,
|
31
31
|
min_blocks=0,
|
32
32
|
max_blocks=0,
|
@@ -35,6 +35,7 @@ def local_config():
|
|
35
35
|
)
|
36
36
|
],
|
37
37
|
strategy='none',
|
38
|
+
strategy_period=0.1
|
38
39
|
)
|
39
40
|
|
40
41
|
|
@@ -51,7 +52,7 @@ def test_drain(try_assert):
|
|
51
52
|
|
52
53
|
# wait till we have a block running...
|
53
54
|
|
54
|
-
try_assert(lambda: len(htex.connected_managers()) == 1)
|
55
|
+
try_assert(lambda: len(htex.connected_managers()) == 1, check_period_ms=CONNECTED_MANAGERS_POLL_MS)
|
55
56
|
|
56
57
|
managers = htex.connected_managers()
|
57
58
|
assert managers[0]['active'], "The manager should be active"
|
@@ -62,7 +63,7 @@ def test_drain(try_assert):
|
|
62
63
|
time.sleep(TIME_CONST)
|
63
64
|
|
64
65
|
# this assert should happen *very fast* after the above delay...
|
65
|
-
try_assert(lambda: htex.connected_managers()[0]['draining'], timeout_ms=500)
|
66
|
+
try_assert(lambda: htex.connected_managers()[0]['draining'], timeout_ms=500, check_period_ms=CONNECTED_MANAGERS_POLL_MS)
|
66
67
|
|
67
68
|
# and the test task should still be running...
|
68
69
|
assert not fut.done(), "The test task should still be running"
|
@@ -75,4 +76,4 @@ def test_drain(try_assert):
|
|
75
76
|
# connected managers.
|
76
77
|
# As with the above draining assert, this should happen very fast after
|
77
78
|
# the task ends.
|
78
|
-
try_assert(lambda: len(htex.connected_managers()) == 0, timeout_ms=500)
|
79
|
+
try_assert(lambda: len(htex.connected_managers()) == 0, timeout_ms=500, check_period_ms=CONNECTED_MANAGERS_POLL_MS)
|
@@ -1,12 +1,12 @@
|
|
1
|
+
import logging
|
1
2
|
import pathlib
|
2
|
-
import
|
3
|
+
from subprocess import Popen, TimeoutExpired
|
4
|
+
from typing import Optional, Sequence
|
3
5
|
from unittest import mock
|
4
6
|
|
5
7
|
import pytest
|
6
8
|
|
7
|
-
from parsl import curvezmq
|
8
|
-
from parsl import HighThroughputExecutor
|
9
|
-
from parsl.multiprocessing import ForkProcess
|
9
|
+
from parsl import HighThroughputExecutor, curvezmq
|
10
10
|
|
11
11
|
_MOCK_BASE = "parsl.executors.high_throughput.executor"
|
12
12
|
|
@@ -72,51 +72,77 @@ def test_htex_start_encrypted(
|
|
72
72
|
@pytest.mark.local
|
73
73
|
@pytest.mark.parametrize("started", (True, False))
|
74
74
|
@pytest.mark.parametrize("timeout_expires", (True, False))
|
75
|
-
@mock.patch(f"{_MOCK_BASE}.logger")
|
76
75
|
def test_htex_shutdown(
|
77
|
-
mock_logger: mock.MagicMock,
|
78
76
|
started: bool,
|
79
77
|
timeout_expires: bool,
|
80
78
|
htex: HighThroughputExecutor,
|
79
|
+
caplog
|
81
80
|
):
|
82
|
-
mock_ix_proc = mock.Mock(spec=
|
81
|
+
mock_ix_proc = mock.Mock(spec=Popen)
|
83
82
|
|
84
83
|
if started:
|
85
84
|
htex.interchange_proc = mock_ix_proc
|
86
|
-
|
85
|
+
|
86
|
+
# This will, in the absence of any exit trigger, block forever if
|
87
|
+
# no timeout is given and if the interchange does not terminate.
|
88
|
+
# Raise an exception to report that, rather than actually block,
|
89
|
+
# and hope that nothing is catching that exception.
|
90
|
+
|
91
|
+
# this function implements the behaviour if the interchange has
|
92
|
+
# not received a termination call
|
93
|
+
def proc_wait_alive(timeout):
|
94
|
+
if timeout:
|
95
|
+
raise TimeoutExpired(cmd="mock-interchange", timeout=timeout)
|
96
|
+
else:
|
97
|
+
raise RuntimeError("This wait call would hang forever")
|
98
|
+
|
99
|
+
def proc_wait_terminated(timeout):
|
100
|
+
return 0
|
101
|
+
|
102
|
+
mock_ix_proc.wait.side_effect = proc_wait_alive
|
87
103
|
|
88
104
|
if not timeout_expires:
|
89
105
|
# Simulate termination of the Interchange process
|
90
106
|
def kill_interchange(*args, **kwargs):
|
91
|
-
mock_ix_proc.
|
107
|
+
mock_ix_proc.wait.side_effect = proc_wait_terminated
|
92
108
|
|
93
109
|
mock_ix_proc.terminate.side_effect = kill_interchange
|
94
110
|
|
95
|
-
|
111
|
+
with caplog.at_level(logging.INFO):
|
112
|
+
htex.shutdown()
|
96
113
|
|
97
|
-
mock_logs = mock_logger.info.call_args_list
|
98
114
|
if started:
|
99
115
|
assert mock_ix_proc.terminate.called
|
100
|
-
assert mock_ix_proc.
|
101
|
-
assert {"timeout": 10} == mock_ix_proc.
|
116
|
+
assert mock_ix_proc.wait.called
|
117
|
+
assert {"timeout": 10} == mock_ix_proc.wait.call_args[1]
|
102
118
|
if timeout_expires:
|
103
|
-
assert "Unable to terminate Interchange" in
|
119
|
+
assert "Unable to terminate Interchange" in caplog.text
|
104
120
|
assert mock_ix_proc.kill.called
|
105
|
-
assert "Attempting" in
|
106
|
-
assert "Finished" in
|
121
|
+
assert "Attempting HighThroughputExecutor shutdown" in caplog.text
|
122
|
+
assert "Finished HighThroughputExecutor shutdown" in caplog.text
|
107
123
|
else:
|
108
124
|
assert not mock_ix_proc.terminate.called
|
109
|
-
assert not mock_ix_proc.
|
110
|
-
assert "has not started" in
|
125
|
+
assert not mock_ix_proc.wait.called
|
126
|
+
assert "HighThroughputExecutor has not started" in caplog.text
|
111
127
|
|
112
128
|
|
113
129
|
@pytest.mark.local
|
114
|
-
|
115
|
-
|
116
|
-
|
130
|
+
@pytest.mark.parametrize("cmd", (None, "custom-launch-cmd"))
|
131
|
+
def test_htex_worker_pool_launch_cmd(cmd: Optional[str]):
|
132
|
+
if cmd:
|
133
|
+
htex = HighThroughputExecutor(launch_cmd=cmd)
|
134
|
+
assert htex.launch_cmd == cmd
|
135
|
+
else:
|
136
|
+
htex = HighThroughputExecutor()
|
137
|
+
assert htex.launch_cmd.startswith("process_worker_pool.py")
|
117
138
|
|
118
|
-
warning_msg = "max_workers is deprecated"
|
119
|
-
assert any(warning_msg in str(warning.message) for warning in record)
|
120
139
|
|
121
|
-
|
122
|
-
|
140
|
+
@pytest.mark.local
|
141
|
+
@pytest.mark.parametrize("cmd", (None, ["custom", "launch", "cmd"]))
|
142
|
+
def test_htex_interchange_launch_cmd(cmd: Optional[Sequence[str]]):
|
143
|
+
if cmd:
|
144
|
+
htex = HighThroughputExecutor(interchange_launch_cmd=cmd)
|
145
|
+
assert htex.interchange_launch_cmd == cmd
|
146
|
+
else:
|
147
|
+
htex = HighThroughputExecutor()
|
148
|
+
assert htex.interchange_launch_cmd == ["interchange.py"]
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import parsl
|
6
|
+
from parsl.app.app import bash_app, python_app
|
7
|
+
from parsl.config import Config
|
8
|
+
from parsl.executors import HighThroughputExecutor
|
9
|
+
from parsl.executors.high_throughput.manager_selector import (
|
10
|
+
BlockIdManagerSelector,
|
11
|
+
ManagerSelector,
|
12
|
+
)
|
13
|
+
from parsl.launchers import WrappedLauncher
|
14
|
+
from parsl.providers import LocalProvider
|
15
|
+
from parsl.usage_tracking.levels import LEVEL_1
|
16
|
+
|
17
|
+
BLOCK_COUNT = 2
|
18
|
+
|
19
|
+
|
20
|
+
@parsl.python_app
|
21
|
+
def get_worker_pid():
|
22
|
+
import os
|
23
|
+
return os.environ.get('PARSL_WORKER_BLOCK_ID')
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.mark.local
|
27
|
+
def test_block_id_selection(try_assert):
|
28
|
+
htex = HighThroughputExecutor(
|
29
|
+
label="htex_local",
|
30
|
+
max_workers_per_node=1,
|
31
|
+
manager_selector=BlockIdManagerSelector(),
|
32
|
+
provider=LocalProvider(
|
33
|
+
init_blocks=BLOCK_COUNT,
|
34
|
+
max_blocks=BLOCK_COUNT,
|
35
|
+
min_blocks=BLOCK_COUNT,
|
36
|
+
),
|
37
|
+
)
|
38
|
+
|
39
|
+
config = Config(
|
40
|
+
executors=[htex],
|
41
|
+
usage_tracking=LEVEL_1,
|
42
|
+
)
|
43
|
+
|
44
|
+
with parsl.load(config):
|
45
|
+
blockids = []
|
46
|
+
try_assert(lambda: len(htex.connected_managers()) == BLOCK_COUNT, timeout_ms=20000)
|
47
|
+
for i in range(10):
|
48
|
+
future = get_worker_pid()
|
49
|
+
blockids.append(future.result())
|
50
|
+
|
51
|
+
assert all(blockid == "1" for blockid in blockids)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import logging
|
2
|
+
import sys
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl.app.app import python_app
|
8
|
+
from parsl.tests.configs.htex_local import fresh_config
|
9
|
+
|
10
|
+
|
11
|
+
def local_config():
|
12
|
+
config = fresh_config()
|
13
|
+
config.executors[0].poll_period = 1
|
14
|
+
config.executors[0].max_workers_per_node = 1
|
15
|
+
return config
|
16
|
+
|
17
|
+
|
18
|
+
@python_app
|
19
|
+
def dummy():
|
20
|
+
pass
|
21
|
+
|
22
|
+
|
23
|
+
@pytest.mark.local
|
24
|
+
def test_connected_managers():
|
25
|
+
|
26
|
+
# Run dummy function to ensure a manager is online
|
27
|
+
x = dummy()
|
28
|
+
assert x.result() is None
|
29
|
+
executor = parsl.dfk().executors['htex_local']
|
30
|
+
manager_info_list = executor.connected_managers()
|
31
|
+
assert len(manager_info_list) == 1
|
32
|
+
manager_info = manager_info_list[0]
|
33
|
+
assert 'python_version' in manager_info
|
34
|
+
assert 'parsl_version' in manager_info
|
35
|
+
assert manager_info['parsl_version'] == parsl.__version__
|
36
|
+
assert manager_info['python_version'] == f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
@@ -5,18 +5,12 @@ from parsl.app.app import python_app
|
|
5
5
|
from parsl.tests.configs.htex_local import fresh_config
|
6
6
|
|
7
7
|
|
8
|
-
def
|
8
|
+
def local_config():
|
9
9
|
config = fresh_config()
|
10
10
|
config.executors[0].poll_period = 1
|
11
11
|
config.executors[0].max_workers_per_node = 1
|
12
12
|
config.executors[0].launch_cmd = "executable_that_hopefully_does_not_exist_1030509.py"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def local_teardown():
|
17
|
-
|
18
|
-
parsl.dfk().cleanup()
|
19
|
-
parsl.clear()
|
13
|
+
return config
|
20
14
|
|
21
15
|
|
22
16
|
@python_app
|
@@ -37,7 +31,3 @@ def test_that_it_fails():
|
|
37
31
|
raise Exception("The app somehow ran without a valid worker")
|
38
32
|
|
39
33
|
assert parsl.dfk().config.executors[0]._executor_bad_state.is_set()
|
40
|
-
|
41
|
-
# htex needs shutting down explicitly because dfk.cleanup() will not
|
42
|
-
# do that, as it is in bad state
|
43
|
-
parsl.dfk().config.executors[0].shutdown()
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import logging
|
2
|
-
|
2
|
+
|
3
3
|
import pytest
|
4
|
-
|
4
|
+
|
5
|
+
import parsl
|
5
6
|
from parsl import Config
|
6
|
-
from parsl.
|
7
|
+
from parsl.executors import HighThroughputExecutor
|
7
8
|
from parsl.executors.errors import BadStateException
|
8
|
-
from parsl.jobs.states import
|
9
|
+
from parsl.jobs.states import JobState, JobStatus
|
10
|
+
from parsl.providers import LocalProvider
|
9
11
|
|
10
12
|
|
11
13
|
def local_config():
|
@@ -19,16 +21,14 @@ def local_config():
|
|
19
21
|
poll_period=100,
|
20
22
|
max_workers_per_node=1,
|
21
23
|
provider=LocalProvider(
|
22
|
-
worker_init="
|
23
|
-
init_blocks=2
|
24
|
-
max_blocks=4,
|
25
|
-
min_blocks=0,
|
24
|
+
worker_init="exit 0",
|
25
|
+
init_blocks=2
|
26
26
|
),
|
27
27
|
)
|
28
28
|
],
|
29
29
|
run_dir="/tmp/test_htex",
|
30
30
|
max_idletime=0.5,
|
31
|
-
strategy='
|
31
|
+
strategy='none',
|
32
32
|
)
|
33
33
|
|
34
34
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import queue
|
2
|
+
from unittest import mock
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
from parsl.executors import HighThroughputExecutor
|
7
|
+
from parsl.executors.errors import InvalidResourceSpecification
|
8
|
+
|
9
|
+
|
10
|
+
def double(x):
|
11
|
+
return x * 2
|
12
|
+
|
13
|
+
|
14
|
+
@pytest.mark.local
|
15
|
+
def test_submit_calls_validate():
|
16
|
+
|
17
|
+
htex = HighThroughputExecutor()
|
18
|
+
htex.outgoing_q = mock.Mock(spec=queue.Queue)
|
19
|
+
htex.validate_resource_spec = mock.Mock(spec=htex.validate_resource_spec)
|
20
|
+
|
21
|
+
res_spec = {}
|
22
|
+
htex.submit(double, res_spec, (5,), {})
|
23
|
+
htex.validate_resource_spec.assert_called()
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.mark.local
|
27
|
+
def test_resource_spec_validation():
|
28
|
+
htex = HighThroughputExecutor()
|
29
|
+
ret_val = htex.validate_resource_spec({})
|
30
|
+
assert ret_val is None
|
31
|
+
|
32
|
+
|
33
|
+
@pytest.mark.local
|
34
|
+
def test_resource_spec_validation_one_key():
|
35
|
+
htex = HighThroughputExecutor()
|
36
|
+
ret_val = htex.validate_resource_spec({"priority": 2})
|
37
|
+
assert ret_val is None
|
38
|
+
|
39
|
+
|
40
|
+
@pytest.mark.local
|
41
|
+
def test_resource_spec_validation_bad_keys():
|
42
|
+
htex = HighThroughputExecutor()
|
43
|
+
|
44
|
+
with pytest.raises(InvalidResourceSpecification):
|
45
|
+
htex.validate_resource_spec({"num_nodes": 2})
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import logging
|
1
2
|
import pathlib
|
2
3
|
from typing import Optional
|
3
4
|
from unittest import mock
|
@@ -8,6 +9,24 @@ import zmq
|
|
8
9
|
|
9
10
|
from parsl import curvezmq
|
10
11
|
from parsl.executors.high_throughput.interchange import Interchange
|
12
|
+
from parsl.executors.high_throughput.manager_selector import RandomManagerSelector
|
13
|
+
|
14
|
+
|
15
|
+
def make_interchange(*, interchange_address: Optional[str], cert_dir: Optional[str]) -> Interchange:
|
16
|
+
return Interchange(interchange_address=interchange_address,
|
17
|
+
cert_dir=cert_dir,
|
18
|
+
client_address="127.0.0.1",
|
19
|
+
client_ports=(50055, 50056, 50057),
|
20
|
+
worker_ports=None,
|
21
|
+
worker_port_range=(54000, 55000),
|
22
|
+
hub_address=None,
|
23
|
+
hub_zmq_port=None,
|
24
|
+
heartbeat_threshold=60,
|
25
|
+
logdir=".",
|
26
|
+
logging_level=logging.INFO,
|
27
|
+
manager_selector=RandomManagerSelector(),
|
28
|
+
poll_period=10,
|
29
|
+
run_id="test_run_id")
|
11
30
|
|
12
31
|
|
13
32
|
@pytest.fixture
|
@@ -31,7 +50,7 @@ def test_interchange_curvezmq_sockets(
|
|
31
50
|
mock_socket: mock.MagicMock, cert_dir: Optional[str], encrypted: bool
|
32
51
|
):
|
33
52
|
address = "127.0.0.1"
|
34
|
-
ix =
|
53
|
+
ix = make_interchange(interchange_address=address, cert_dir=cert_dir)
|
35
54
|
assert isinstance(ix.zmq_context, curvezmq.ServerContext)
|
36
55
|
assert ix.zmq_context.encrypted is encrypted
|
37
56
|
assert mock_socket.call_count == 5
|
@@ -40,7 +59,7 @@ def test_interchange_curvezmq_sockets(
|
|
40
59
|
@pytest.mark.local
|
41
60
|
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
42
61
|
def test_interchange_binding_no_address(cert_dir: Optional[str]):
|
43
|
-
ix =
|
62
|
+
ix = make_interchange(interchange_address=None, cert_dir=cert_dir)
|
44
63
|
assert ix.interchange_address == "*"
|
45
64
|
|
46
65
|
|
@@ -49,17 +68,18 @@ def test_interchange_binding_no_address(cert_dir: Optional[str]):
|
|
49
68
|
def test_interchange_binding_with_address(cert_dir: Optional[str]):
|
50
69
|
# Using loopback address
|
51
70
|
address = "127.0.0.1"
|
52
|
-
ix =
|
71
|
+
ix = make_interchange(interchange_address=address, cert_dir=cert_dir)
|
53
72
|
assert ix.interchange_address == address
|
54
73
|
|
55
74
|
|
75
|
+
@pytest.mark.skip("This behaviour is possibly unexpected. See issue #3037")
|
56
76
|
@pytest.mark.local
|
57
77
|
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
58
78
|
def test_interchange_binding_with_non_ipv4_address(cert_dir: Optional[str]):
|
59
79
|
# Confirm that a ipv4 address is required
|
60
80
|
address = "localhost"
|
61
81
|
with pytest.raises(zmq.error.ZMQError):
|
62
|
-
|
82
|
+
make_interchange(interchange_address=address, cert_dir=cert_dir)
|
63
83
|
|
64
84
|
|
65
85
|
@pytest.mark.local
|
@@ -67,8 +87,8 @@ def test_interchange_binding_with_non_ipv4_address(cert_dir: Optional[str]):
|
|
67
87
|
def test_interchange_binding_bad_address(cert_dir: Optional[str]):
|
68
88
|
"""Confirm that we raise a ZMQError when a bad address is supplied"""
|
69
89
|
address = "550.0.0.0"
|
70
|
-
with pytest.raises(
|
71
|
-
|
90
|
+
with pytest.raises(ValueError):
|
91
|
+
make_interchange(interchange_address=address, cert_dir=cert_dir)
|
72
92
|
|
73
93
|
|
74
94
|
@pytest.mark.local
|
@@ -76,11 +96,12 @@ def test_interchange_binding_bad_address(cert_dir: Optional[str]):
|
|
76
96
|
def test_limited_interface_binding(cert_dir: Optional[str]):
|
77
97
|
"""When address is specified the worker_port would be bound to it rather than to 0.0.0.0"""
|
78
98
|
address = "127.0.0.1"
|
79
|
-
ix =
|
99
|
+
ix = make_interchange(interchange_address=address, cert_dir=cert_dir)
|
80
100
|
ix.worker_result_port
|
81
101
|
proc = psutil.Process()
|
82
102
|
conns = proc.connections(kind="tcp")
|
83
103
|
|
84
104
|
matched_conns = [conn for conn in conns if conn.laddr.port == ix.worker_result_port]
|
85
105
|
assert len(matched_conns) == 1
|
86
|
-
|
106
|
+
# laddr.ip can return ::ffff:127.0.0.1 when using IPv6
|
107
|
+
assert address in matched_conns[0].laddr.ip
|
@@ -2,10 +2,11 @@
|
|
2
2
|
"""
|
3
3
|
|
4
4
|
import os
|
5
|
-
import parsl
|
6
|
-
import pytest
|
7
5
|
import time
|
8
6
|
|
7
|
+
import pytest
|
8
|
+
|
9
|
+
import parsl
|
9
10
|
from parsl.tests.configs.htex_local_alternate import fresh_config
|
10
11
|
|
11
12
|
|
@@ -61,13 +62,12 @@ def test_app_name(get_app, expected_name, expected_result, tmpd_cwd):
|
|
61
62
|
assert app().result() == expected_result
|
62
63
|
|
63
64
|
parsl.dfk().cleanup()
|
64
|
-
parsl.clear()
|
65
65
|
|
66
66
|
engine = sqlalchemy.create_engine(c.monitoring.logging_endpoint)
|
67
67
|
with engine.begin() as connection:
|
68
68
|
|
69
69
|
def count_rows(table: str):
|
70
|
-
result = connection.execute(f"SELECT COUNT(*) FROM {table}")
|
70
|
+
result = connection.execute(sqlalchemy.text(f"SELECT COUNT(*) FROM {table}"))
|
71
71
|
(c, ) = result.first()
|
72
72
|
return c
|
73
73
|
|
@@ -81,6 +81,6 @@ def test_app_name(get_app, expected_name, expected_result, tmpd_cwd):
|
|
81
81
|
assert count_rows("try") == 1
|
82
82
|
|
83
83
|
# ... and has the expected name.
|
84
|
-
result = connection.execute("SELECT task_func_name FROM task")
|
84
|
+
result = connection.execute(sqlalchemy.text("SELECT task_func_name FROM task"))
|
85
85
|
(c, ) = result.first()
|
86
86
|
assert c == expected_name
|
@@ -1,10 +1,13 @@
|
|
1
|
-
import logging
|
2
1
|
import os
|
3
|
-
import parsl
|
4
|
-
import pytest
|
5
2
|
import time
|
6
3
|
|
7
|
-
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl import HighThroughputExecutor
|
8
|
+
from parsl.config import Config
|
9
|
+
from parsl.executors.taskvine import TaskVineExecutor, TaskVineManagerConfig
|
10
|
+
from parsl.monitoring import MonitoringHub
|
8
11
|
|
9
12
|
|
10
13
|
@parsl.python_app
|
@@ -18,34 +21,79 @@ def this_app():
|
|
18
21
|
return 5
|
19
22
|
|
20
23
|
|
24
|
+
# The below fresh configs are for use in parametrization, and should return
|
25
|
+
# a configuration that is suitably configured for monitoring.
|
26
|
+
|
27
|
+
def htex_config():
|
28
|
+
"""This config will use htex's default htex-specific monitoring radio mode"""
|
29
|
+
from parsl.tests.configs.htex_local_alternate import fresh_config
|
30
|
+
return fresh_config()
|
31
|
+
|
32
|
+
|
33
|
+
def htex_udp_config():
|
34
|
+
"""This config will force UDP"""
|
35
|
+
from parsl.tests.configs.htex_local_alternate import fresh_config
|
36
|
+
c = fresh_config()
|
37
|
+
assert len(c.executors) == 1
|
38
|
+
|
39
|
+
assert c.executors[0].radio_mode == "htex", "precondition: htex has a radio mode attribute, configured for htex radio"
|
40
|
+
c.executors[0].radio_mode = "udp"
|
41
|
+
|
42
|
+
return c
|
43
|
+
|
44
|
+
|
45
|
+
def htex_filesystem_config():
|
46
|
+
"""This config will force filesystem radio"""
|
47
|
+
from parsl.tests.configs.htex_local_alternate import fresh_config
|
48
|
+
c = fresh_config()
|
49
|
+
assert len(c.executors) == 1
|
50
|
+
|
51
|
+
assert c.executors[0].radio_mode == "htex", "precondition: htex has a radio mode attribute, configured for htex radio"
|
52
|
+
c.executors[0].radio_mode = "filesystem"
|
53
|
+
|
54
|
+
return c
|
55
|
+
|
56
|
+
|
57
|
+
def workqueue_config():
|
58
|
+
from parsl.tests.configs.workqueue_ex import fresh_config
|
59
|
+
c = fresh_config()
|
60
|
+
c.monitoring = MonitoringHub(
|
61
|
+
hub_address="localhost",
|
62
|
+
resource_monitoring_interval=1)
|
63
|
+
return c
|
64
|
+
|
65
|
+
|
66
|
+
def taskvine_config():
|
67
|
+
c = Config(executors=[TaskVineExecutor(manager_config=TaskVineManagerConfig(port=9000),
|
68
|
+
worker_launch_method='provider')],
|
69
|
+
|
70
|
+
monitoring=MonitoringHub(hub_address="localhost",
|
71
|
+
resource_monitoring_interval=1))
|
72
|
+
return c
|
73
|
+
|
74
|
+
|
21
75
|
@pytest.mark.local
|
22
|
-
|
76
|
+
@pytest.mark.parametrize("fresh_config", [htex_config, htex_filesystem_config, htex_udp_config, workqueue_config, taskvine_config])
|
77
|
+
def test_row_counts(tmpd_cwd, fresh_config):
|
23
78
|
# this is imported here rather than at module level because
|
24
79
|
# it isn't available in a plain parsl install, so this module
|
25
80
|
# would otherwise fail to import and break even a basic test
|
26
81
|
# run.
|
27
82
|
import sqlalchemy
|
28
83
|
from sqlalchemy import text
|
29
|
-
from parsl.tests.configs.htex_local_alternate import fresh_config
|
30
84
|
|
31
|
-
|
32
|
-
logger.info("Monitoring database already exists - deleting")
|
33
|
-
os.remove("runinfo/monitoring.db")
|
85
|
+
db_url = f"sqlite:///{tmpd_cwd}/monitoring.db"
|
34
86
|
|
35
|
-
|
36
|
-
|
87
|
+
config = fresh_config()
|
88
|
+
config.run_dir = tmpd_cwd
|
89
|
+
config.monitoring.logging_endpoint = db_url
|
37
90
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
logger.info("cleaning up parsl")
|
42
|
-
parsl.dfk().cleanup()
|
43
|
-
parsl.clear()
|
91
|
+
with parsl.load(config):
|
92
|
+
assert this_app().result() == 5
|
44
93
|
|
45
94
|
# at this point, we should find one row in the monitoring database.
|
46
95
|
|
47
|
-
|
48
|
-
engine = sqlalchemy.create_engine("sqlite:///runinfo/monitoring.db")
|
96
|
+
engine = sqlalchemy.create_engine(db_url)
|
49
97
|
with engine.begin() as connection:
|
50
98
|
|
51
99
|
result = connection.execute(text("SELECT COUNT(*) FROM workflow"))
|
@@ -67,10 +115,12 @@ def test_row_counts():
|
|
67
115
|
(c, ) = result.first()
|
68
116
|
assert c == 0
|
69
117
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
118
|
+
if isinstance(config.executors[0], HighThroughputExecutor):
|
119
|
+
# The node table is specific to the HighThroughputExecutor
|
120
|
+
# Two entries: one showing manager active, one inactive
|
121
|
+
result = connection.execute(text("SELECT COUNT(*) FROM node"))
|
122
|
+
(c, ) = result.first()
|
123
|
+
assert c == 2
|
74
124
|
|
75
125
|
# There should be one block polling status
|
76
126
|
# local provider has a status_polling_interval of 5s
|
@@ -81,5 +131,3 @@ def test_row_counts():
|
|
81
131
|
result = connection.execute(text("SELECT COUNT(*) FROM resource"))
|
82
132
|
(c, ) = result.first()
|
83
133
|
assert c >= 1
|
84
|
-
|
85
|
-
logger.info("all done")
|