Skip to content

Commit c6b6b40

Browse files
author
Vasileios Karakasis
authored
Merge branch 'master' into vasp_update
2 parents 2c8068d + fd8ef4b commit c6b6b40

File tree

7 files changed

+133
-44
lines changed

7 files changed

+133
-44
lines changed

reframe/core/launchers/mpi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def command(self, job):
110110
if job.num_cpus_per_task:
111111
ret += ['--cpus-per-task=%s' % str(job.num_cpus_per_task)]
112112

113-
if job.sched_exclusive_access:
113+
if job.exclusive_access:
114114
ret += ['--exclusive']
115115

116116
if job.use_smt is not None:

reframe/core/pipeline.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,9 +1514,7 @@ def _setup_job(self, name, force_local=False, **job_opts):
15141514
launcher,
15151515
name=name,
15161516
workdir=self._stagedir,
1517-
max_pending_time=self.max_pending_time,
15181517
sched_access=self._current_partition.access,
1519-
sched_exclusive_access=self.exclusive_access,
15201518
**job_opts)
15211519

15221520
def _setup_perf_logging(self):
@@ -1768,6 +1766,8 @@ def run(self):
17681766
self.job.time_limit = (self.time_limit or rt.runtime().get_option(
17691767
f'systems/0/partitions/@{self.current_partition.name}/time_limit')
17701768
)
1769+
self.job.max_pending_time = self.max_pending_time
1770+
self.job.exclusive_access = self.exclusive_access
17711771
exec_cmd = [self.job.launcher.run_command(self.job),
17721772
self.executable, *self.executable_opts]
17731773

reframe/core/schedulers/__init__.py

Lines changed: 120 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
from reframe.core.exceptions import JobError, JobNotStartedError
1919
from reframe.core.launchers import JobLauncher
2020
from reframe.core.logging import getlogger, DEBUG2
21+
from reframe.core.meta import RegressionTestMeta
22+
23+
24+
class JobMeta(RegressionTestMeta, abc.ABCMeta):
25+
'''Job metaclass.'''
2126

2227

2328
class JobScheduler(abc.ABC):
@@ -112,7 +117,7 @@ def log(self, message, level=DEBUG2):
112117
getlogger().log(level, f'[S] {self.registered_name}: {message}')
113118

114119

115-
class Job(jsonext.JSONSerializable):
120+
class Job(jsonext.JSONSerializable, metaclass=JobMeta):
116121
'''A job descriptor.
117122
118123
A job descriptor is created by the framework after the "setup" phase and
@@ -123,19 +128,120 @@ class Job(jsonext.JSONSerializable):
123128
124129
'''
125130

