|
30 | 30 | import reframe.utility.typecheck as typ |
31 | 31 |
|
32 | 32 |
|
33 | | -from reframe.frontend.printer import PrettyPrinter |
34 | | -from reframe.frontend.loader import RegressionCheckLoader |
| 33 | +from reframe.frontend.distribute import distribute_tests, getallnodes |
35 | 34 | from reframe.frontend.executors.policies import (SerialExecutionPolicy, |
36 | 35 | AsynchronousExecutionPolicy) |
37 | 36 | from reframe.frontend.executors import Runner, generate_testcases |
| 37 | +from reframe.frontend.loader import RegressionCheckLoader |
| 38 | +from reframe.frontend.printer import PrettyPrinter |
38 | 39 |
|
39 | 40 |
|
40 | 41 | def format_env(envvars): |
@@ -370,6 +371,12 @@ def main(): |
370 | 371 | '--disable-hook', action='append', metavar='NAME', dest='hooks', |
371 | 372 | default=[], help='Disable a pipeline hook for this run' |
372 | 373 | ) |
| 374 | + run_options.add_argument( |
| 375 | + '--distribute', action='store', metavar='{all|STATE}', |
| 376 | + nargs='?', const='idle', |
| 377 | + help=('Distribute the selected single-node jobs on every node that' |
| 378 | + 'is in STATE (default: "idle"') |
| 379 | + ) |
373 | 380 | run_options.add_argument( |
374 | 381 | '--exec-policy', metavar='POLICY', action='store', |
375 | 382 | choices=['async', 'serial'], default='async', |
@@ -933,6 +940,19 @@ def print_infoline(param, value): |
933 | 940 | print_infoline('output directory', repr(session_info['prefix_output'])) |
934 | 941 | printer.info('') |
935 | 942 | try: |
| 943 | + # Need to parse the cli options before loading the tests |
| 944 | + parsed_job_options = [] |
| 945 | + for opt in options.job_options: |
| 946 | + opt_split = opt.split('=', maxsplit=1) |
| 947 | + optstr = opt_split[0] |
| 948 | + valstr = opt_split[1] if len(opt_split) > 1 else '' |
| 949 | + if opt.startswith('-') or opt.startswith('#'): |
| 950 | + parsed_job_options.append(opt) |
| 951 | + elif len(optstr) == 1: |
| 952 | + parsed_job_options.append(f'-{optstr} {valstr}') |
| 953 | + else: |
| 954 | + parsed_job_options.append(f'--{optstr} {valstr}') |
| 955 | + |
936 | 956 | # Locate and load checks; `force=True` is not needed for normal |
937 | 957 | # invocations from the command line and has practically no effect, but |
938 | 958 | # it is needed to better emulate the behavior of running reframe's CLI |
@@ -1015,6 +1035,22 @@ def _case_failed(t): |
1015 | 1035 | f'{len(testcases)} remaining' |
1016 | 1036 | ) |
1017 | 1037 |
|
| 1038 | + if options.distribute: |
| 1039 | + node_map = getallnodes(options.distribute, parsed_job_options) |
| 1040 | + |
| 1041 | + # Remove the job options that begin with '--nodelist' and '-w', so |
| 1042 | + # that they do not override those set from the distribute feature. |
| 1043 | + # |
| 1044 | + # NOTE: This is Slurm-specific. When support of distributing tests |
| 1045 | + # is added to other scheduler backends, this needs to be updated, |
| 1046 | + # too. |
| 1047 | + parsed_job_options = [ |
| 1048 | + x for x in parsed_job_options |
| 1049 | + if (not x.startswith('-w') and not x.startswith('--nodelist')) |
| 1050 | + ] |
| 1051 | + testcases = distribute_tests(testcases, node_map) |
| 1052 | + testcases_all = testcases |
| 1053 | + |
1018 | 1054 | # Prepare for running |
1019 | 1055 | printer.debug('Building and validating the full test DAG') |
1020 | 1056 | testgraph, skipped_cases = dependencies.build_deps(testcases_all) |
@@ -1194,18 +1230,6 @@ def module_unuse(*paths): |
1194 | 1230 | sched_flex_alloc_nodes = options.flex_alloc_nodes |
1195 | 1231 |
|
1196 | 1232 | exec_policy.sched_flex_alloc_nodes = sched_flex_alloc_nodes |
1197 | | - parsed_job_options = [] |
1198 | | - for opt in options.job_options: |
1199 | | - opt_split = opt.split('=', maxsplit=1) |
1200 | | - optstr = opt_split[0] |
1201 | | - valstr = opt_split[1] if len(opt_split) > 1 else '' |
1202 | | - if opt.startswith('-') or opt.startswith('#'): |
1203 | | - parsed_job_options.append(opt) |
1204 | | - elif len(optstr) == 1: |
1205 | | - parsed_job_options.append(f'-{optstr} {valstr}') |
1206 | | - else: |
1207 | | - parsed_job_options.append(f'--{optstr} {valstr}') |
1208 | | - |
1209 | 1233 | exec_policy.sched_options = parsed_job_options |
1210 | 1234 | if options.maxfail < 0: |
1211 | 1235 | raise errors.ConfigError( |
@@ -1236,7 +1260,9 @@ def module_unuse(*paths): |
1236 | 1260 | success = True |
1237 | 1261 | if runner.stats.failed(): |
1238 | 1262 | success = False |
1239 | | - runner.stats.print_failure_report(printer) |
| 1263 | + runner.stats.print_failure_report( |
| 1264 | + printer, not options.distribute |
| 1265 | + ) |
1240 | 1266 | if options.failure_stats: |
1241 | 1267 | runner.stats.print_failure_stats(printer) |
1242 | 1268 |
|
|
0 commit comments