parsl 2024.3.11__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 +29 -7
- 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 +57 -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 +262 -224
- 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 +316 -282
- parsl/executors/high_throughput/interchange.py +158 -167
- parsl/executors/high_throughput/manager_record.py +5 -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 +115 -77
- 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 +41 -57
- parsl/executors/taskvine/factory.py +1 -1
- parsl/executors/taskvine/factory_config.py +1 -1
- parsl/executors/taskvine/manager.py +18 -13
- parsl/executors/taskvine/manager_config.py +9 -5
- 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 +30 -113
- 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 +6 -12
- parsl/log_utils.py +9 -6
- parsl/monitoring/db_manager.py +59 -95
- parsl/monitoring/errors.py +6 -0
- parsl/monitoring/monitoring.py +87 -356
- 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 +11 -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 -8
- 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 +4 -12
- 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 +2 -8
- 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 +79 -0
- 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 +86 -0
- 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 +6 -18
- 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 +139 -6
- parsl/version.py +1 -1
- {parsl-2024.3.11.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.11.data → parsl-2025.1.13.data}/scripts/process_worker_pool.py +115 -77
- parsl-2025.1.13.dist-info/METADATA +96 -0
- parsl-2025.1.13.dist-info/RECORD +462 -0
- {parsl-2024.3.11.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.11.dist-info/METADATA +0 -98
- parsl-2024.3.11.dist-info/RECORD +0 -447
- 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.11.data → parsl-2025.1.13.data}/scripts/parsl_coprocess.py +1 -1
- {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/LICENSE +0 -0
- {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/entry_points.txt +0 -0
- {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/top_level.txt +0 -0
@@ -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")
|
@@ -1,9 +1,11 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
import parsl
|
4
|
-
import pytest
|
5
3
|
import time
|
6
4
|
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
import parsl
|
8
|
+
|
7
9
|
logger = logging.getLogger(__name__)
|
8
10
|
|
9
11
|
|
@@ -14,9 +16,10 @@ def this_app():
|
|
14
16
|
|
15
17
|
@pytest.mark.local
|
16
18
|
def test_row_counts():
|
17
|
-
from parsl.tests.configs.htex_local_alternate import fresh_config
|
18
19
|
import sqlalchemy
|
19
20
|
from sqlalchemy import text
|
21
|
+
|
22
|
+
from parsl.tests.configs.htex_local_alternate import fresh_config
|
20
23
|
if os.path.exists("runinfo/monitoring.db"):
|
21
24
|
logger.info("Monitoring database already exists - deleting")
|
22
25
|
os.remove("runinfo/monitoring.db")
|
@@ -63,7 +66,6 @@ def test_row_counts():
|
|
63
66
|
|
64
67
|
logger.info("cleaning up parsl")
|
65
68
|
parsl.dfk().cleanup()
|
66
|
-
parsl.clear()
|
67
69
|
|
68
70
|
# at this point, we should find data consistent with executing one
|
69
71
|
# task in the database.
|
@@ -1,10 +1,13 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
import parsl
|
4
|
-
import pytest
|
5
3
|
import socket
|
6
4
|
import time
|
7
5
|
|
6
|
+
import pytest
|
7
|
+
import zmq
|
8
|
+
|
9
|
+
import parsl
|
10
|
+
|
8
11
|
logger = logging.getLogger(__name__)
|
9
12
|
|
10
13
|
|
@@ -15,10 +18,11 @@ def this_app():
|
|
15
18
|
|
16
19
|
@pytest.mark.local
|
17
20
|
def test_row_counts():
|
18
|
-
from parsl.tests.configs.htex_local_alternate import fresh_config
|
19
21
|
import sqlalchemy
|
20
22
|
from sqlalchemy import text
|
21
23
|
|
24
|
+
from parsl.tests.configs.htex_local_alternate import fresh_config
|
25
|
+
|
22
26
|
if os.path.exists("runinfo/monitoring.db"):
|
23
27
|
logger.info("Monitoring database already exists - deleting")
|
24
28
|
os.remove("runinfo/monitoring.db")
|
@@ -40,16 +44,24 @@ def test_row_counts():
|
|
40
44
|
# the latter is what i'm most suspicious of in my present investigation
|
41
45
|
|
42
46
|
# dig out the interchange port...
|
43
|
-
hub_address = parsl.dfk().hub_address
|
44
|
-
|
47
|
+
hub_address = parsl.dfk().monitoring.hub_address
|
48
|
+
hub_zmq_port = parsl.dfk().monitoring.hub_zmq_port
|
45
49
|
|
46
50
|
# this will send a string to a new socket connection
|
47
51
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
48
|
-
s.connect((hub_address,
|
52
|
+
s.connect((hub_address, hub_zmq_port))
|
49
53
|
s.sendall(b'fuzzing\r')
|
50
54
|
|
55
|
+
context = zmq.Context()
|
56
|
+
channel_timeout = 10000 # in milliseconds
|
57
|
+
hub_channel = context.socket(zmq.DEALER)
|
58
|
+
hub_channel.setsockopt(zmq.LINGER, 0)
|
59
|
+
hub_channel.set_hwm(0)
|
60
|
+
hub_channel.setsockopt(zmq.SNDTIMEO, channel_timeout)
|
61
|
+
hub_channel.connect("tcp://{}:{}".format(hub_address, hub_zmq_port))
|
62
|
+
|
51
63
|
# this will send a non-object down the DFK's existing ZMQ connection
|
52
|
-
|
64
|
+
hub_channel.send(b'FuzzyByte\rSTREAM')
|
53
65
|
|
54
66
|
# This following attack is commented out, because monitoring is not resilient
|
55
67
|
# to this.
|
@@ -74,7 +86,6 @@ def test_row_counts():
|
|
74
86
|
|
75
87
|
logger.info("cleaning up parsl")
|
76
88
|
parsl.dfk().cleanup()
|
77
|
-
parsl.clear()
|
78
89
|
|
79
90
|
# at this point, we should find one row in the monitoring database.
|
80
91
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import time
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
import parsl
|
8
|
+
from parsl.config import Config
|
9
|
+
from parsl.executors import HighThroughputExecutor
|
10
|
+
from parsl.launchers import SimpleLauncher
|
11
|
+
from parsl.monitoring import MonitoringHub
|
12
|
+
from parsl.providers import LocalProvider
|
13
|
+
|
14
|
+
|
15
|
+
def fresh_config(run_dir, strategy, db_url):
|
16
|
+
return Config(
|
17
|
+
run_dir=os.fspath(run_dir),
|
18
|
+
executors=[
|
19
|
+
HighThroughputExecutor(
|
20
|
+
label="htex_local",
|
21
|
+
cores_per_worker=1,
|
22
|
+
encrypted=True,
|
23
|
+
provider=LocalProvider(
|
24
|
+
init_blocks=1,
|
25
|
+
# min and max are set to 0 to ensure that we don't get
|
26
|
+
# a block from ongoing strategy scaling, only from
|
27
|
+
# init_blocks
|
28
|
+
min_blocks=0,
|
29
|
+
max_blocks=0,
|
30
|
+
launcher=SimpleLauncher(),
|
31
|
+
),
|
32
|
+
)
|
33
|
+
],
|
34
|
+
strategy=strategy,
|
35
|
+
strategy_period=0.1,
|
36
|
+
monitoring=MonitoringHub(
|
37
|
+
hub_address="localhost",
|
38
|
+
logging_endpoint=db_url
|
39
|
+
)
|
40
|
+
)
|
41
|
+
|
42
|
+
|
43
|
+
@parsl.python_app
|
44
|
+
def this_app():
|
45
|
+
pass
|
46
|
+
|
47
|
+
|
48
|
+
@pytest.mark.local
|
49
|
+
@pytest.mark.parametrize("strategy", ('none', 'simple', 'htex_auto_scale'))
|
50
|
+
def test_row_counts(tmpd_cwd, strategy):
|
51
|
+
# this is imported here rather than at module level because
|
52
|
+
# it isn't available in a plain parsl install, so this module
|
53
|
+
# would otherwise fail to import and break even a basic test
|
54
|
+
# run.
|
55
|
+
import sqlalchemy
|
56
|
+
from sqlalchemy import text
|
57
|
+
|
58
|
+
db_url = f"sqlite:///{tmpd_cwd}/monitoring.db"
|
59
|
+
with parsl.load(fresh_config(tmpd_cwd, strategy, db_url)):
|
60
|
+
dfk = parsl.dfk()
|
61
|
+
run_id = dfk.run_id
|
62
|
+
|
63
|
+
this_app().result()
|
64
|
+
|
65
|
+
engine = sqlalchemy.create_engine(db_url)
|
66
|
+
with engine.begin() as connection:
|
67
|
+
|
68
|
+
binds = {"run_id": run_id}
|
69
|
+
|
70
|
+
result = connection.execute(text("SELECT COUNT(DISTINCT block_id) FROM block WHERE run_id = :run_id"), binds)
|
71
|
+
(c, ) = result.first()
|
72
|
+
assert c == 1, "We should see a single block in this database"
|
73
|
+
|
74
|
+
result = connection.execute(text("SELECT COUNT(*) FROM block WHERE block_id = 0 AND status = 'PENDING' AND run_id = :run_id"), binds)
|
75
|
+
(c, ) = result.first()
|
76
|
+
assert c == 1, "There should be a single pending status"
|
77
|
+
|
78
|
+
result = connection.execute(text("SELECT COUNT(*) FROM block WHERE block_id = 0 AND status = 'SCALED_IN' AND run_id = :run_id"), binds)
|
79
|
+
(c, ) = result.first()
|
80
|
+
assert c == 1, "There should be a single cancelled status"
|
@@ -1,11 +1,12 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
import parsl
|
4
|
-
import pytest
|
5
3
|
import random
|
6
|
-
|
7
4
|
from concurrent.futures import Future
|
8
5
|
|
6
|
+
import pytest
|
7
|
+
|
8
|
+
import parsl
|
9
|
+
|
9
10
|
|
10
11
|
@parsl.python_app
|
11
12
|
def this_app(inputs=()):
|
@@ -16,6 +17,7 @@ def this_app(inputs=()):
|
|
16
17
|
def test_future_representation(tmpd_cwd):
|
17
18
|
import sqlalchemy
|
18
19
|
from sqlalchemy import text
|
20
|
+
|
19
21
|
from parsl.tests.configs.htex_local_alternate import fresh_config
|
20
22
|
|
21
23
|
monitoring_db = str(tmpd_cwd / "monitoring.db")
|
@@ -52,7 +54,6 @@ def test_future_representation(tmpd_cwd):
|
|
52
54
|
# seconds, with the assumption "data will arrive in the DB within
|
53
55
|
# 30 seconds, but probably much sooner".
|
54
56
|
parsl.dfk().cleanup()
|
55
|
-
parsl.clear()
|
56
57
|
|
57
58
|
engine = sqlalchemy.create_engine(monitoring_url)
|
58
59
|
with engine.begin() as connection:
|
@@ -1,9 +1,11 @@
|
|
1
1
|
|
2
2
|
import logging
|
3
3
|
import os
|
4
|
-
|
4
|
+
|
5
5
|
import pytest
|
6
6
|
|
7
|
+
import parsl
|
8
|
+
|
7
9
|
logger = logging.getLogger(__name__)
|
8
10
|
|
9
11
|
|
@@ -16,6 +18,7 @@ def this_app(x):
|
|
16
18
|
def test_hashsum():
|
17
19
|
import sqlalchemy
|
18
20
|
from sqlalchemy import text
|
21
|
+
|
19
22
|
from parsl.tests.configs.htex_local_alternate import fresh_config
|
20
23
|
|
21
24
|
if os.path.exists("runinfo/monitoring.db"):
|
@@ -47,7 +50,6 @@ def test_hashsum():
|
|
47
50
|
|
48
51
|
logger.info("cleaning up parsl")
|
49
52
|
parsl.dfk().cleanup()
|
50
|
-
parsl.clear()
|
51
53
|
|
52
54
|
# at this point, we should find one row in the monitoring database.
|
53
55
|
|
@@ -0,0 +1,134 @@
|
|
1
|
+
"""Tests monitoring records app name under various decoration patterns.
|
2
|
+
"""
|
3
|
+
|
4
|
+
import logging
|
5
|
+
import os
|
6
|
+
import re
|
7
|
+
import time
|
8
|
+
from typing import Union
|
9
|
+
|
10
|
+
import pytest
|
11
|
+
|
12
|
+
import parsl
|
13
|
+
from parsl.config import Config
|
14
|
+
from parsl.data_provider.data_manager import default_staging
|
15
|
+
from parsl.data_provider.files import File
|
16
|
+
from parsl.data_provider.staging import Staging
|
17
|
+
from parsl.executors import HighThroughputExecutor
|
18
|
+
from parsl.monitoring import MonitoringHub
|
19
|
+
from parsl.providers import LocalProvider
|
20
|
+
|
21
|
+
|
22
|
+
def fresh_config(run_dir):
|
23
|
+
return Config(
|
24
|
+
run_dir=str(run_dir),
|
25
|
+
executors=[
|
26
|
+
HighThroughputExecutor(
|
27
|
+
address="127.0.0.1",
|
28
|
+
label="htex_Local",
|
29
|
+
provider=LocalProvider(
|
30
|
+
init_blocks=1,
|
31
|
+
min_blocks=1,
|
32
|
+
max_blocks=1,
|
33
|
+
)
|
34
|
+
)
|
35
|
+
],
|
36
|
+
strategy='simple',
|
37
|
+
strategy_period=0.1,
|
38
|
+
monitoring=MonitoringHub(
|
39
|
+
hub_address="localhost",
|
40
|
+
)
|
41
|
+
)
|
42
|
+
|
43
|
+
|
44
|
+
@parsl.python_app
|
45
|
+
def stdapp(stdout=None, stderr=None):
|
46
|
+
pass
|
47
|
+
|
48
|
+
|
49
|
+
class ArbitraryPathLike(os.PathLike):
|
50
|
+
def __init__(self, path: Union[str, bytes]) -> None:
|
51
|
+
self.path = path
|
52
|
+
|
53
|
+
def __fspath__(self) -> Union[str, bytes]:
|
54
|
+
return self.path
|
55
|
+
|
56
|
+
|
57
|
+
class ArbitraryStaging(Staging):
|
58
|
+
"""This staging provider will not actually do any staging, but will
|
59
|
+
accept arbitrary: scheme URLs. That's enough for this monitoring test
|
60
|
+
which doesn't need any actual stage out action to happen.
|
61
|
+
"""
|
62
|
+
def can_stage_out(self, file):
|
63
|
+
return file.scheme == "arbitrary"
|
64
|
+
|
65
|
+
|
66
|
+
@pytest.mark.local
|
67
|
+
@pytest.mark.parametrize('stdx,expected_stdx',
|
68
|
+
[('hello.txt', 'hello.txt'),
|
69
|
+
(None, ''),
|
70
|
+
(('tuple.txt', 'w'), 'tuple.txt'),
|
71
|
+
(ArbitraryPathLike('pl.txt'), 'pl.txt'),
|
72
|
+
(ArbitraryPathLike(b'pl2.txt'), 'pl2.txt'),
|
73
|
+
((ArbitraryPathLike('pl3.txt'), 'w'), 'pl3.txt'),
|
74
|
+
((ArbitraryPathLike(b'pl4.txt'), 'w'), 'pl4.txt'),
|
75
|
+
(parsl.AUTO_LOGNAME,
|
76
|
+
lambda p:
|
77
|
+
isinstance(p, str) and
|
78
|
+
os.path.isabs(p) and
|
79
|
+
re.match("^.*/task_0000_stdapp\\.std...$", p)),
|
80
|
+
(File("arbitrary:abc123"), "arbitrary:abc123"),
|
81
|
+
(File("file:///tmp/pl5"), "file:///tmp/pl5"),
|
82
|
+
])
|
83
|
+
@pytest.mark.parametrize('stream', ['stdout', 'stderr'])
|
84
|
+
def test_stdstream_to_monitoring(stdx, expected_stdx, stream, tmpd_cwd, caplog):
|
85
|
+
"""This tests that various forms of stdout/err specification are
|
86
|
+
represented in monitoring correctly. The stderr and stdout codepaths
|
87
|
+
are generally duplicated, rather than factorised, and so this test
|
88
|
+
runs the same tests on both stdout and stderr.
|
89
|
+
"""
|
90
|
+
|
91
|
+
# this is imported here rather than at module level because
|
92
|
+
# it isn't available in a plain parsl install, so this module
|
93
|
+
# would otherwise fail to import and break even a basic test
|
94
|
+
# run.
|
95
|
+
import sqlalchemy
|
96
|
+
|
97
|
+
c = fresh_config(tmpd_cwd)
|
98
|
+
c.monitoring.logging_endpoint = f"sqlite:///{tmpd_cwd}/monitoring.db"
|
99
|
+
c.executors[0].storage_access = default_staging + [ArbitraryStaging()]
|
100
|
+
|
101
|
+
with parsl.load(c):
|
102
|
+
kwargs = {stream: stdx}
|
103
|
+
stdapp(**kwargs).result()
|
104
|
+
|
105
|
+
engine = sqlalchemy.create_engine(c.monitoring.logging_endpoint)
|
106
|
+
with engine.begin() as connection:
|
107
|
+
|
108
|
+
def count_rows(table: str):
|
109
|
+
result = connection.execute(sqlalchemy.text(f"SELECT COUNT(*) FROM {table}"))
|
110
|
+
(c, ) = result.first()
|
111
|
+
return c
|
112
|
+
|
113
|
+
# one workflow...
|
114
|
+
assert count_rows("workflow") == 1
|
115
|
+
|
116
|
+
# ... with one task ...
|
117
|
+
assert count_rows("task") == 1
|
118
|
+
|
119
|
+
# ... that was tried once ...
|
120
|
+
assert count_rows("try") == 1
|
121
|
+
|
122
|
+
# ... and has the expected name.
|
123
|
+
result = connection.execute(sqlalchemy.text(f"SELECT task_{stream} FROM task"))
|
124
|
+
(c, ) = result.first()
|
125
|
+
|
126
|
+
if isinstance(expected_stdx, str):
|
127
|
+
assert c == expected_stdx
|
128
|
+
elif callable(expected_stdx):
|
129
|
+
assert expected_stdx(c)
|
130
|
+
else:
|
131
|
+
raise RuntimeError("Bad expected_stdx value")
|
132
|
+
|
133
|
+
for record in caplog.records:
|
134
|
+
assert record.levelno < logging.ERROR
|
@@ -1,41 +1,48 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
3
|
from parsl import Config
|
4
|
-
from parsl.executors import
|
5
|
-
from parsl.launchers import
|
4
|
+
from parsl.executors import MPIExecutor
|
5
|
+
from parsl.launchers import AprunLauncher, SimpleLauncher, SrunLauncher
|
6
6
|
from parsl.providers import SlurmProvider
|
7
7
|
|
8
8
|
|
9
9
|
@pytest.mark.local
|
10
|
-
def
|
11
|
-
"""
|
10
|
+
def test_bad_launcher():
|
11
|
+
"""TypeError if a launcher other than SimpleLauncher is supplied"""
|
12
12
|
|
13
|
-
for launcher in [SrunLauncher(),
|
14
|
-
with pytest.raises(
|
13
|
+
for launcher in [SrunLauncher(), AprunLauncher()]:
|
14
|
+
with pytest.raises(TypeError):
|
15
15
|
Config(executors=[
|
16
|
-
|
17
|
-
enable_mpi_mode=True,
|
16
|
+
MPIExecutor(
|
18
17
|
provider=SlurmProvider(launcher=launcher),
|
19
18
|
)
|
20
19
|
])
|
21
20
|
|
22
21
|
|
23
22
|
@pytest.mark.local
|
24
|
-
def
|
25
|
-
"""
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
23
|
+
def test_bad_mpi_launcher():
|
24
|
+
"""ValueError if an unsupported mpi_launcher is specified"""
|
25
|
+
|
26
|
+
with pytest.raises(ValueError):
|
27
|
+
Config(executors=[
|
28
|
+
MPIExecutor(
|
29
|
+
mpi_launcher="bad_launcher",
|
30
|
+
provider=SlurmProvider(launcher=SimpleLauncher()),
|
31
|
+
)
|
32
|
+
])
|
33
|
+
|
34
|
+
|
35
|
+
@pytest.mark.local
|
36
|
+
@pytest.mark.parametrize(
|
37
|
+
"mpi_launcher",
|
38
|
+
["srun", "aprun", "mpiexec"]
|
39
|
+
)
|
40
|
+
def test_correct_launcher_with_mpi_mode(mpi_launcher: str):
|
41
|
+
"""Confirm that SimpleLauncher works with mpi_mode"""
|
42
|
+
|
43
|
+
executor = MPIExecutor(
|
44
|
+
mpi_launcher=mpi_launcher,
|
45
|
+
provider=SlurmProvider(launcher=SimpleLauncher()),
|
46
|
+
)
|
47
|
+
|
48
|
+
assert isinstance(executor.provider.launcher, SimpleLauncher)
|
@@ -1,34 +1,43 @@
|
|
1
1
|
import logging
|
2
|
+
import os
|
2
3
|
import random
|
3
4
|
from typing import Dict
|
5
|
+
|
4
6
|
import pytest
|
5
|
-
import parsl
|
6
|
-
from parsl import python_app, bash_app
|
7
|
-
from parsl.tests.configs.htex_local import fresh_config
|
8
7
|
|
9
|
-
import
|
8
|
+
import parsl
|
9
|
+
from parsl import Config, bash_app, python_app
|
10
|
+
from parsl.executors import MPIExecutor
|
11
|
+
from parsl.executors.errors import InvalidResourceSpecification
|
12
|
+
from parsl.launchers import SimpleLauncher
|
13
|
+
from parsl.providers import LocalProvider
|
10
14
|
|
11
15
|
EXECUTOR_LABEL = "MPI_TEST"
|
12
16
|
|
13
17
|
|
14
18
|
def local_setup():
|
15
|
-
config = fresh_config()
|
16
|
-
config.executors[0].label = EXECUTOR_LABEL
|
17
|
-
config.executors[0].max_workers_per_node = 2
|
18
|
-
config.executors[0].enable_mpi_mode = True
|
19
|
-
config.executors[0].mpi_launcher = "mpiexec"
|
20
19
|
|
21
20
|
cwd = os.path.abspath(os.path.dirname(__file__))
|
22
21
|
pbs_nodefile = os.path.join(cwd, "mocks", "pbs_nodefile")
|
23
22
|
|
24
|
-
config
|
23
|
+
config = Config(
|
24
|
+
executors=[
|
25
|
+
MPIExecutor(
|
26
|
+
label=EXECUTOR_LABEL,
|
27
|
+
max_workers_per_block=2,
|
28
|
+
mpi_launcher="mpiexec",
|
29
|
+
provider=LocalProvider(
|
30
|
+
worker_init=f"export PBS_NODEFILE={pbs_nodefile}",
|
31
|
+
launcher=SimpleLauncher()
|
32
|
+
)
|
33
|
+
)
|
34
|
+
])
|
25
35
|
|
26
36
|
parsl.load(config)
|
27
37
|
|
28
38
|
|
29
39
|
def local_teardown():
|
30
40
|
parsl.dfk().cleanup()
|
31
|
-
parsl.clear()
|
32
41
|
|
33
42
|
|
34
43
|
@python_app
|
@@ -169,3 +178,11 @@ def test_simulated_load(rounds: int = 100):
|
|
169
178
|
total_ranks, nodes = future.result(timeout=10)
|
170
179
|
assert len(nodes) == futures[future]["num_nodes"]
|
171
180
|
assert total_ranks == futures[future]["num_nodes"] * futures[future]["ranks_per_node"]
|
181
|
+
|
182
|
+
|
183
|
+
@pytest.mark.local
|
184
|
+
def test_missing_resource_spec():
|
185
|
+
|
186
|
+
with pytest.raises(InvalidResourceSpecification):
|
187
|
+
future = mock_app(sleep_dur=0.4)
|
188
|
+
future.result(timeout=10)
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import logging
|
2
|
+
|
2
3
|
import pytest
|
3
4
|
|
4
|
-
from parsl.executors.high_throughput.mpi_resource_management import Scheduler
|
5
5
|
from parsl.executors.high_throughput.mpi_prefix_composer import (
|
6
|
-
|
6
|
+
compose_all,
|
7
7
|
compose_aprun_launch_cmd,
|
8
8
|
compose_mpiexec_launch_cmd,
|
9
|
-
|
9
|
+
compose_srun_launch_cmd,
|
10
10
|
)
|
11
|
-
|
11
|
+
from parsl.executors.high_throughput.mpi_resource_management import Scheduler
|
12
12
|
|
13
13
|
resource_spec = {"num_nodes": 2,
|
14
14
|
"num_ranks": 8,
|