126-
num_tasks = fields.TypedField(int)
127-
num_tasks_per_node = fields.TypedField(int, type(None))
128-
num_tasks_per_core = fields.TypedField(int, type(None))
129-
num_tasks_per_socket = fields.TypedField(int, type(None))
130-
num_cpus_per_task = fields.TypedField(int, type(None))
131-
use_smt = fields.TypedField(bool, type(None))
132-
time_limit = fields.TimerField(type(None))
131+
#: Number of tasks for this job.
132+
#:
133+
#: :type: integral
134+
#: :default: ``1``
135+
#:
136+
#: .. note::
137+
#: This attribute is set by the framework just before submitting the job
138+
#: based on the test information.
139+
#:
140+
#: .. versionadded:: 3.11.0
141+
num_tasks = variable(int, value=1)
142+
143+
#: Number of tasks per node for this job.
144+
#:
145+
#: :type: integral or :class:`NoneType`
146+
#: :default: ``None``
147+
#:
148+
#: .. note::
149+
#: This attribute is set by the framework just before submitting the job
150+
#: based on the test information.
151+
#:
152+
#: .. versionadded:: 3.11.0
153+
num_tasks_per_node = variable(int, type(None), value=None)
154+
155+
#: Number of tasks per core for this job.
156+
#:
157+
#: :type: integral or :class:`NoneType`
158+
#: :default: ``None``
159+
#:
160+
#: .. note::
161+
#: This attribute is set by the framework just before submitting the job
162+
#: based on the test information.
163+
#:
164+
#: .. versionadded:: 3.11.0
165+
num_tasks_per_core = variable(int, type(None), value=None)
166+
167+
#: Number of tasks per socket for this job.
168+
#:
169+
#: :type: integral or :class:`NoneType`
170+
#: :default: ``None``
171+
#:
172+
#: .. note::
173+
#: This attribute is set by the framework just before submitting the job
174+
#: based on the test information.
175+
#:
176+
#: .. versionadded:: 3.11.0
177+
num_tasks_per_socket = variable(int, type(None), value=None)
178+
179+
#: Number of processing elements associated with each task for this job.
180+
#:
181+
#: :type: integral or :class:`NoneType`
182+
#: :default: ``None``
183+
#:
184+
#: .. note::
185+
#: This attribute is set by the framework just before submitting the job
186+
#: based on the test information.
187+
#:
188+
#: .. versionadded:: 3.11.0
189+
num_cpus_per_task = variable(int, type(None), value=None)
190+
191+
#: Enable SMT for this job.
192+
#:
193+
#: :type: :class:`bool` or :class:`NoneType`
194+
#: :default: ``None``
195+
#:
196+
#: .. note::
197+
#: This attribute is set by the framework just before submitting the job
198+
#: based on the test information.
199+
#:
200+
#: .. versionadded:: 3.11.0
201+
use_smt = variable(bool, type(None), value=None)
202+
203+
#: Request exclusive access on the nodes for this job.
204+
#:
205+
#: :type: :class:`bool`
206+
#: :default: ``false``
207+
#:
208+
#: .. note::
209+
#: This attribute is set by the framework just before submitting the job
210+
#: based on the test information.
211+
#:
212+
#: .. versionadded:: 3.11.0
213+
exclusive_access = variable(bool, value=False)
214+
215+
#: Time limit for this job.
216+
#:
217+
#: See :attr:`reframe.core.pipeline.RegressionTest.time_limit` for more
218+
#: details.
219+
#:
220+
#: .. note::
221+
#: This attribute is set by the framework just before submitting the job
222+
#: based on the test information.
223+
#:
224+
#: .. versionadded:: 3.11.0
225+
time_limit = variable(type(None), field=fields.TimerField, value=None)
226+
227+
#: Maximum pending time for this job.
228+
#:
229+
#: See :attr:`reframe.core.pipeline.RegressionTest.max_pending_time` for
230+
#: more details.
231+
#:
232+
#: .. note::
233+
#: This attribute is set by the framework just before submitting the job
234+
#: based on the test information.
235+
#:
236+
#: .. versionadded:: 3.11.0
237+
max_pending_time = variable(type(None),
238+
field=fields.TimerField, value=None)
133239

134-
#: Options to be passed to the backend job scheduler.
240+
#: Arbitrary options to be passed to the backend job scheduler.
135241
#:
136242
#: :type: :class:`List[str]`
137243
#: :default: ``[]``
138-
options = fields.TypedField(typ.List[str])
244+
options = variable(typ.List[str], value=[])
139245

140246
#: The (parallel) program launcher that will be used to launch the
141247
#: (parallel) executable of this job.
@@ -157,7 +263,7 @@ class Job(jsonext.JSONSerializable):
157263
#: self.job.launcher = getlauncher('local')()
158264
#:
159265
#: :type: :class:`reframe.core.launchers.JobLauncher`
160-
launcher = fields.TypedField(JobLauncher)
266+
launcher = variable(JobLauncher)
161267

