Skip to content

Commit 13a7261

Browse files
author
Vasileios Karakasis
authored
Merge pull request #1803 from rsarm/feat/preload-cmds
[feat] Add a new `prepare_cmds` partition configuration parameter which allows to emit commands before loading any environment
2 parents aabcab1 + f535351 commit 13a7261

File tree

7 files changed

+53
-12
lines changed

7 files changed

+53
-12
lines changed

docs/config_reference.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,16 @@ System Partition Configuration
308308
This option is relevant only when ReFrame executes with the `asynchronous execution policy <pipeline.html#execution-policies>`__.
309309

310310

311+
.. js:attribute:: .systems[].partitions[].prepare_cmds
312+
313+
:required: No
314+
:default: ``[]``
315+
316+
List of shell commands to be emitted before any environment loading commands are emitted.
317+
318+
.. versionadded:: 3.5.0
319+
320+
311321
.. js:attribute:: .systems[].partitions[].resources
312322

313323
:required: No

docs/tutorial_advanced.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,13 +389,16 @@ Generally, ReFrame generates the job shell scripts using the following pattern:
389389
390390
#!/bin/bash -l
391391
{job_scheduler_preamble}
392-
{test_environment}
392+
{prepare_cmds}
393+
{env_load_cmds}
393394
{prerun_cmds}
394395
{parallel_launcher} {executable} {executable_opts}
395396
{postrun_cmds}
396397
397398
The ``job_scheduler_preamble`` contains the backend job scheduler directives that control the job allocation.
398-
The ``test_environment`` are the necessary commands for setting up the environment of the test.
399+
The ``prepare_cmds`` are commands that can be emitted before the test environment commands.
400+
These can be specified with the :js:attr:`prepare_cmds <.systems[].partitions[].prepare_cmds>` partition configuration option.
401+
The ``env_load_cmds`` are the necessary commands for setting up the environment of the test.
399402
These include any modules or environment variables set at the `system partition level <config_reference.html#system-partition-configuration>`__ or any `modules <regression_test_api.html#reframe.core.pipeline.RegressionTest.modules>`__ or `environment variables <regression_test_api.html#reframe.core.pipeline.RegressionTest.variables>`__ set at the test level.
400403
Then the commands specified in :attr:`prerun_cmds <reframe.core.pipeline.RegressionTest.prerun_cmds>` follow, while those specified in the :attr:`postrun_cmds <reframe.core.pipeline.RegressionTest.postrun_cmds>` come after the launch of the parallel job.
401404
The parallel launch itself consists of three parts:

reframe/core/pipeline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,7 @@ def compile(self):
12201220
try:
12211221
self._build_job.prepare(
12221222
build_commands, environs,
1223+
self._current_partition.prepare_cmds,
12231224
login=rt.runtime().get_option('general/0/use_login_shell'),
12241225
trap_errors=True
12251226
)
@@ -1345,6 +1346,7 @@ def run(self):
13451346
self.logger.debug('Generating the run script')
13461347
self._job.prepare(
13471348
commands, environs,
1349+
self._current_partition.prepare_cmds,
13481350
login=rt.runtime().get_option('general/0/use_login_shell'),
13491351
trap_errors=rt.runtime().get_option(
13501352
'general/0/trap_job_errors'

reframe/core/schedulers/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ def nodelist(self):
330330
def submit_time(self):
331331
return self._submit_time
332332

333-
def prepare(self, commands, environs=None, **gen_opts):
333+
def prepare(self, commands, environs=None, prepare_cmds=None, **gen_opts):
334334
environs = environs or []
335335
if self.num_tasks <= 0:
336336
getlogger().debug(f'[F] Flexible node allocation requested')
@@ -357,6 +357,10 @@ def prepare(self, commands, environs=None, **gen_opts):
357357
with shell.generate_script(self.script_filename,
358358
**gen_opts) as builder:
359359
builder.write_prolog(self.scheduler.emit_preamble(self))
360+
prepare_cmds = prepare_cmds or []
361+
for c in prepare_cmds:
362+
builder.write_body(c)
363+
360364
builder.write(runtime.emit_loadenv_commands(*environs))
361365
for c in commands:
362366
builder.write_body(c)

reframe/core/systems.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class SystemPartition(jsonext.JSONSerializable):
2222

2323
def __init__(self, parent, name, sched_type, launcher_type,
2424
descr, access, container_environs, resources,
25-
local_env, environs, max_jobs):
25+
local_env, environs, max_jobs, prepare_cmds):
2626
getlogger().debug(f'Initializing system partition {name!r}')
2727
self._parent_system = parent
2828
self._name = name
@@ -35,6 +35,7 @@ def __init__(self, parent, name, sched_type, launcher_type,
3535
self._local_env = local_env
3636
self._environs = environs
3737
self._max_jobs = max_jobs
38+
self._prepare_cmds = prepare_cmds
3839
self._resources = {r['name']: r['options'] for r in resources}
3940

4041
@property
@@ -98,6 +99,14 @@ def max_jobs(self):
9899
'''
99100
return self._max_jobs
100101

102+
@property
103+
def prepare_cmds(self):
104+
'''Commands to be emitted before loading the modules.
105+
106+
:type: :class:`List[str]`
107+
'''
108+
return self._prepare_cmds
109+
101110
@property
102111
def name(self):
103112
'''The name of this partition.
@@ -317,7 +326,8 @@ def create(cls, site_config):
317326
modules=site_config.get(f'{partid}/modules'),
318327
variables=site_config.get(f'{partid}/variables')
319328
),
320-
max_jobs=site_config.get(f'{partid}/max_jobs')
329+
max_jobs=site_config.get(f'{partid}/max_jobs'),
330+
prepare_cmds=site_config.get(f'{partid}/prepare_cmds')
321331
)
322332
)
323333

