From c67e8742c81e129b559220ea84c4b6eeed0919ae Mon Sep 17 00:00:00 2001 From: Benoit Seignovert Date: Mon, 26 Feb 2024 14:35:30 +0100 Subject: [PATCH] Add working directory field in options form --- render/__main__.py | 4 +++- src/glicid_spawner/form.py | 5 ++++- src/glicid_spawner/resources.py | 12 ++++++++++++ src/glicid_spawner/templates/spawner_form.jinja | 1 + src/glicid_spawner/templates/views/chdir.jinja | 13 +++++++++++++ src/glicid_spawner/templates/views/envs.jinja | 13 ++++++++----- tests/test_form.py | 17 +++++++++++++++++ tests/test_resources.py | 14 +++++++++++++- 8 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 src/glicid_spawner/templates/views/chdir.jinja diff --git a/render/__main__.py b/render/__main__.py index 63c11dd..6f84032 100644 --- a/render/__main__.py +++ b/render/__main__.py @@ -11,7 +11,7 @@ from traceback import format_exc from flask import Flask, render_template, request from glicid_spawner.form import TEMPLATES, options_from_form from glicid_spawner.micromamba import MicromambaEnv -from glicid_spawner.resources import CPU, MEMORY, gpu_max_duration +from glicid_spawner.resources import CPU, MEMORY, get_folders, gpu_max_duration from glicid_spawner.slurm import gres, sinfo_from_file from livereload import Server @@ -22,6 +22,7 @@ ENVS = [ MicromambaEnv('USER', 'bar', f'/{USERNAME}/envs/bar'), MicromambaEnv('GLOBAL', 'baz', '/global/envs/baz'), ] +FOLDERS = get_folders(USERNAME) # Dummy SLURM config DATA = Path(__file__).parent / '..' / 'tests' / 'data' @@ -38,6 +39,7 @@ GPU_MULTI_CLUSTER = gpu_max_duration(gres(SLURM_MULTI_CLUSTER)) # Format dummy options OPTIONS = { 'username': USERNAME, + 'folders': FOLDERS, 'envs': ENVS, 'cpus': CPU, 'mems': MEMORY, diff --git a/src/glicid_spawner/form.py b/src/glicid_spawner/form.py index aa4a87b..143e228 100644 --- a/src/glicid_spawner/form.py +++ b/src/glicid_spawner/form.py @@ -3,7 +3,7 @@ from jinja2 import Environment, PackageLoader, select_autoescape from .micromamba import get_envs -from .resources import CPU, GPU_DEFAULTS, MEMORY, gpu_max_duration +from .resources import CPU, GPU_DEFAULTS, MEMORY, get_folders, gpu_max_duration from .slurm import gres, sinfo TEMPLATES = Environment( @@ -21,6 +21,7 @@ def options_attrs(username: str) -> dict: return { 'username': username, + 'folders': get_folders(username), 'envs': get_envs(username), 'cpus': CPU, 'mems': MEMORY, @@ -38,6 +39,7 @@ def options_form(username: str) -> str: def options_from_form(formdata) -> dict: """Export options from default form.""" # Parse form data response + workdir = formdata['workdir'][0] env = formdata['python-env'][0] cpu = int(formdata['cpu'][0]) mem = int(formdata['mem'][0]) @@ -52,6 +54,7 @@ def options_from_form(formdata) -> dict: # Export options options = { + 'workdir': workdir, 'pyenv': env, 'nprocs': cpu, 'memory': f'{mem}GB', diff --git a/src/glicid_spawner/resources.py b/src/glicid_spawner/resources.py index b4cdc52..3927afd 100644 --- a/src/glicid_spawner/resources.py +++ b/src/glicid_spawner/resources.py @@ -37,3 +37,15 @@ def gpu_max_duration(gpus: list, unknown_default=1) -> dict: defaults = {gpu: duration for gpu, duration in GPU_DEFAULTS.items() if gpu in gpus} unknowns = {gpu: unknown_default for gpu in gpus if gpu not in GPU_DEFAULTS} return defaults | unknowns + + +def get_folders(username: str) -> list: + """List of folders accessible to the users as a working directory.""" + return [ + f'/home/{username}', + f'/scratch/nautilus/users/{username}', + f'/scratch/waves/users/{username}', + '/scratch/nautilus/projects', + '/scratch/waves/projects', + '/LAB-DATA/', + ] diff --git a/src/glicid_spawner/templates/spawner_form.jinja b/src/glicid_spawner/templates/spawner_form.jinja index d6263f2..366d080 100644 --- a/src/glicid_spawner/templates/spawner_form.jinja +++ b/src/glicid_spawner/templates/spawner_form.jinja @@ -5,6 +5,7 @@
{% include "views/username.jinja" %} + {% include "views/chdir.jinja" %} {% include "views/envs.jinja" %} {% include "views/resources.jinja" %} {% include "views/slurm.jinja" %} diff --git a/src/glicid_spawner/templates/views/chdir.jinja b/src/glicid_spawner/templates/views/chdir.jinja new file mode 100644 index 0000000..8d33074 --- /dev/null +++ b/src/glicid_spawner/templates/views/chdir.jinja @@ -0,0 +1,13 @@ +
+ +
+
+
+ +
+
+
diff --git a/src/glicid_spawner/templates/views/envs.jinja b/src/glicid_spawner/templates/views/envs.jinja index 954db74..831d8ea 100644 --- a/src/glicid_spawner/templates/views/envs.jinja +++ b/src/glicid_spawner/templates/views/envs.jinja @@ -1,10 +1,13 @@
- +
+
+ +
diff --git a/tests/test_form.py b/tests/test_form.py index 2a706a1..d04fafd 100644 --- a/tests/test_form.py +++ b/tests/test_form.py @@ -48,6 +48,15 @@ def test_options_attrs(mock_cluster): assert options['username'] == 'john-doe' + assert options['folders'] == [ + '/home/john-doe', + '/scratch/nautilus/users/john-doe', + '/scratch/waves/users/john-doe', + '/scratch/nautilus/projects', + '/scratch/waves/projects', + '/LAB-DATA/', + ] + assert [env.path for env in options['envs']] == [ '/john-doe/envs/foo', '/john-doe/envs/bar', @@ -243,6 +252,7 @@ def test_options_from_form(): """Test options from form parser.""" # No GPU formdata = { + 'workdir': ['/home/john-doe/'], 'python-env': ['/john-doe/envs/foo'], 'cpu': ['1'], 'mem': ['4'], @@ -250,6 +260,7 @@ def test_options_from_form(): } assert options_from_form(formdata) == { + 'workdir': '/home/john-doe/', 'pyenv': '/john-doe/envs/foo', 'nprocs': 1, 'memory': '4GB', @@ -258,6 +269,7 @@ def test_options_from_form(): # With GPU (in defaults list) formdata = { + 'workdir': ['/scratch/nautilus/users/john-doe/'], 'python-env': ['/john-doe/envs/bar'], 'cpu': ['2'], 'mem': ['16'], @@ -266,6 +278,7 @@ def test_options_from_form(): } assert options_from_form(formdata) == { + 'workdir': '/scratch/nautilus/users/john-doe/', 'pyenv': '/john-doe/envs/bar', 'nprocs': 2, 'memory': '16GB', @@ -276,6 +289,7 @@ def test_options_from_form(): # With unknown GPU (default 1h allocation) formdata = { + 'workdir': ['/scratch/waves/projects/'], 'python-env': ['/global/envs/baz'], 'cpu': ['8'], 'mem': ['16'], @@ -284,6 +298,7 @@ def test_options_from_form(): } assert options_from_form(formdata) == { + 'workdir': '/scratch/waves/projects/', 'pyenv': '/global/envs/baz', 'nprocs': 8, 'memory': '16GB', @@ -294,6 +309,7 @@ def test_options_from_form(): # Invalid CPU request (0h allocated) formdata = { + 'workdir': ['/LAD-DATA/'], 'python-env': ['/global/envs/qux'], 'cpu': ['128'], 'mem': ['4096'], @@ -302,6 +318,7 @@ def test_options_from_form(): } assert options_from_form(formdata) == { + 'workdir': '/LAD-DATA/', 'pyenv': '/global/envs/qux', 'nprocs': 128, 'memory': '4096GB', diff --git a/tests/test_resources.py b/tests/test_resources.py index 482ba1c..f910d2f 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -1,6 +1,6 @@ """Test resources module.""" -from glicid_spawner.resources import CPU, GPU_DEFAULTS, MEMORY, gpu_max_duration +from glicid_spawner.resources import CPU, GPU_DEFAULTS, MEMORY, get_folders, gpu_max_duration def test_default_resources(): @@ -22,3 +22,15 @@ def test_gpu_max_duration(): # Sorted by defaults order, then unknowns assert list(gpu) == ['None', 'A100', 'T4', 'K80'] assert list(gpu.values()) == [24, 1, 3, 3] + + +def test_resources_workdir(): + """Test resources working directories.""" + assert get_folders('john-doe') == [ + '/home/john-doe', + '/scratch/nautilus/users/john-doe', + '/scratch/waves/users/john-doe', + '/scratch/nautilus/projects', + '/scratch/waves/projects', + '/LAB-DATA/', + ]