fractal-server 2.14.0a21__tar.gz → 2.14.0a23__tar.gz
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.
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/PKG-INFO +1 -1
- fractal_server-2.14.0a23/fractal_server/__init__.py +1 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/history.py +14 -2
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/compress_folder.py +58 -30
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +46 -16
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +48 -16
- fractal_server-2.14.0a23/fractal_server/app/runner/executors/slurm_ssh/runner.py +208 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_sudo/runner.py +29 -9
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/extract_archive.py +1 -3
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/task_files.py +18 -6
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/ssh/_fabric.py +4 -2
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/pyproject.toml +2 -2
- fractal_server-2.14.0a21/fractal_server/__init__.py +0 -1
- fractal_server-2.14.0a21/fractal_server/app/runner/executors/slurm_ssh/runner.py +0 -172
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/LICENSE +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/README.md +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/__main__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/alembic.ini +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/db/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/history/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/linkusergroup.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/linkuserproject.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/security.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/user_settings.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/accounting.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/dataset.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/history.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/job.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/project.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/task.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/task_group.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/workflow.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/models/v2/workflowtask.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/accounting.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/impersonate.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/job.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/project.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/task.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/_aux_functions_history.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/dataset.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/images.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/job.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/project.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/status_legacy.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/submit.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/task.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/task_group.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/verify_image_types.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/workflow.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/current_user.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/group.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/login.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/oauth.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/register.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/router.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/auth/users.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/aux/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/aux/_job.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/aux/_runner.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/pagination.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/components.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/exceptions.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/base_runner.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/local/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/local/get_local_config.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/local/runner.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/_batching.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/_job_states.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/_slurm_config.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/remote.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_common/utils_executors.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_ssh/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/filenames.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/run_subprocess.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/shutdown.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/_local.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/_slurm_ssh.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/_slurm_sudo.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/db_tools.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/runner.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/runner_functions.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/submit_workflow.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/v2/task_interface.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/versions.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/_filter_validators.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/_validators.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/user.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/user_group.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/user_settings.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/accounting.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/dataset.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/dumps.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/history.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/job.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/manifest.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/project.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/status_legacy.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/task.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/task_collection.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/task_group.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/workflow.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/security/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/security/signup_email.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/user_settings.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/config.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/data_migrations/README.md +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/data_migrations/tools.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/gunicorn_fractal.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/images/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/images/models.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/images/tools.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/logger.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/main.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/env.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/naming_convention.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/py.typed +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/ssh/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/string_tools.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/syringe.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/utils.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/local/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/local/_utils.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/local/collect.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/local/deactivate.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/local/reactivate.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/ssh/collect.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/utils_background.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/utils_database.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/utils_package_names.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/tasks/v2/utils_templates.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/urls.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/utils.py +0 -0
- {fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/zip_tools.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__VERSION__ = "2.14.0a23"
|
{fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/routes/api/v2/history.py
RENAMED
@@ -1,3 +1,4 @@
|
|
1
|
+
from copy import deepcopy
|
1
2
|
from typing import Any
|
2
3
|
from typing import Optional
|
3
4
|
|
@@ -92,7 +93,7 @@ async def get_workflow_tasks_statuses(
|
|
92
93
|
db=db,
|
93
94
|
)
|
94
95
|
|
95
|
-
response = {}
|
96
|
+
response: dict[int, dict[str, int | str] | None] = {}
|
96
97
|
for wftask in workflow.task_list:
|
97
98
|
res = await db.execute(
|
98
99
|
select(HistoryRun)
|
@@ -130,7 +131,18 @@ async def get_workflow_tasks_statuses(
|
|
130
131
|
f"num_{target_status.value}_images"
|
131
132
|
] = num_images
|
132
133
|
|
133
|
-
|
134
|
+
new_response = deepcopy(response)
|
135
|
+
for key, value in response.items():
|
136
|
+
if value is not None:
|
137
|
+
num_total_images = sum(
|
138
|
+
value[f"num_{target_status.value}_images"]
|
139
|
+
for target_status in HistoryUnitStatus
|
140
|
+
)
|
141
|
+
if num_total_images > value["num_available_images"]:
|
142
|
+
value["num_available_images"] = None
|
143
|
+
new_response[key] = value
|
144
|
+
|
145
|
+
return JSONResponse(content=new_response, status_code=200)
|
134
146
|
|
135
147
|
|
136
148
|
@router.get("/project/{project_id}/status/run/")
|
{fractal_server-2.14.0a21 → fractal_server-2.14.0a23}/fractal_server/app/runner/compress_folder.py
RENAMED
@@ -11,8 +11,8 @@ built-in `tarfile` library has to do with performance issues we observed
|
|
11
11
|
when handling files which were just created within a SLURM job, and in the
|
12
12
|
context of a CephFS filesystem.
|
13
13
|
"""
|
14
|
-
import shutil
|
15
14
|
import sys
|
15
|
+
import time
|
16
16
|
from pathlib import Path
|
17
17
|
|
18
18
|
from fractal_server.app.runner.run_subprocess import run_subprocess
|
@@ -20,48 +20,66 @@ from fractal_server.logger import get_logger
|
|
20
20
|
from fractal_server.logger import set_logger
|
21
21
|
|
22
22
|
|
23
|
-
def
|
23
|
+
def _copy_subfolder(src: Path, dest: Path, logger_name: str):
|
24
|
+
t_start = time.perf_counter()
|
24
25
|
cmd_cp = f"cp -r {src.as_posix()} {dest.as_posix()}"
|
25
26
|
logger = get_logger(logger_name=logger_name)
|
26
27
|
logger.debug(f"{cmd_cp=}")
|
27
28
|
res = run_subprocess(cmd=cmd_cp, logger_name=logger_name)
|
29
|
+
elapsed = time.perf_counter() - t_start
|
30
|
+
logger.debug(f"[_copy_subfolder] END {elapsed=} s ({dest.as_posix()})")
|
28
31
|
return res
|
29
32
|
|
30
33
|
|
31
|
-
def
|
32
|
-
tarfile_path:
|
34
|
+
def _create_tar_archive(
|
35
|
+
tarfile_path: str,
|
33
36
|
subfolder_path_tmp_copy: Path,
|
34
37
|
logger_name: str,
|
35
|
-
|
38
|
+
filelist_path: str | None,
|
36
39
|
):
|
37
40
|
logger = get_logger(logger_name)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
logger.debug(f"[_create_tar_archive] START ({tarfile_path})")
|
42
|
+
t_start = time.perf_counter()
|
43
|
+
|
44
|
+
if filelist_path is None:
|
45
|
+
cmd_tar = (
|
46
|
+
f"tar -c -z -f {tarfile_path} "
|
47
|
+
f"--directory={subfolder_path_tmp_copy.as_posix()} "
|
48
|
+
"."
|
49
|
+
)
|
41
50
|
else:
|
42
|
-
|
51
|
+
cmd_tar = (
|
52
|
+
f"tar -c -z -f {tarfile_path} "
|
53
|
+
f"--directory={subfolder_path_tmp_copy.as_posix()} "
|
54
|
+
f"--files-from={filelist_path} --ignore-failed-read"
|
55
|
+
)
|
56
|
+
|
57
|
+
logger.critical(f"cmd tar:\n{cmd_tar}")
|
43
58
|
|
44
|
-
cmd_tar = (
|
45
|
-
f"tar czf {tarfile_path} "
|
46
|
-
f"{exclude_options} "
|
47
|
-
f"--directory={subfolder_path_tmp_copy.as_posix()} "
|
48
|
-
"."
|
49
|
-
)
|
50
|
-
logger.debug(f"cmd tar:\n{cmd_tar}")
|
51
59
|
run_subprocess(cmd=cmd_tar, logger_name=logger_name, allow_char="*")
|
60
|
+
elapsed = time.perf_counter() - t_start
|
61
|
+
logger.debug(f"[_create_tar_archive] END {elapsed=} s ({tarfile_path})")
|
52
62
|
|
53
63
|
|
54
|
-
def
|
64
|
+
def _remove_temp_subfolder(subfolder_path_tmp_copy: Path, logger_name: str):
|
55
65
|
logger = get_logger(logger_name)
|
66
|
+
t_start = time.perf_counter()
|
56
67
|
try:
|
57
|
-
|
58
|
-
|
68
|
+
cmd_rm = f"rm -rf {subfolder_path_tmp_copy}"
|
69
|
+
logger.debug(f"cmd rm:\n{cmd_rm}")
|
70
|
+
run_subprocess(cmd=cmd_rm, logger_name=logger_name, allow_char="*")
|
59
71
|
except Exception as e:
|
60
|
-
logger.debug(f"ERROR during
|
72
|
+
logger.debug(f"ERROR during {cmd_rm}: {e}")
|
73
|
+
elapsed = time.perf_counter() - t_start
|
74
|
+
logger.debug(
|
75
|
+
f"[_remove_temp_subfolder] END {elapsed=} s "
|
76
|
+
f"({subfolder_path_tmp_copy=})"
|
77
|
+
)
|
61
78
|
|
62
79
|
|
63
80
|
def compress_folder(
|
64
|
-
subfolder_path: Path,
|
81
|
+
subfolder_path: Path,
|
82
|
+
filelist_path: str | None,
|
65
83
|
) -> str:
|
66
84
|
"""
|
67
85
|
Compress e.g. `/path/archive` into `/path/archive.tar.gz`
|
@@ -91,14 +109,16 @@ def compress_folder(
|
|
91
109
|
subfolder_path.parent / f"{subfolder_path.name}_copy"
|
92
110
|
)
|
93
111
|
try:
|
94
|
-
|
95
|
-
subfolder_path,
|
112
|
+
_copy_subfolder(
|
113
|
+
subfolder_path,
|
114
|
+
subfolder_path_tmp_copy,
|
115
|
+
logger_name=logger_name,
|
96
116
|
)
|
97
|
-
|
117
|
+
_create_tar_archive(
|
98
118
|
tarfile_path,
|
99
119
|
subfolder_path_tmp_copy,
|
100
120
|
logger_name=logger_name,
|
101
|
-
|
121
|
+
filelist_path=filelist_path,
|
102
122
|
)
|
103
123
|
return tarfile_path
|
104
124
|
|
@@ -107,7 +127,9 @@ def compress_folder(
|
|
107
127
|
sys.exit(1)
|
108
128
|
|
109
129
|
finally:
|
110
|
-
|
130
|
+
_remove_temp_subfolder(
|
131
|
+
subfolder_path_tmp_copy, logger_name=logger_name
|
132
|
+
)
|
111
133
|
|
112
134
|
|
113
135
|
def main(sys_argv: list[str]):
|
@@ -115,15 +137,21 @@ def main(sys_argv: list[str]):
|
|
115
137
|
help_msg = (
|
116
138
|
"Expected use:\n"
|
117
139
|
"python -m fractal_server.app.runner.compress_folder "
|
118
|
-
"path/to/folder [--
|
140
|
+
"path/to/folder [--filelist /path/to/filelist]\n"
|
119
141
|
)
|
120
142
|
num_args = len(sys_argv[1:])
|
121
143
|
if num_args == 0:
|
122
144
|
sys.exit(f"Invalid argument.\n{help_msg}\nProvided: {sys_argv[1:]=}")
|
123
145
|
elif num_args == 1:
|
124
|
-
compress_folder(
|
125
|
-
|
126
|
-
|
146
|
+
compress_folder(
|
147
|
+
subfolder_path=Path(sys_argv[1]),
|
148
|
+
filelist_path=None,
|
149
|
+
)
|
150
|
+
elif num_args == 3 and sys_argv[2] == "--filelist":
|
151
|
+
compress_folder(
|
152
|
+
subfolder_path=Path(sys_argv[1]),
|
153
|
+
filelist_path=sys_argv[3],
|
154
|
+
)
|
127
155
|
else:
|
128
156
|
sys.exit(f"Invalid argument.\n{help_msg}\nProvided: {sys_argv[1:]=}")
|
129
157
|
|
@@ -60,6 +60,7 @@ class BaseSlurmRunner(BaseRunner):
|
|
60
60
|
root_dir_local: Path,
|
61
61
|
root_dir_remote: Path,
|
62
62
|
slurm_runner_type: Literal["ssh", "sudo"],
|
63
|
+
python_worker_interpreter: str,
|
63
64
|
common_script_lines: Optional[list[str]] = None,
|
64
65
|
user_cache_dir: Optional[str] = None,
|
65
66
|
poll_interval: Optional[int] = None,
|
@@ -70,6 +71,7 @@ class BaseSlurmRunner(BaseRunner):
|
|
70
71
|
self.common_script_lines = common_script_lines or []
|
71
72
|
self._check_slurm_account()
|
72
73
|
self.user_cache_dir = user_cache_dir
|
74
|
+
self.python_worker_interpreter = python_worker_interpreter
|
73
75
|
|
74
76
|
settings = Inject(get_settings)
|
75
77
|
|
@@ -327,9 +329,9 @@ class BaseSlurmRunner(BaseRunner):
|
|
327
329
|
)
|
328
330
|
logger.info("[_submit_single_sbatch] END")
|
329
331
|
|
330
|
-
def
|
332
|
+
def _fetch_artifacts(
|
331
333
|
self,
|
332
|
-
|
334
|
+
finished_slurm_jobs: list[SlurmJob],
|
333
335
|
) -> None:
|
334
336
|
raise NotImplementedError("Implement in child class.")
|
335
337
|
|
@@ -530,14 +532,14 @@ class BaseSlurmRunner(BaseRunner):
|
|
530
532
|
# Look for finished jobs
|
531
533
|
finished_job_ids = self._get_finished_jobs(job_ids=self.job_ids)
|
532
534
|
logger.debug(f"[submit] {finished_job_ids=}")
|
533
|
-
|
535
|
+
finished_jobs = [
|
536
|
+
self.jobs[_slurm_job_id] for _slurm_job_id in finished_job_ids
|
537
|
+
]
|
538
|
+
self._fetch_artifacts(finished_jobs)
|
534
539
|
with next(get_sync_db()) as db:
|
535
540
|
for slurm_job_id in finished_job_ids:
|
536
541
|
logger.debug(f"[submit] Now process {slurm_job_id=}")
|
537
542
|
slurm_job = self.jobs.pop(slurm_job_id)
|
538
|
-
self._copy_files_from_remote_to_local(
|
539
|
-
slurm_job
|
540
|
-
) # FIXME: add prefix # noqa
|
541
543
|
was_job_scancelled = slurm_job_id in scancelled_job_ids
|
542
544
|
result, exception = self._postprocess_single_task(
|
543
545
|
task=slurm_job.tasks[0],
|
@@ -653,7 +655,9 @@ class BaseSlurmRunner(BaseRunner):
|
|
653
655
|
if len(args_batches) != math.ceil(tot_tasks / tasks_per_job):
|
654
656
|
raise RuntimeError("Something wrong here while batching tasks")
|
655
657
|
|
656
|
-
|
658
|
+
# Part 1/3: Iterate over chunks, prepare SlurmJob objects
|
659
|
+
logger.info("[multisubmit] Prepare `SlurmJob`s.")
|
660
|
+
jobs_to_submit = []
|
657
661
|
for ind_batch, chunk in enumerate(args_batches):
|
658
662
|
prefix = f"{MULTISUBMIT_PREFIX}-{ind_batch:06d}"
|
659
663
|
tasks = []
|
@@ -673,17 +677,26 @@ class BaseSlurmRunner(BaseRunner):
|
|
673
677
|
),
|
674
678
|
)
|
675
679
|
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
680
|
+
jobs_to_submit.append(
|
681
|
+
SlurmJob(
|
682
|
+
prefix=prefix,
|
683
|
+
workdir_local=workdir_local,
|
684
|
+
workdir_remote=workdir_remote,
|
685
|
+
tasks=tasks,
|
686
|
+
)
|
681
687
|
)
|
688
|
+
|
689
|
+
# FIXME: split parts 2 and 3
|
690
|
+
# Part 2/3. Transfer all relevant input files (for SSH)
|
691
|
+
# Part 3/3. Run all `sbatch`es and update `self.jobs`
|
692
|
+
logger.info("[multisubmit] Transfer files and submit jobs.")
|
693
|
+
for slurm_job in jobs_to_submit:
|
682
694
|
self._submit_single_sbatch(
|
683
695
|
func,
|
684
696
|
slurm_job=slurm_job,
|
685
697
|
slurm_config=config,
|
686
698
|
)
|
699
|
+
|
687
700
|
if task_type == "parallel":
|
688
701
|
# FIXME: replace loop with a `bulk_update_history_unit` function
|
689
702
|
for ind, task_files in enumerate(list_task_files):
|
@@ -711,20 +724,21 @@ class BaseSlurmRunner(BaseRunner):
|
|
711
724
|
|
712
725
|
# Retrieval phase
|
713
726
|
logger.info("[multisubmit] START retrieval phase")
|
727
|
+
scancelled_job_ids = []
|
714
728
|
while len(self.jobs) > 0:
|
715
729
|
|
716
730
|
# Look for finished jobs
|
717
731
|
finished_job_ids = self._get_finished_jobs(job_ids=self.job_ids)
|
718
732
|
logger.debug(f"[multisubmit] {finished_job_ids=}")
|
733
|
+
finished_jobs = [
|
734
|
+
self.jobs[_slurm_job_id] for _slurm_job_id in finished_job_ids
|
735
|
+
]
|
736
|
+
self._fetch_artifacts(finished_jobs)
|
719
737
|
|
720
|
-
scancelled_job_ids = []
|
721
738
|
with next(get_sync_db()) as db:
|
722
739
|
for slurm_job_id in finished_job_ids:
|
723
740
|
logger.info(f"[multisubmit] Now process {slurm_job_id=}")
|
724
741
|
slurm_job = self.jobs.pop(slurm_job_id)
|
725
|
-
self._copy_files_from_remote_to_local(
|
726
|
-
slurm_job
|
727
|
-
) # FIXME: add prefix # noqa
|
728
742
|
for task in slurm_job.tasks:
|
729
743
|
logger.info(f"[multisubmit] Now process {task.index=}")
|
730
744
|
was_job_scancelled = slurm_job_id in scancelled_job_ids
|
@@ -810,3 +824,19 @@ class BaseSlurmRunner(BaseRunner):
|
|
810
824
|
)
|
811
825
|
logger.info("[scancel_jobs] END")
|
812
826
|
return scancelled_job_ids
|
827
|
+
|
828
|
+
def validate_slurm_jobs_workdirs(
|
829
|
+
self,
|
830
|
+
slurm_jobs: list[SlurmJob],
|
831
|
+
) -> None:
|
832
|
+
"""
|
833
|
+
Check that a list of `SlurmJob`s have homogeneous working folders.
|
834
|
+
"""
|
835
|
+
# Extract `workdir_remote` and `workdir_local`
|
836
|
+
set_workdir_local = set(_job.workdir_local for _job in slurm_jobs)
|
837
|
+
set_workdir_remote = set(_job.workdir_remote for _job in slurm_jobs)
|
838
|
+
|
839
|
+
if len(set_workdir_local) > 1:
|
840
|
+
raise ValueError(f"Non-unique values in {set_workdir_local=}.")
|
841
|
+
if len(set_workdir_remote) > 1:
|
842
|
+
raise ValueError(f"Non-unique values in {set_workdir_remote=}.")
|
@@ -20,31 +20,47 @@ class SlurmTask(BaseModel):
|
|
20
20
|
index: int
|
21
21
|
|
22
22
|
@property
|
23
|
-
def
|
23
|
+
def input_pickle_file_local_path(self) -> Path:
|
24
24
|
return (
|
25
25
|
self.workdir_local / f"{self.prefix}-{self.component}-input.pickle"
|
26
|
-
)
|
26
|
+
)
|
27
27
|
|
28
28
|
@property
|
29
|
-
def
|
29
|
+
def input_pickle_file_remote_path(self) -> Path:
|
30
30
|
return (
|
31
31
|
self.workdir_remote
|
32
32
|
/ f"{self.prefix}-{self.component}-input.pickle"
|
33
|
-
)
|
33
|
+
)
|
34
34
|
|
35
35
|
@property
|
36
|
-
def
|
36
|
+
def output_pickle_file_local_path(self) -> Path:
|
37
37
|
return (
|
38
38
|
self.workdir_local
|
39
39
|
/ f"{self.prefix}-{self.component}-output.pickle"
|
40
|
-
)
|
40
|
+
)
|
41
41
|
|
42
42
|
@property
|
43
|
-
def
|
43
|
+
def output_pickle_file_remote_path(self) -> Path:
|
44
44
|
return (
|
45
45
|
self.workdir_remote
|
46
46
|
/ f"{self.prefix}-{self.component}-output.pickle"
|
47
|
-
)
|
47
|
+
)
|
48
|
+
|
49
|
+
@property
|
50
|
+
def input_pickle_file_local(self) -> str:
|
51
|
+
return self.input_pickle_file_local_path.as_posix()
|
52
|
+
|
53
|
+
@property
|
54
|
+
def input_pickle_file_remote(self) -> str:
|
55
|
+
return self.input_pickle_file_remote_path.as_posix()
|
56
|
+
|
57
|
+
@property
|
58
|
+
def output_pickle_file_local(self) -> str:
|
59
|
+
return self.output_pickle_file_local_path.as_posix()
|
60
|
+
|
61
|
+
@property
|
62
|
+
def output_pickle_file_remote(self) -> str:
|
63
|
+
return self.output_pickle_file_remote_path.as_posix()
|
48
64
|
|
49
65
|
|
50
66
|
class SlurmJob(BaseModel):
|
@@ -74,29 +90,45 @@ class SlurmJob(BaseModel):
|
|
74
90
|
return "%j"
|
75
91
|
|
76
92
|
@property
|
77
|
-
def
|
93
|
+
def slurm_stdout_remote_path(self) -> Path:
|
78
94
|
return (
|
79
95
|
self.workdir_remote
|
80
96
|
/ f"{self.prefix}-slurm-{self.slurm_job_id_placeholder}.out"
|
81
|
-
)
|
97
|
+
)
|
82
98
|
|
83
99
|
@property
|
84
|
-
def
|
100
|
+
def slurm_stdout_remote(self) -> str:
|
101
|
+
return self.slurm_stdout_remote_path.as_posix()
|
102
|
+
|
103
|
+
@property
|
104
|
+
def slurm_stderr_remote_path(self) -> Path:
|
85
105
|
return (
|
86
106
|
self.workdir_remote
|
87
107
|
/ f"{self.prefix}-slurm-{self.slurm_job_id_placeholder}.err"
|
88
|
-
)
|
108
|
+
)
|
89
109
|
|
90
110
|
@property
|
91
|
-
def
|
111
|
+
def slurm_stderr_remote(self) -> str:
|
112
|
+
return self.slurm_stderr_remote_path.as_posix()
|
113
|
+
|
114
|
+
@property
|
115
|
+
def slurm_stdout_local_path(self) -> str:
|
92
116
|
return (
|
93
117
|
self.workdir_local
|
94
118
|
/ f"{self.prefix}-slurm-{self.slurm_job_id_placeholder}.out"
|
95
|
-
)
|
119
|
+
)
|
96
120
|
|
97
121
|
@property
|
98
|
-
def
|
122
|
+
def slurm_stdout_local(self) -> str:
|
123
|
+
return self.slurm_stdout_local_path.as_posix()
|
124
|
+
|
125
|
+
@property
|
126
|
+
def slurm_stderr_local_path(self) -> Path:
|
99
127
|
return (
|
100
128
|
self.workdir_local
|
101
129
|
/ f"{self.prefix}-slurm-{self.slurm_job_id_placeholder}.err"
|
102
|
-
)
|
130
|
+
)
|
131
|
+
|
132
|
+
@property
|
133
|
+
def slurm_stderr_local(self) -> str:
|
134
|
+
return self.slurm_stderr_local_path.as_posix()
|
@@ -0,0 +1,208 @@
|
|
1
|
+
import time
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from ..slurm_common.base_slurm_runner import BaseSlurmRunner
|
6
|
+
from ..slurm_common.slurm_job_task_models import SlurmJob
|
7
|
+
from fractal_server.app.runner.compress_folder import compress_folder
|
8
|
+
from fractal_server.app.runner.extract_archive import extract_archive
|
9
|
+
from fractal_server.config import get_settings
|
10
|
+
from fractal_server.logger import set_logger
|
11
|
+
from fractal_server.ssh._fabric import FractalSSH
|
12
|
+
from fractal_server.syringe import Inject
|
13
|
+
|
14
|
+
|
15
|
+
logger = set_logger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
class SlurmSSHRunner(BaseSlurmRunner):
|
19
|
+
fractal_ssh: FractalSSH
|
20
|
+
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
*,
|
24
|
+
# Common
|
25
|
+
root_dir_local: Path,
|
26
|
+
root_dir_remote: Path,
|
27
|
+
common_script_lines: Optional[list[str]] = None,
|
28
|
+
user_cache_dir: Optional[str] = None,
|
29
|
+
poll_interval: Optional[int] = None,
|
30
|
+
# Specific
|
31
|
+
fractal_ssh: FractalSSH,
|
32
|
+
) -> None:
|
33
|
+
"""
|
34
|
+
Set parameters that are the same for different Fractal tasks and for
|
35
|
+
different SLURM jobs/tasks.
|
36
|
+
"""
|
37
|
+
self.fractal_ssh = fractal_ssh
|
38
|
+
logger.warning(self.fractal_ssh)
|
39
|
+
|
40
|
+
settings = Inject(get_settings)
|
41
|
+
|
42
|
+
super().__init__(
|
43
|
+
slurm_runner_type="ssh",
|
44
|
+
root_dir_local=root_dir_local,
|
45
|
+
root_dir_remote=root_dir_remote,
|
46
|
+
common_script_lines=common_script_lines,
|
47
|
+
user_cache_dir=user_cache_dir,
|
48
|
+
poll_interval=poll_interval,
|
49
|
+
python_worker_interpreter=settings.FRACTAL_SLURM_WORKER_PYTHON,
|
50
|
+
)
|
51
|
+
|
52
|
+
def _mkdir_local_folder(self, folder: str) -> None:
|
53
|
+
Path(folder).mkdir(parents=True)
|
54
|
+
|
55
|
+
def _mkdir_remote_folder(self, folder: str):
|
56
|
+
self.fractal_ssh.mkdir(
|
57
|
+
folder=folder,
|
58
|
+
parents=True,
|
59
|
+
)
|
60
|
+
|
61
|
+
def _fetch_artifacts(
|
62
|
+
self,
|
63
|
+
finished_slurm_jobs: list[SlurmJob],
|
64
|
+
) -> None:
|
65
|
+
"""
|
66
|
+
Fetch artifacts for a list of SLURM jobs.
|
67
|
+
"""
|
68
|
+
|
69
|
+
# Check length
|
70
|
+
if len(finished_slurm_jobs) == 0:
|
71
|
+
logger.debug(f"[_fetch_artifacts] EXIT ({finished_slurm_jobs=}).")
|
72
|
+
return None
|
73
|
+
|
74
|
+
t_0 = time.perf_counter()
|
75
|
+
logger.debug(
|
76
|
+
f"[_fetch_artifacts] START ({len(finished_slurm_jobs)=})."
|
77
|
+
)
|
78
|
+
|
79
|
+
# Extract `workdir_remote` and `workdir_local`
|
80
|
+
self.validate_slurm_jobs_workdirs(finished_slurm_jobs)
|
81
|
+
workdir_local = finished_slurm_jobs[0].workdir_local
|
82
|
+
workdir_remote = finished_slurm_jobs[0].workdir_remote
|
83
|
+
|
84
|
+
# Define local/remote tarfile paths
|
85
|
+
tarfile_path_local = (
|
86
|
+
workdir_local.parent / f"{workdir_local.name}.tar.gz"
|
87
|
+
).as_posix()
|
88
|
+
tarfile_path_remote = (
|
89
|
+
workdir_remote.parent / f"{workdir_remote.name}.tar.gz"
|
90
|
+
).as_posix()
|
91
|
+
|
92
|
+
# Create file list
|
93
|
+
# # FIXME can we make this more efficient with iterations?
|
94
|
+
filelist = []
|
95
|
+
for _slurm_job in finished_slurm_jobs:
|
96
|
+
_single_job_filelist = [
|
97
|
+
_slurm_job.slurm_stdout_remote_path.name,
|
98
|
+
_slurm_job.slurm_stderr_remote_path.name,
|
99
|
+
]
|
100
|
+
for task in _slurm_job.tasks:
|
101
|
+
_single_job_filelist.extend(
|
102
|
+
[
|
103
|
+
task.output_pickle_file_remote_path.name,
|
104
|
+
task.task_files.log_file_remote_path.name,
|
105
|
+
task.task_files.args_file_remote_path.name,
|
106
|
+
task.task_files.metadiff_file_remote_path.name,
|
107
|
+
]
|
108
|
+
)
|
109
|
+
filelist.extend(_single_job_filelist)
|
110
|
+
filelist_string = "\n".join(filelist)
|
111
|
+
elapsed = time.perf_counter() - t_0
|
112
|
+
logger.debug(
|
113
|
+
"[_fetch_artifacts] Created filelist "
|
114
|
+
f"({len(filelist)=}, from start: {elapsed:.3f} s)."
|
115
|
+
)
|
116
|
+
|
117
|
+
# Write filelist to file remotely
|
118
|
+
tmp_filelist_path = workdir_remote / f"filelist_{time.time()}.txt"
|
119
|
+
self.fractal_ssh.write_remote_file(
|
120
|
+
path=tmp_filelist_path.as_posix(),
|
121
|
+
content=f"{filelist_string}\n",
|
122
|
+
)
|
123
|
+
elapsed = time.perf_counter() - t_0
|
124
|
+
logger.debug(
|
125
|
+
f"[_fetch_artifacts] File list written to {tmp_filelist_path} "
|
126
|
+
f"(from start: {elapsed:.3f} s)."
|
127
|
+
)
|
128
|
+
|
129
|
+
# Create remote tarfile
|
130
|
+
t_0_tar = time.perf_counter()
|
131
|
+
tar_command = (
|
132
|
+
f"{self.python_worker_interpreter} "
|
133
|
+
"-m fractal_server.app.runner.compress_folder "
|
134
|
+
f"{workdir_remote.as_posix()} "
|
135
|
+
f"--filelist {tmp_filelist_path}"
|
136
|
+
)
|
137
|
+
self.fractal_ssh.run_command(cmd=tar_command)
|
138
|
+
t_1_tar = time.perf_counter()
|
139
|
+
logger.info(
|
140
|
+
f"Remote archive {tarfile_path_remote} created"
|
141
|
+
f" - elapsed: {t_1_tar - t_0_tar:.3f} s"
|
142
|
+
)
|
143
|
+
|
144
|
+
# Fetch tarfile
|
145
|
+
t_0_get = time.perf_counter()
|
146
|
+
self.fractal_ssh.fetch_file(
|
147
|
+
remote=tarfile_path_remote,
|
148
|
+
local=tarfile_path_local,
|
149
|
+
)
|
150
|
+
t_1_get = time.perf_counter()
|
151
|
+
logger.info(
|
152
|
+
f"Subfolder archive transferred back to {tarfile_path_local}"
|
153
|
+
f" - elapsed: {t_1_get - t_0_get:.3f} s"
|
154
|
+
)
|
155
|
+
|
156
|
+
# Extract tarfile locally
|
157
|
+
extract_archive(Path(tarfile_path_local))
|
158
|
+
|
159
|
+
# Remove local tarfile
|
160
|
+
Path(tarfile_path_local).unlink(missing_ok=True)
|
161
|
+
|
162
|
+
t_1 = time.perf_counter()
|
163
|
+
logger.info(f"[_get_subfolder_sftp] End - elapsed: {t_1 - t_0:.3f} s")
|
164
|
+
|
165
|
+
def _send_inputs(self, jobs: list[SlurmJob]) -> None:
|
166
|
+
"""
|
167
|
+
Transfer the jobs subfolder to the remote host.
|
168
|
+
"""
|
169
|
+
for job in jobs:
|
170
|
+
|
171
|
+
# Create local archive
|
172
|
+
tarfile_path_local = compress_folder(
|
173
|
+
job.workdir_local,
|
174
|
+
filelist_path=None,
|
175
|
+
)
|
176
|
+
tarfile_name = Path(tarfile_path_local).name
|
177
|
+
logger.info(f"Subfolder archive created at {tarfile_path_local}")
|
178
|
+
|
179
|
+
# Transfer archive
|
180
|
+
tarfile_path_remote = (
|
181
|
+
job.workdir_remote.parent / tarfile_name
|
182
|
+
).as_posix()
|
183
|
+
t_0_put = time.perf_counter()
|
184
|
+
self.fractal_ssh.send_file(
|
185
|
+
local=tarfile_path_local,
|
186
|
+
remote=tarfile_path_remote,
|
187
|
+
)
|
188
|
+
t_1_put = time.perf_counter()
|
189
|
+
logger.info(
|
190
|
+
f"Subfolder archive transferred to {tarfile_path_remote}"
|
191
|
+
f" - elapsed: {t_1_put - t_0_put:.3f} s"
|
192
|
+
)
|
193
|
+
|
194
|
+
# Remove local archive
|
195
|
+
Path(tarfile_path_local).unlink()
|
196
|
+
logger.debug(f"Local archive {tarfile_path_local} removed")
|
197
|
+
|
198
|
+
# Uncompress remote archive
|
199
|
+
tar_command = (
|
200
|
+
f"{self.python_worker_interpreter} -m "
|
201
|
+
"fractal_server.app.runner.extract_archive "
|
202
|
+
f"{tarfile_path_remote}"
|
203
|
+
)
|
204
|
+
self.fractal_ssh.run_command(cmd=tar_command)
|
205
|
+
|
206
|
+
def _run_remote_cmd(self, cmd: str) -> str:
|
207
|
+
stdout = self.fractal_ssh.run_command(cmd=cmd)
|
208
|
+
return stdout
|