spawner/src/glicid_spawner/spawner.py

93 lines
2.7 KiB
Python
Raw Normal View History

2024-01-29 17:47:47 +01:00
"""GLiCID spawner module."""
2024-02-22 17:41:58 +01:00
import asyncio
import re
2024-02-22 17:41:58 +01:00
from batchspawner import JobStatus, SlurmSpawner
2024-02-26 14:54:56 +01:00
from traitlets import Bool, Integer, Unicode, default
from .form import options_form, options_from_form
2024-02-22 17:41:58 +01:00
from .progress import ElapseTime, get_progress
2024-01-29 17:47:47 +01:00
class GlicidSpawner(SlurmSpawner):
"""Glicid SLURM Spawner."""
2024-02-26 14:54:56 +01:00
disable_user_config = Bool(
True,
help='Disable per-user configuration of single-user servers.',
).tag(config=True)
notebook_dir = Unicode(
'/',
help='Path to the notebook directory for the single-user server.',
).tag(config=True)
@default('default_url')
def _default_url_default(self) -> str:
"""The URL the single-user server should start in."""
2024-02-26 14:59:24 +01:00
return '/lab/tree' + self.user_options.get('workdir', '/home/{username}')
2024-02-26 14:54:56 +01:00
batchspawner_singleuser_cmd = Unicode(
'glicid-spawner-singleuser',
help='Spawner singleuser command.',
).tag(config=True)
2024-02-26 09:55:15 +01:00
req_job_name = Unicode(
'jupyterhub_glicid',
help='SLURM job name',
).tag(config=True)
req_qos = Unicode(
'short',
help='QoS name to submit job to resource manager',
).tag(config=True)
slurm_job_id_re = Unicode(r'(\d+)(?:;(\w+))?').tag(config=True)
def parse_job_id(self, output):
"""Parse job id with cluster name support.
If cluster name is present, `job_id` will be a string
and suffix with `-M job_cluster` name.
"""
for job_id, job_cluster in re.findall(self.slurm_job_id_re, output):
return f'{job_id} -M {job_cluster}' if job_cluster else int(job_id)
self.log.error(f'GlicidSpawner unable to parse job ID from text: {output}')
return None
2024-02-14 10:48:56 +01:00
@default('options_form')
def _options_form_default(self) -> str:
"""JupyterHub rendered form template."""
return options_form(self.user.name)
2024-01-29 17:47:47 +01:00
def options_from_form(self, formdata) -> dict:
"""Export options from form."""
return options_from_form(formdata)
2024-02-22 17:41:58 +01:00
progress_rate = Integer(
5, help='Interval in seconds at which progress is polled for messages'
).tag(config=True)
async def progress(self):
"""Progress bar feedback."""
elapse_time = 0
while True:
if self.state_isrunning():
job_status = JobStatus.RUNNING
elapse_time += self.progress_rate
elif self.state_ispending():
job_status = JobStatus.PENDING
else:
job_status = JobStatus.NOTFOUND
yield get_progress(job_status, elapse_time)
if elapse_time >= ElapseTime.CONNECT:
break
await asyncio.sleep(self.progress_rate)