reframe/schemas/config.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@
220220
"modules": {"$ref": "#/defs/modules_list"},
221221
"variables": {"$ref": "#/defs/envvar_list"},
222222
"max_jobs": {"type": "number"},
223+
"prepare_cmds": {
224+
"type": "array",
225+
"items": {"type": "string"}
226+
},
223227
"resources": {
224228
"type": "array",
225229
"items": {
@@ -472,6 +476,7 @@
472476
"systems/partitions/modules": [],
473477
"systems/partitions/variables": [],
474478
"systems/partitions/environs": [],
475-
"systems/partitions/max_jobs": 8
479+
"systems/partitions/max_jobs": 8,
480+
"systems/partitions/prepare_cmds": []
476481
}
477482
}

unittests/test_schedulers.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,27 +115,32 @@ def fake_job(make_job):
115115
return ret
116116

117117

118-
def prepare_job(job, command='hostname', pre_run=None, post_run=None):
118+
def prepare_job(job, command='hostname',
119+
pre_run=None, post_run=None,
120+
prepare_cmds=None):
119121
environs = [Environment(name='foo', modules=['testmod_foo'])]
120122
pre_run = pre_run or ['echo prerun']
121123
post_run = post_run or ['echo postrun']
124+
prepare_cmds = prepare_cmds or ['echo prepare']
122125
with rt.module_use('unittests/modules'):
123126
job.prepare(
124127
[
125128
*pre_run,
126129
job.launcher.run_command(job) + ' ' + command,
127130
post_run
128131
],
129-
environs
132+
environs,
133+
prepare_cmds
130134
)
131135

132136

133137
def assert_job_script_sanity(job):
134138
'''Assert the sanity of the produced script file.'''
135139
with open(job.script_filename) as fp:
136-
matches = re.findall(r'echo prerun|echo postrun|hostname',
140+
matches = re.findall(r'echo prepare|echo prerun|echo postrun|hostname',
137141
fp.read())
138-
assert ['echo prerun', 'hostname', 'echo postrun'] == matches
142+
assert ['echo prepare', 'echo prerun', 'hostname',
143+
'echo postrun'] == matches
139144

140145

141146
def _expected_slurm_directives(job):
@@ -508,7 +513,8 @@ def test_cancel_with_grace(minimal_job, scheduler, local_only):
508513
prepare_job(minimal_job,
509514
command='sleep 5 &',
510515
pre_run=['trap -- "" TERM'],
511-
post_run=['echo $!', 'wait'])
516+
post_run=['echo $!', 'wait'],
517+
prepare_cmds=[''])
512518
minimal_job.submit()
513519

514520
# Stall a bit here to let the the spawned process start and install its
@@ -552,7 +558,8 @@ def test_cancel_term_ignore(minimal_job, scheduler, local_only):
552558
command=os.path.join(fixtures.TEST_RESOURCES_CHECKS,
553559
'src', 'sleep_deeply.sh'),
554560
pre_run=[''],
555-
post_run=[''])
561+
post_run=[''],
562+
prepare_cmds=[''])
556563
minimal_job.submit()
557564

558565
# Stall a bit here to let the the spawned process start and install its

0 commit comments

Comments
 (0)