17
17
"""
18
18
import pwd
19
19
import os
20
+ import re
20
21
21
22
import xml .etree .ElementTree as ET
22
23
@@ -46,7 +47,7 @@ def format_template(template, *args, **kwargs):
46
47
"""
47
48
if isinstance (template , Template ):
48
49
return template .render (* args , ** kwargs )
49
- elif '{{' in template or '{%' in template :
50
+ elif '{{' in template or '{%' in template :
50
51
return Template (template ).render (* args , ** kwargs )
51
52
return template .format (* args , ** kwargs )
52
53
@@ -78,52 +79,52 @@ class BatchSpawnerBase(Spawner):
78
79
# override default server ip since batch jobs normally running remotely
79
80
ip = Unicode ("0.0.0.0" , help = "Address for singleuser server to listen at" ).tag (config = True )
80
81
81
- exec_prefix = Unicode ('sudo -E -u {username}' , \
82
+ exec_prefix = Unicode ('sudo -E -u {username}' ,
82
83
help = "Standard executon prefix (e.g. the default sudo -E -u {username})"
83
84
).tag (config = True )
84
85
85
86
# all these req_foo traits will be available as substvars for templated strings
86
- req_queue = Unicode ('' , \
87
+ req_queue = Unicode ('' ,
87
88
help = "Queue name to submit job to resource manager"
88
89
).tag (config = True )
89
90
90
- req_host = Unicode ('' , \
91
+ req_host = Unicode ('' ,
91
92
help = "Host name of batch server to submit job to resource manager"
92
93
).tag (config = True )
93
94
94
- req_memory = Unicode ('' , \
95
+ req_memory = Unicode ('' ,
95
96
help = "Memory to request from resource manager"
96
97
).tag (config = True )
97
98
98
- req_nprocs = Unicode ('' , \
99
+ req_nprocs = Unicode ('' ,
99
100
help = "Number of processors to request from resource manager"
100
101
).tag (config = True )
101
102
102
- req_ngpus = Unicode ('' , \
103
+ req_ngpus = Unicode ('' ,
103
104
help = "Number of GPUs to request from resource manager"
104
105
).tag (config = True )
105
106
106
- req_runtime = Unicode ('' , \
107
+ req_runtime = Unicode ('' ,
107
108
help = "Length of time for submitted job to run"
108
109
).tag (config = True )
109
110
110
- req_partition = Unicode ('' , \
111
+ req_partition = Unicode ('' ,
111
112
help = "Partition name to submit job to resource manager"
112
113
).tag (config = True )
113
114
114
- req_account = Unicode ('' , \
115
+ req_account = Unicode ('' ,
115
116
help = "Account name string to pass to the resource manager"
116
117
).tag (config = True )
117
118
118
- req_options = Unicode ('' , \
119
+ req_options = Unicode ('' ,
119
120
help = "Other options to include into job submission script"
120
121
).tag (config = True )
121
122
122
- req_prologue = Unicode ('' , \
123
+ req_prologue = Unicode ('' ,
123
124
help = "Script to run before single user server starts."
124
125
).tag (config = True )
125
126
126
- req_epilogue = Unicode ('' , \
127
+ req_epilogue = Unicode ('' ,
127
128
help = "Script to run after single user server ends."
128
129
).tag (config = True )
129
130
@@ -148,8 +149,7 @@ def _req_keepvars_default(self):
148
149
"added to the defaults in keepvars, "
149
150
"comma separated list." )
150
151
151
-
152
- batch_script = Unicode ('' , \
152
+ batch_script = Unicode ('' ,
153
153
help = "Template for job submission script. Traits on this class named like req_xyz "
154
154
"will be substituted in the template for {xyz} using string.Formatter. "
155
155
"Must include {cmd} which will be replaced with the jupyterhub-singleuser command line."
@@ -174,7 +174,7 @@ def get_req_subvars(self):
174
174
subvars ['keepvars' ] += ',' + subvars ['keepvars_extra' ]
175
175
return subvars
176
176
177
- batch_submit_cmd = Unicode ('' , \
177
+ batch_submit_cmd = Unicode ('' ,
178
178
help = "Command to run to submit batch scripts. Formatted using req_xyz traits as {xyz}."
179
179
).tag (config = True )
180
180
@@ -244,7 +244,7 @@ def submit_batch_script(self):
244
244
return self .job_id
245
245
246
246
# Override if your batch system needs something more elaborate to read the job status
247
- batch_query_cmd = Unicode ('' , \
247
+ batch_query_cmd = Unicode ('' ,
248
248
help = "Command to run to read job status. Formatted using req_xyz traits as {xyz} "
249
249
"and self.job_id as {job_id}."
250
250
).tag (config = True )
@@ -335,7 +335,7 @@ def poll(self):
335
335
self .clear_state ()
336
336
return 1
337
337
338
- startup_poll_interval = Float (0.5 , \
338
+ startup_poll_interval = Float (0.5 ,
339
339
help = "Polling interval (seconds) to check job state during startup"
340
340
).tag (config = True )
341
341
@@ -345,8 +345,9 @@ def start(self):
345
345
if self .user and self .user .server and self .user .server .port :
346
346
self .port = self .user .server .port
347
347
self .db .commit ()
348
- elif (jupyterhub .version_info < (0 ,7 ) and not self .user .server .port ) or \
349
- (jupyterhub .version_info >= (0 ,7 ) and not self .port ):
348
+ elif (jupyterhub .version_info < (0 ,7 ) and not self .user .server .port ) or (
349
+ jupyterhub .version_info >= (0 ,7 ) and not self .port
350
+ ):
350
351
self .port = random_port ()
351
352
self .db .commit ()
352
353
job = yield self .submit_batch_script ()
@@ -367,8 +368,8 @@ def start(self):
367
368
else :
368
369
self .log .warn ('Job ' + self .job_id + ' neither pending nor running.\n ' +
369
370
self .job_status )
370
- raise RuntimeError ('The Jupyter batch job has disappeared '
371
- ' while pending in the queue or died immediately '
371
+ raise RuntimeError ('The Jupyter batch job has disappeared'
372
+ ' while pending in the queue or died immediately'
372
373
' after starting.' )
373
374
yield gen .sleep (self .startup_poll_interval )
374
375
@@ -405,7 +406,6 @@ def stop(self, now=False):
405
406
self .job_id , self .current_ip , self .port )
406
407
)
407
408
408
- import re
409
409
410
410
class BatchSpawnerRegexStates (BatchSpawnerBase ):
411
411
"""Subclass of BatchSpawnerBase that uses config-supplied regular expressions
@@ -442,13 +442,15 @@ def state_ispending(self):
442
442
assert self .state_pending_re , "Misconfigured: define state_running_re"
443
443
if self .job_status and re .search (self .state_pending_re , self .job_status ):
444
444
return True
445
- else : return False
445
+ else :
446
+ return False
446
447
447
448
def state_isrunning (self ):
448
449
assert self .state_running_re , "Misconfigured: define state_running_re"
449
450
if self .job_status and re .search (self .state_running_re , self .job_status ):
450
451
return True
451
- else : return False
452
+ else :
453
+ return False
452
454
453
455
def state_gethost (self ):
454
456
assert self .state_exechost_re , "Misconfigured: define state_exechost_re"
@@ -461,6 +463,7 @@ def state_gethost(self):
461
463
else :
462
464
return match .expand (self .state_exechost_exp )
463
465
466
+
464
467
class TorqueSpawner (BatchSpawnerRegexStates ):
465
468
batch_script = Unicode ("""#!/bin/sh
466
469
#PBS -q {queue}@{host}
@@ -486,6 +489,7 @@ class TorqueSpawner(BatchSpawnerRegexStates):
486
489
state_running_re = Unicode (r'<job_state>R</job_state>' ).tag (config = True )
487
490
state_exechost_re = Unicode (r'<exec_host>((?:[\w_-]+\.?)+)/\d+' ).tag (config = True )
488
491
492
+
489
493
class MoabSpawner (TorqueSpawner ):
490
494
# outputs job id string
491
495
batch_submit_cmd = Unicode ('msub' ).tag (config = True )
@@ -496,6 +500,7 @@ class MoabSpawner(TorqueSpawner):
496
500
state_running_re = Unicode (r'State="Running"' ).tag (config = True )
497
501
state_exechost_re = Unicode (r'AllocNodeList="([^\r\n\t\f :"]*)' ).tag (config = True )
498
502
503
+
499
504
class UserEnvMixin :
500
505
"""Mixin class that computes values for USER, SHELL and HOME in the environment passed to
501
506
the job submission subprocess in case the batch system needs these for the batch script."""
@@ -522,23 +527,8 @@ def get_env(self):
522
527
env = self .user_env (env )
523
528
return env
524
529
525
- class SlurmSpawner (UserEnvMixin ,BatchSpawnerRegexStates ):
526
- """A Spawner that just uses Popen to start local processes."""
527
-
528
- # all these req_foo traits will be available as substvars for templated strings
529
- req_cluster = Unicode ('' , \
530
- help = "Cluster name to submit job to resource manager"
531
- ).tag (config = True )
532
-
533
- req_qos = Unicode ('' , \
534
- help = "QoS name to submit job to resource manager"
535
- ).tag (config = True )
536
-
537
- req_srun = Unicode ('srun' ,
538
- help = "Job step wrapper, default 'srun'. Set to '' you do not want "
539
- "to run in job step (affects environment handling)"
540
- ).tag (config = True )
541
530
531
+ class SlurmSpawner (UserEnvMixin ,BatchSpawnerRegexStates ):
542
532
batch_script = Unicode ("""#!/bin/bash
543
533
#SBATCH --output={{homedir}}/jupyterhub_slurmspawner_%j.log
544
534
#SBATCH --job-name=spawner-jupyterhub
@@ -558,6 +548,21 @@ class SlurmSpawner(UserEnvMixin,BatchSpawnerRegexStates):
558
548
echo "jupyterhub-singleuser ended gracefully"
559
549
{{epilogue}}
560
550
""" ).tag (config = True )
551
+
552
+ # all these req_foo traits will be available as substvars for templated strings
553
+ req_cluster = Unicode ('' ,
554
+ help = "Cluster name to submit job to resource manager"
555
+ ).tag (config = True )
556
+
557
+ req_qos = Unicode ('' ,
558
+ help = "QoS name to submit job to resource manager"
559
+ ).tag (config = True )
560
+
561
+ req_srun = Unicode ('srun' ,
562
+ help = "Job step wrapper, default 'srun'. Set to '' you do not want "
563
+ "to run in job step (affects environment handling)"
564
+ ).tag (config = True )
565
+
561
566
# outputs line like "Submitted batch job 209"
562
567
batch_submit_cmd = Unicode ('sbatch --parsable' ).tag (config = True )
563
568
# outputs status and exec node like "RUNNING hostname"
@@ -579,6 +584,7 @@ def parse_job_id(self, output):
579
584
raise e
580
585
return id
581
586
587
+
582
588
class MultiSlurmSpawner (SlurmSpawner ):
583
589
'''When slurm has been compiled with --enable-multiple-slurmd, the
584
590
administrator sets the name of the slurmd instance via the slurmd -N
@@ -591,6 +597,7 @@ def state_gethost(self):
591
597
host = SlurmSpawner .state_gethost (self )
592
598
return self .daemon_resolver .get (host , host )
593
599
600
+
594
601
class GridengineSpawner (BatchSpawnerBase ):
595
602
batch_script = Unicode ("""#!/bin/bash
596
603
#$ -j yes
@@ -640,6 +647,7 @@ def state_gethost(self):
640
647
self .log .error ("Spawner unable to match host addr in job {0} with status {1}" .format (self .job_id , self .job_status ))
641
648
return
642
649
650
+
643
651
class CondorSpawner (UserEnvMixin ,BatchSpawnerRegexStates ):
644
652
batch_script = Unicode ("""
645
653
Executable = /bin/sh
@@ -677,6 +685,7 @@ def parse_job_id(self, output):
677
685
def cmd_formatted_for_batch (self ):
678
686
return super (CondorSpawner ,self ).cmd_formatted_for_batch ().replace ('"' ,'""' ).replace ("'" ,"''" )
679
687
688
+
680
689
class LsfSpawner (BatchSpawnerBase ):
681
690
'''A Spawner that uses IBM's Platform Load Sharing Facility (LSF) to launch notebooks.'''
682
691
@@ -723,7 +732,6 @@ def state_isrunning(self):
723
732
if self .job_status :
724
733
return self .job_status .split (' ' )[0 ].upper () == 'RUN'
725
734
726
-
727
735
def state_gethost (self ):
728
736
if self .job_status :
729
737
return self .job_status .split (' ' )[1 ].strip ()
0 commit comments