Add working directory field in options form
This commit is contained in:
parent
0273f449b8
commit
c67e8742c8
8 changed files with 71 additions and 8 deletions
|
@ -11,7 +11,7 @@ from traceback import format_exc
|
||||||
from flask import Flask, render_template, request
|
from flask import Flask, render_template, request
|
||||||
from glicid_spawner.form import TEMPLATES, options_from_form
|
from glicid_spawner.form import TEMPLATES, options_from_form
|
||||||
from glicid_spawner.micromamba import MicromambaEnv
|
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 glicid_spawner.slurm import gres, sinfo_from_file
|
||||||
from livereload import Server
|
from livereload import Server
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ ENVS = [
|
||||||
MicromambaEnv('USER', 'bar', f'/{USERNAME}/envs/bar'),
|
MicromambaEnv('USER', 'bar', f'/{USERNAME}/envs/bar'),
|
||||||
MicromambaEnv('GLOBAL', 'baz', '/global/envs/baz'),
|
MicromambaEnv('GLOBAL', 'baz', '/global/envs/baz'),
|
||||||
]
|
]
|
||||||
|
FOLDERS = get_folders(USERNAME)
|
||||||
|
|
||||||
# Dummy SLURM config
|
# Dummy SLURM config
|
||||||
DATA = Path(__file__).parent / '..' / 'tests' / 'data'
|
DATA = Path(__file__).parent / '..' / 'tests' / 'data'
|
||||||
|
@ -38,6 +39,7 @@ GPU_MULTI_CLUSTER = gpu_max_duration(gres(SLURM_MULTI_CLUSTER))
|
||||||
# Format dummy options
|
# Format dummy options
|
||||||
OPTIONS = {
|
OPTIONS = {
|
||||||
'username': USERNAME,
|
'username': USERNAME,
|
||||||
|
'folders': FOLDERS,
|
||||||
'envs': ENVS,
|
'envs': ENVS,
|
||||||
'cpus': CPU,
|
'cpus': CPU,
|
||||||
'mems': MEMORY,
|
'mems': MEMORY,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from jinja2 import Environment, PackageLoader, select_autoescape
|
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||||
|
|
||||||
from .micromamba import get_envs
|
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
|
from .slurm import gres, sinfo
|
||||||
|
|
||||||
TEMPLATES = Environment(
|
TEMPLATES = Environment(
|
||||||
|
@ -21,6 +21,7 @@ def options_attrs(username: str) -> dict:
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'username': username,
|
'username': username,
|
||||||
|
'folders': get_folders(username),
|
||||||
'envs': get_envs(username),
|
'envs': get_envs(username),
|
||||||
'cpus': CPU,
|
'cpus': CPU,
|
||||||
'mems': MEMORY,
|
'mems': MEMORY,
|
||||||
|
@ -38,6 +39,7 @@ def options_form(username: str) -> str:
|
||||||
def options_from_form(formdata) -> dict:
|
def options_from_form(formdata) -> dict:
|
||||||
"""Export options from default form."""
|
"""Export options from default form."""
|
||||||
# Parse form data response
|
# Parse form data response
|
||||||
|
workdir = formdata['workdir'][0]
|
||||||
env = formdata['python-env'][0]
|
env = formdata['python-env'][0]
|
||||||
cpu = int(formdata['cpu'][0])
|
cpu = int(formdata['cpu'][0])
|
||||||
mem = int(formdata['mem'][0])
|
mem = int(formdata['mem'][0])
|
||||||
|
@ -52,6 +54,7 @@ def options_from_form(formdata) -> dict:
|
||||||
|
|
||||||
# Export options
|
# Export options
|
||||||
options = {
|
options = {
|
||||||
|
'workdir': workdir,
|
||||||
'pyenv': env,
|
'pyenv': env,
|
||||||
'nprocs': cpu,
|
'nprocs': cpu,
|
||||||
'memory': f'{mem}GB',
|
'memory': f'{mem}GB',
|
||||||
|
|
|
@ -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}
|
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}
|
unknowns = {gpu: unknown_default for gpu in gpus if gpu not in GPU_DEFAULTS}
|
||||||
return defaults | unknowns
|
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/',
|
||||||
|
]
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<div class="form-horizontal">
|
<div class="form-horizontal">
|
||||||
|
|
||||||
{% include "views/username.jinja" %}
|
{% include "views/username.jinja" %}
|
||||||
|
{% include "views/chdir.jinja" %}
|
||||||
{% include "views/envs.jinja" %}
|
{% include "views/envs.jinja" %}
|
||||||
{% include "views/resources.jinja" %}
|
{% include "views/resources.jinja" %}
|
||||||
{% include "views/slurm.jinja" %}
|
{% include "views/slurm.jinja" %}
|
||||||
|
|
13
src/glicid_spawner/templates/views/chdir.jinja
Normal file
13
src/glicid_spawner/templates/views/chdir.jinja
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="workdir" class="col-sm-3 control-label">Working directory:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-addon"><span class="fa fa-briefcase"></span></div>
|
||||||
|
<select class="form-control" name="workdir">
|
||||||
|
{%- for folder in folders -%}
|
||||||
|
<option value="{{ folder }}">{{ folder }}</option>
|
||||||
|
{% endfor -%}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,10 +1,13 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="python-env" class="col-sm-3 control-label">Python environment:</label>
|
<label for="python-env" class="col-sm-3 control-label">Python environment:</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select class="form-control" name="python-env">
|
<div class="input-group">
|
||||||
{%- for pyenv in envs -%}
|
<div class="input-group-addon"><span class="fa fa-bar-chart"></span></div>
|
||||||
<option value="{{ pyenv.path }}">{{ pyenv.name }} ({{ pyenv.scope | upper }})</option>
|
<select class="form-control" name="python-env">
|
||||||
{% endfor -%}
|
{%- for pyenv in envs -%}
|
||||||
</select>
|
<option value="{{ pyenv.path }}">{{ pyenv.name }} ({{ pyenv.scope | upper }})</option>
|
||||||
|
{% endfor -%}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -48,6 +48,15 @@ def test_options_attrs(mock_cluster):
|
||||||
|
|
||||||
assert options['username'] == 'john-doe'
|
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']] == [
|
assert [env.path for env in options['envs']] == [
|
||||||
'/john-doe/envs/foo',
|
'/john-doe/envs/foo',
|
||||||
'/john-doe/envs/bar',
|
'/john-doe/envs/bar',
|
||||||
|
@ -243,6 +252,7 @@ def test_options_from_form():
|
||||||
"""Test options from form parser."""
|
"""Test options from form parser."""
|
||||||
# No GPU
|
# No GPU
|
||||||
formdata = {
|
formdata = {
|
||||||
|
'workdir': ['/home/john-doe/'],
|
||||||
'python-env': ['/john-doe/envs/foo'],
|
'python-env': ['/john-doe/envs/foo'],
|
||||||
'cpu': ['1'],
|
'cpu': ['1'],
|
||||||
'mem': ['4'],
|
'mem': ['4'],
|
||||||
|
@ -250,6 +260,7 @@ def test_options_from_form():
|
||||||
}
|
}
|
||||||
|
|
||||||
assert options_from_form(formdata) == {
|
assert options_from_form(formdata) == {
|
||||||
|
'workdir': '/home/john-doe/',
|
||||||
'pyenv': '/john-doe/envs/foo',
|
'pyenv': '/john-doe/envs/foo',
|
||||||
'nprocs': 1,
|
'nprocs': 1,
|
||||||
'memory': '4GB',
|
'memory': '4GB',
|
||||||
|
@ -258,6 +269,7 @@ def test_options_from_form():
|
||||||
|
|
||||||
# With GPU (in defaults list)
|
# With GPU (in defaults list)
|
||||||
formdata = {
|
formdata = {
|
||||||
|
'workdir': ['/scratch/nautilus/users/john-doe/'],
|
||||||
'python-env': ['/john-doe/envs/bar'],
|
'python-env': ['/john-doe/envs/bar'],
|
||||||
'cpu': ['2'],
|
'cpu': ['2'],
|
||||||
'mem': ['16'],
|
'mem': ['16'],
|
||||||
|
@ -266,6 +278,7 @@ def test_options_from_form():
|
||||||
}
|
}
|
||||||
|
|
||||||
assert options_from_form(formdata) == {
|
assert options_from_form(formdata) == {
|
||||||
|
'workdir': '/scratch/nautilus/users/john-doe/',
|
||||||
'pyenv': '/john-doe/envs/bar',
|
'pyenv': '/john-doe/envs/bar',
|
||||||
'nprocs': 2,
|
'nprocs': 2,
|
||||||
'memory': '16GB',
|
'memory': '16GB',
|
||||||
|
@ -276,6 +289,7 @@ def test_options_from_form():
|
||||||
|
|
||||||
# With unknown GPU (default 1h allocation)
|
# With unknown GPU (default 1h allocation)
|
||||||
formdata = {
|
formdata = {
|
||||||
|
'workdir': ['/scratch/waves/projects/'],
|
||||||
'python-env': ['/global/envs/baz'],
|
'python-env': ['/global/envs/baz'],
|
||||||
'cpu': ['8'],
|
'cpu': ['8'],
|
||||||
'mem': ['16'],
|
'mem': ['16'],
|
||||||
|
@ -284,6 +298,7 @@ def test_options_from_form():
|
||||||
}
|
}
|
||||||
|
|
||||||
assert options_from_form(formdata) == {
|
assert options_from_form(formdata) == {
|
||||||
|
'workdir': '/scratch/waves/projects/',
|
||||||
'pyenv': '/global/envs/baz',
|
'pyenv': '/global/envs/baz',
|
||||||
'nprocs': 8,
|
'nprocs': 8,
|
||||||
'memory': '16GB',
|
'memory': '16GB',
|
||||||
|
@ -294,6 +309,7 @@ def test_options_from_form():
|
||||||
|
|
||||||
# Invalid CPU request (0h allocated)
|
# Invalid CPU request (0h allocated)
|
||||||
formdata = {
|
formdata = {
|
||||||
|
'workdir': ['/LAD-DATA/'],
|
||||||
'python-env': ['/global/envs/qux'],
|
'python-env': ['/global/envs/qux'],
|
||||||
'cpu': ['128'],
|
'cpu': ['128'],
|
||||||
'mem': ['4096'],
|
'mem': ['4096'],
|
||||||
|
@ -302,6 +318,7 @@ def test_options_from_form():
|
||||||
}
|
}
|
||||||
|
|
||||||
assert options_from_form(formdata) == {
|
assert options_from_form(formdata) == {
|
||||||
|
'workdir': '/LAD-DATA/',
|
||||||
'pyenv': '/global/envs/qux',
|
'pyenv': '/global/envs/qux',
|
||||||
'nprocs': 128,
|
'nprocs': 128,
|
||||||
'memory': '4096GB',
|
'memory': '4096GB',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Test resources module."""
|
"""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():
|
def test_default_resources():
|
||||||
|
@ -22,3 +22,15 @@ def test_gpu_max_duration():
|
||||||
# Sorted by defaults order, then unknowns
|
# Sorted by defaults order, then unknowns
|
||||||
assert list(gpu) == ['None', 'A100', 'T4', 'K80']
|
assert list(gpu) == ['None', 'A100', 'T4', 'K80']
|
||||||
assert list(gpu.values()) == [24, 1, 3, 3]
|
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/',
|
||||||
|
]
|
||||||
|
|
Loading…
Add table
Reference in a new issue