diff --git a/cscs-checks/system/slurm/slurm.py b/cscs-checks/system/slurm/slurm.py index e48e4eb09e..cda6879c9c 100644 --- a/cscs-checks/system/slurm/slurm.py +++ b/cscs-checks/system/slurm/slurm.py @@ -100,7 +100,8 @@ def __init__(self): self.sanity_patterns = sn.assert_found( r'error: You have to specify, at least, what sort of node you ' r'need: -C gpu for GPU enabled nodes, or -C mc for multicore ' - r'nodes.', self.stderr) + r'nodes.|ERROR: you must specify -C with one of the following: ' + r'mc,gpu,storage', self.stderr) @rfm.simple_test @@ -175,11 +176,8 @@ def __init__(self): @rfm.run_before('run') def set_slurm_constraint(self): cabinet = self.cabinets.get(self.current_partition.fullname) - constraint = f'--constraint={self.current_partition.name}' if cabinet: - constraint += f'&{cabinet}' - - self.job.options += [constraint] + self.job.options += [f'--constraint={cabinet}'] @rfm.simple_test diff --git a/docs/manpage.rst b/docs/manpage.rst index c83151f895..85e3621ec5 100644 --- a/docs/manpage.rst +++ b/docs/manpage.rst @@ -352,13 +352,16 @@ Options controlling job submission Any job options specified with this command-line option will be emitted after any job options specified in the :js:attr:`access` system partition configuration parameter. Especially for the Slurm backends, constraint options, such as ``-J constraint=value``, ``-J C=value``, ``-J --constraint=value`` or ``-J -C=value``, are going to be combined with any constraint options specified in the :js:attr:`access` system partition configuration parameter. - For example, if ``-C x`` is specified in the :js:attr:`access` and ``-J C=y`` is passed to the command-line, ReFrame will pass ``-C x,y`` as a constraint to the scheduler. + For example, if ``-C x`` is specified in the :js:attr:`access` and ``-J C=y`` is passed to the command-line, ReFrame will pass ``-C x&y`` as a constraint to the scheduler. Notice, however, that if constraint options are specified through multiple :option:`-J` options, only the last one will be considered. If you wish to completely overwrite any constraint options passed in :js:attr:`access`, you should consider passing explicitly the Slurm directive with ``-J '#SBATCH --constraint=new'``. .. versionchanged:: 3.0 This option has become more flexible. + .. versionchanged:: 3.1 + Use ``&`` to combine constraints. + ------------------------ Flexible node allocation ------------------------ diff --git a/reframe/core/schedulers/slurm.py b/reframe/core/schedulers/slurm.py index 2e6db6581a..b294d4e66b 100644 --- a/reframe/core/schedulers/slurm.py +++ b/reframe/core/schedulers/slurm.py @@ -214,7 +214,7 @@ def emit_preamble(self, job): if constraints: preamble.append( - self._format_option(','.join(constraints), '--constraint={0}') + self._format_option('&'.join(constraints), '--constraint={0}') ) preamble.append(self._format_option(hint, '--hint={0}')) @@ -326,7 +326,7 @@ def filternodes(self, job, nodes): 'available nodes now: %s' % (partitions, len(nodes))) if constraints: - constraints = set(constraints.strip().split(',')) + constraints = set(constraints.strip().split('&')) nodes = {n for n in nodes if n.active_features >= constraints} getlogger().debug( 'flex_alloc_nodes: filtering nodes by constraint(s) %s: ' diff --git a/unittests/test_schedulers.py b/unittests/test_schedulers.py index a52352bfd6..d6bd7f68e2 100644 --- a/unittests/test_schedulers.py +++ b/unittests/test_schedulers.py @@ -372,13 +372,13 @@ def test_no_empty_lines_in_preamble(minimal_job): def test_combined_access_constraint(make_job, slurm_only): job = make_job(sched_access=['--constraint=c1']) - job.options = ['-C c2,c3'] + job.options = ['-C c2&c3'] prepare_job(job) with open(job.script_filename) as fp: script_content = fp.read() - assert re.search(r'(?m)--constraint=c1,c2,c3$', script_content) - assert re.search(r'(?m)--constraint=(c1|c2,c3)$', script_content) is None + assert re.search(r'(?m)--constraint=c1&c2&c3$', script_content) + assert re.search(r'(?m)--constraint=(c1|c2&c3)$', script_content) is None def test_combined_access_multiple_constraints(make_job, slurm_only): @@ -388,7 +388,7 @@ def test_combined_access_multiple_constraints(make_job, slurm_only): with open(job.script_filename) as fp: script_content = fp.read() - assert re.search(r'(?m)--constraint=c1,c3$', script_content) + assert re.search(r'(?m)--constraint=c1&c3$', script_content) assert re.search(r'(?m)--constraint=(c1|c2|c3)$', script_content) is None @@ -781,7 +781,7 @@ def test_flex_alloc_valid_constraint_opt(make_flexible_job): def test_flex_alloc_valid_multiple_constraints(make_flexible_job): job = make_flexible_job('all') - job.options = ['-C f1,f3'] + job.options = ['-C f1&f3'] prepare_job(job) assert job.num_tasks == 4 @@ -808,7 +808,7 @@ def test_flex_alloc_valid_multiple_partitions(make_flexible_job): def test_flex_alloc_valid_constraint_partition(make_flexible_job): job = make_flexible_job('all') - job.options = ['-C f1,f2', '--partition=p1,p2'] + job.options = ['-C f1&f2', '--partition=p1,p2'] prepare_job(job) assert job.num_tasks == 4 @@ -867,7 +867,7 @@ def test_flex_alloc_exclude_nodes_opt(make_flexible_job): def test_flex_alloc_no_num_tasks_per_node(make_flexible_job): job = make_flexible_job('all') job.num_tasks_per_node = None - job.options = ['-C f1,f2', '--partition=p1,p2'] + job.options = ['-C f1&f2', '--partition=p1,p2'] prepare_job(job) assert job.num_tasks == 1 @@ -888,7 +888,7 @@ def test_flex_alloc_maintenance_nodes(make_flexible_job): def test_flex_alloc_not_enough_nodes_constraint_partition(make_flexible_job): job = make_flexible_job('all') - job.options = ['-C f1,f2', '--partition=p1,p2'] + job.options = ['-C f1&f2', '--partition=p1,p2'] job.num_tasks = -8 with pytest.raises(JobError): prepare_job(job) @@ -896,7 +896,7 @@ def test_flex_alloc_not_enough_nodes_constraint_partition(make_flexible_job): def test_flex_alloc_enough_nodes_constraint_partition(make_flexible_job): job = make_flexible_job('all') - job.options = ['-C f1,f2', '--partition=p1,p2'] + job.options = ['-C f1&f2', '--partition=p1,p2'] job.num_tasks = -4 prepare_job(job) assert job.num_tasks == 4