162268
# The sched_* arguments are exposed also to the frontend
163269
def __init__(self,
@@ -166,34 +272,20 @@ def __init__(self,
166272
script_filename=None,
167273
stdout=None,
168274
stderr=None,
169-
max_pending_time=None,
170275
sched_flex_alloc_nodes=None,
171276
sched_access=[],
172-
sched_exclusive_access=None,
173277
sched_options=None):
174278

175-
# Mutable fields
176-
self.num_tasks = 1
177-
self.num_tasks_per_node = None
178-
self.num_tasks_per_core = None
179-
self.num_tasks_per_socket = None
180-
self.num_cpus_per_task = None
181-
self.use_smt = None
182-
self.time_limit = None
183-
self.cli_options = list(sched_options) if sched_options else []
184-
self.options = []
185-
279+
self._cli_options = list(sched_options) if sched_options else []
186280
self._name = name
187281
self._workdir = workdir
188282
self._script_filename = script_filename or '%s.sh' % name
189283
self._stdout = stdout or '%s.out' % name
190284
self._stderr = stderr or '%s.err' % name
191-
self._max_pending_time = max_pending_time
192285

193286
# Backend scheduler related information
194287
self._sched_flex_alloc_nodes = sched_flex_alloc_nodes
195288
self._sched_access = sched_access
196-
self._sched_exclusive_access = sched_exclusive_access
197289

198290
# Live job information; to be filled during job's lifetime by the
199291
# scheduler
@@ -224,8 +316,8 @@ def workdir(self):
224316
return self._workdir
225317

226318
@property
227-
def max_pending_time(self):
228-
return self._max_pending_time
319+
def cli_options(self):
320+
return self._cli_options
229321

230322
@property
231323
def script_filename(self):
@@ -247,10 +339,6 @@ def sched_flex_alloc_nodes(self):
247339
def sched_access(self):
248340
return self._sched_access
249341

250-
@property
251-
def sched_exclusive_access(self):
252-
return self._sched_exclusive_access
253-
254342
@property
255343
def completion_time(self):
256344
'''The completion time of this job as a floating point number

reframe/core/schedulers/lsf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def emit_preamble(self, job):
7171
else:
7272
preamble.append(self._prefix + ' ' + opt)
7373

74-
if job.sched_exclusive_access:
74+
if job.exclusive_access:
7575
preamble.append(f'{self._prefix} -x')
7676

7777
# Filter out empty statements before returning

reframe/core/schedulers/slurm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ def emit_preamble(self, job):
178178
self._format_option('%d:%d:%d' % (h, m, s), '--time={0}')
179179
)
180180

181-
if job.sched_exclusive_access:
181+
if job.exclusive_access:
182182
preamble.append(
183-
self._format_option(job.sched_exclusive_access, '--exclusive')
183+
self._format_option(job.exclusive_access, '--exclusive')
184184
)
185185

186186
if self._use_nodes_opt:

unittests/test_launchers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ def job(make_job, launcher):
7676
stdout='fake_stdout',
7777
stderr='fake_stderr',
7878
sched_access=access,
79-
sched_exclusive_access='fake_exclude_access',
8079
sched_options=['--fake'])
8180
job.num_tasks = 4
8281
job.num_tasks_per_node = 2
@@ -85,6 +84,7 @@ def job(make_job, launcher):
8584
job.num_cpus_per_task = 2
8685
job.use_smt = True
8786
job.time_limit = '10m'
87+
job.exclusive_access = True
8888
job.options = ['--gres=gpu:4', '#DW jobdw anything']
8989
job.launcher.options = ['--foo']
9090
return job

unittests/test_schedulers.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ def minimal_job(make_job):
9090

9191
@pytest.fixture
9292
def fake_job(make_job):
93-
ret = make_job(sched_exclusive_access=True,
94-
sched_options=['--account=spam'])
93+
ret = make_job(sched_options=['--account=spam'])
9594
ret.time_limit = '5m'
9695
ret.num_tasks = 16
9796
ret.num_tasks_per_node = 2
9897
ret.num_tasks_per_core = 1
9998
ret.num_tasks_per_socket = 1
10099
ret.num_cpus_per_task = 18
101100
ret.use_smt = True
101+
ret.exclusive_access = True
102102
ret.options += ['--gres=gpu:4',
103103
'#DW jobdw capacity=100GB',
104104
'#DW stage_in source=/foo']
@@ -332,7 +332,8 @@ def test_prepare_minimal(minimal_job):
332332

333333

334334
def test_prepare_no_exclusive(make_job, slurm_only):
335-
job = make_job(sched_exclusive_access=False)
335+
job = make_job()
336+
job.exclusive_access = False
336337
prepare_job(job)
337338
with open(job.script_filename) as fp:
338339
assert re.search(r'--exclusive', fp.read()) is None
@@ -566,8 +567,8 @@ def test_submit_max_pending_time(make_job, exec_ctx, scheduler):
566567
pytest.skip(f"max_pending_time not supported by the "
567568
f"'{scheduler.registered_name}' scheduler")
568569

569-
minimal_job = make_job(sched_access=exec_ctx.access,
570-
max_pending_time=0.05)
570+
minimal_job = make_job(sched_access=exec_ctx.access)
571+
minimal_job.max_pending_time = 0.05
571572

572573
# Monkey-patch the Job's state property to pretend that the job is always
573574
# pending

0 commit comments

Comments
 (0)