2024-02-14 10:02:07 +01:00
|
|
|
"""Web Server Gateway Interface with autoreload to render spawner template.
|
|
|
|
|
|
|
|
Usage: `python -m render`
|
|
|
|
|
|
|
|
"""
|
2024-02-20 15:13:14 +01:00
|
|
|
import sys
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
from pathlib import Path
|
2024-02-14 10:02:07 +01:00
|
|
|
from traceback import format_exc
|
|
|
|
|
|
|
|
from flask import Flask, render_template, request
|
2024-03-13 18:38:29 +01:00
|
|
|
from glicid_spawner.form import options_from_form
|
2024-02-14 15:19:31 +01:00
|
|
|
from glicid_spawner.micromamba import MicromambaEnv
|
2024-02-26 14:35:30 +01:00
|
|
|
from glicid_spawner.resources import CPU, MEMORY, get_folders, gpu_max_duration
|
2024-02-20 15:13:14 +01:00
|
|
|
from glicid_spawner.slurm import gres, sinfo_from_file
|
2024-03-13 18:38:29 +01:00
|
|
|
from glicid_spawner.templates import render_template as glicid_template
|
2024-02-14 10:02:07 +01:00
|
|
|
from livereload import Server
|
|
|
|
|
2024-02-14 18:45:57 +01:00
|
|
|
# Dummy username and python environments
|
|
|
|
USERNAME = 'john-doe'
|
|
|
|
ENVS = [
|
|
|
|
MicromambaEnv('USER', 'foo', f'/{USERNAME}/envs/foo'),
|
|
|
|
MicromambaEnv('USER', 'bar', f'/{USERNAME}/envs/bar'),
|
|
|
|
MicromambaEnv('GLOBAL', 'baz', '/global/envs/baz'),
|
|
|
|
]
|
2024-02-26 14:35:30 +01:00
|
|
|
FOLDERS = get_folders(USERNAME)
|
2024-02-20 15:13:14 +01:00
|
|
|
|
|
|
|
# Dummy SLURM config
|
|
|
|
DATA = Path(__file__).parent / '..' / 'tests' / 'data'
|
|
|
|
SINFO = sinfo_from_file(
|
|
|
|
DATA / 'sinfo.txt', with_states=('idle', 'mixed', 'allocated', 'completing', 'planned')
|
|
|
|
)
|
|
|
|
|
|
|
|
# Single vs. multi-cluster implementation
|
|
|
|
SLURM_SINGLE_CLUSTER = {'N/A': SINFO.pop('N/A')}
|
|
|
|
SLURM_MULTI_CLUSTER = SINFO
|
|
|
|
GPU_SINGLE_CLUSTER = gpu_max_duration(gres(SLURM_SINGLE_CLUSTER))
|
|
|
|
GPU_MULTI_CLUSTER = gpu_max_duration(gres(SLURM_MULTI_CLUSTER))
|
|
|
|
|
|
|
|
# Format dummy options
|
2024-02-14 15:19:31 +01:00
|
|
|
OPTIONS = {
|
2024-02-14 18:45:57 +01:00
|
|
|
'username': USERNAME,
|
2024-02-26 14:35:30 +01:00
|
|
|
'folders': FOLDERS,
|
2024-02-14 18:45:57 +01:00
|
|
|
'envs': ENVS,
|
|
|
|
'cpus': CPU,
|
2024-02-20 15:13:14 +01:00
|
|
|
'mems': MEMORY,
|
|
|
|
# Multi-cluster by default. See `--single-cluster` flag in CLI for single cluster config.
|
|
|
|
'gpus': GPU_MULTI_CLUSTER,
|
|
|
|
'sinfo': SLURM_MULTI_CLUSTER,
|
2024-02-14 15:19:31 +01:00
|
|
|
}
|
|
|
|
|
2024-02-14 10:02:07 +01:00
|
|
|
# Flask app
|
|
|
|
app = Flask(__name__)
|
|
|
|
app.debug = True
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def home():
|
|
|
|
"""Form spawner home page."""
|
|
|
|
return render_template(
|
2024-02-14 18:45:57 +01:00
|
|
|
'form.html',
|
2024-03-13 18:38:29 +01:00
|
|
|
spawner_options_form=glicid_template('spawner_form.jinja', **OPTIONS),
|
2024-02-14 18:45:57 +01:00
|
|
|
options=OPTIONS,
|
2024-02-14 10:02:07 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/submit', methods=['POST'])
|
|
|
|
def submit():
|
|
|
|
"""Reformat form data and extract spawner options.
|
|
|
|
|
|
|
|
https://jupyterhub.readthedocs.io/en/stable/reference/spawners.html#spawner-options-from-form
|
|
|
|
|
|
|
|
"""
|
|
|
|
formdata = dict(request.form.lists())
|
|
|
|
|
|
|
|
# Trying to parse the options from the formdata
|
|
|
|
try:
|
|
|
|
return render_template(
|
|
|
|
'options.html', formdata=formdata, options=options_from_form(formdata)
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
return render_template('options.html', formdata=formdata, err=format_exc())
|
|
|
|
|
|
|
|
|
2024-02-20 15:13:14 +01:00
|
|
|
@app.route('/favicon.ico')
|
|
|
|
def favicon():
|
|
|
|
"""Dummy favicon."""
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
2024-02-14 10:02:07 +01:00
|
|
|
def server_autoreload():
|
|
|
|
"""Start auto-reload server."""
|
|
|
|
server = Server(app.wsgi_app)
|
|
|
|
server.serve()
|
|
|
|
|
|
|
|
|
2024-02-20 15:13:14 +01:00
|
|
|
def cli(argv=None):
|
|
|
|
"""Command line interface."""
|
|
|
|
parser = ArgumentParser('Spawner form render')
|
|
|
|
parser.add_argument(
|
|
|
|
'--single-cluster',
|
|
|
|
action='store_true',
|
|
|
|
help='Toggle SLURM config to single cluster configuration.',
|
|
|
|
)
|
|
|
|
|
|
|
|
args, _ = parser.parse_known_args(argv)
|
|
|
|
|
|
|
|
if args.single_cluster:
|
|
|
|
OPTIONS['gpus'] = GPU_SINGLE_CLUSTER
|
|
|
|
OPTIONS['sinfo'] = SLURM_SINGLE_CLUSTER
|
|
|
|
|
2024-02-14 10:02:07 +01:00
|
|
|
server_autoreload()
|
2024-02-20 15:13:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
cli(sys.argv)
|