|
19 | 19 | # along with MDBenchmark. If not, see <http://www.gnu.org/licenses/>. |
20 | 20 | import click |
21 | 21 | import datreant.core as dtr |
22 | | -from jinja2.exceptions import TemplateNotFound |
23 | 22 |
|
24 | | -from . import console |
| 23 | +from . import console, mdengines, utils |
25 | 24 | from .cli import cli |
26 | | -from .mdengines import detect_md_engine, namd |
27 | | -from .utils import ENV, normalize_host, print_possible_hosts |
| 25 | + |
| 26 | + |
| 27 | +def validate_name(ctx, param, name=None): |
| 28 | + """Validate that we are given a name argument.""" |
| 29 | + if name is None: |
| 30 | + raise click.BadParameter( |
| 31 | + 'Please specify the base name of your input files.', |
| 32 | + param_hint='"-n" / "--name"') |
| 33 | + |
| 34 | + return name |
| 35 | + |
| 36 | + |
| 37 | +def validate_module(ctx, param, module=None): |
| 38 | + """Validate that we are given a module argument.""" |
| 39 | + if module is None or not module: |
| 40 | + raise click.BadParameter( |
| 41 | + 'Please specify which MD engine module to use for the benchmarks.', |
| 42 | + param_hint='"-m" / "--module"') |
| 43 | + return module |
| 44 | + |
| 45 | + |
| 46 | +def validate_number_of_nodes(min_nodes, max_nodes): |
| 47 | + """Validate that the minimal number of nodes is smaller than the maximal |
| 48 | + number. |
| 49 | + """ |
| 50 | + if min_nodes > max_nodes: |
| 51 | + raise click.BadParameter( |
| 52 | + 'The minimal number of nodes needs to be smaller than the maximal number.', |
| 53 | + param_hint='"--min-nodes"') |
| 54 | + |
| 55 | + |
| 56 | +def print_known_hosts(ctx, param, value): |
| 57 | + """Callback to print all available hosts to the user.""" |
| 58 | + if not value or ctx.resilient_parsing: |
| 59 | + return |
| 60 | + utils.print_possible_hosts() |
| 61 | + ctx.exit() |
| 62 | + |
| 63 | + |
| 64 | +def validate_hosts(ctx, param, host=None): |
| 65 | + """Callback to validate the hostname received as input. |
| 66 | +
|
| 67 | + If we were not given a hostname, we first try to guess it via |
| 68 | + `utils.guess_host`. If this fails, we give up and throw an error. |
| 69 | +
|
| 70 | + Otherwise we compare the provided/guessed host with the list of available |
| 71 | + templates. If the hostname matches the template name, we continue by |
| 72 | + returning the hostname. |
| 73 | + """ |
| 74 | + if host is None: |
| 75 | + host = utils.guess_host() |
| 76 | + if host is None: |
| 77 | + raise click.BadParameter( |
| 78 | + 'Could not guess host. Please provide a value explicitly.', |
| 79 | + param_hint='"--host"') |
| 80 | + |
| 81 | + known_hosts = utils.get_possible_hosts() |
| 82 | + if host not in known_hosts: |
| 83 | + console.info('Could not find template for host \'{}\'.', host) |
| 84 | + utils.print_possible_hosts() |
| 85 | + # TODO: Raise some appropriate error here |
| 86 | + ctx.exit() |
| 87 | + return |
| 88 | + |
| 89 | + return host |
28 | 90 |
|
29 | 91 |
|
30 | 92 | @cli.command() |
31 | 93 | @click.option( |
32 | 94 | '-n', |
33 | 95 | '--name', |
34 | 96 | help='Name of input files. All files must have the same base name.', |
35 | | - show_default=True) |
| 97 | + callback=validate_name) |
36 | 98 | @click.option( |
37 | 99 | '-g', |
38 | 100 | '--gpu', |
|
43 | 105 | '-m', |
44 | 106 | '--module', |
45 | 107 | help='Name of the MD engine module to use.', |
46 | | - multiple=True) |
47 | | -@click.option('--host', help='Name of the job template.', default=None) |
| 108 | + multiple=True, |
| 109 | + callback=validate_module) |
| 110 | +@click.option( |
| 111 | + '--host', |
| 112 | + help='Name of the job template.', |
| 113 | + default=None, |
| 114 | + callback=validate_hosts) |
48 | 115 | @click.option( |
49 | 116 | '--min-nodes', |
50 | 117 | help='Minimal number of nodes to request.', |
|
64 | 131 | show_default=True, |
65 | 132 | type=click.IntRange(1, 1440)) |
66 | 133 | @click.option( |
67 | | - '--list-hosts', help='Show available job templates.', is_flag=True) |
68 | | -def generate(name, gpu, module, host, min_nodes, max_nodes, time, list_hosts): |
69 | | - """Generate benchmarks.""" |
70 | | - if list_hosts: |
71 | | - print_possible_hosts() |
72 | | - return |
73 | | - |
74 | | - if not name: |
75 | | - raise click.BadParameter( |
76 | | - 'Please specify the base name of your input files.', |
77 | | - param_hint='"-n" / "--name"') |
78 | | - |
79 | | - if not module: |
80 | | - raise click.BadParameter( |
81 | | - 'Please specify which mdengine module to use for the benchmarks.', |
82 | | - param_hint='"-m" / "--module"') |
83 | | - |
84 | | - if min_nodes > max_nodes: |
85 | | - raise click.BadParameter( |
86 | | - 'The minimal number of nodes needs to be smaller than the maximal number.', |
87 | | - param_hint='"--min-nodes"') |
| 134 | + '--list-hosts', |
| 135 | + help='Show available job templates.', |
| 136 | + is_flag=True, |
| 137 | + is_eager=True, |
| 138 | + callback=print_known_hosts, |
| 139 | + expose_value=False) |
| 140 | +@click.option( |
| 141 | + '--skip-validation', |
| 142 | + help='Skip the validation of module names.', |
| 143 | + default=False, |
| 144 | + is_flag=True) |
| 145 | +def generate(name, gpu, module, host, min_nodes, max_nodes, time, |
| 146 | + skip_validation): |
| 147 | + """Generate benchmarks simulations from the CLI.""" |
| 148 | + # Validate the number of nodes |
| 149 | + validate_number_of_nodes(min_nodes=min_nodes, max_nodes=max_nodes) |
88 | 150 |
|
89 | | - host = normalize_host(host) |
90 | | - try: |
91 | | - tmpl = ENV.get_template(host) |
92 | | - except TemplateNotFound: |
93 | | - raise click.BadParameter( |
94 | | - 'Could not find template for host \'{}\'.'.format(host), |
95 | | - param_hint='"--host"') |
| 151 | + # Grab the template name for the host. This should always work because |
| 152 | + # click does the validation for us |
| 153 | + tmpl = utils.retrieve_host_template(host) |
96 | 154 |
|
97 | | - # Make sure we only warn the user once, if they are using NAMD. |
| 155 | + # Warn the user that NAMD support is still experimental. |
98 | 156 | if any(['namd' in m for m in module]): |
99 | 157 | console.warn( |
100 | 158 | 'NAMD support is experimental. ' |
101 | 159 | 'All input files must be in the current directory. ' |
102 | | - 'Parameter paths must be absolute. Only crude file checks are performed!' |
| 160 | + 'Parameter paths must be absolute. Only crude file checks are performed! ' |
103 | 161 | 'If you use the {} option make sure you use the GPU compatible NAMD module!', |
104 | 162 | '--gpu') |
105 | 163 |
|
| 164 | + module = mdengines.normalize_modules(module, skip_validation) |
| 165 | + |
| 166 | + # If several modules were given and we only cannot find one of them, we |
| 167 | + # continue. |
| 168 | + if not module: |
| 169 | + console.error('No requested modules available!') |
| 170 | + |
106 | 171 | for m in module: |
107 | | - # Here we detect the mdengine (GROMACS or NAMD). |
108 | | - engine = detect_md_engine(m) |
| 172 | + # Here we detect the MD engine (supported: GROMACS and NAMD). |
| 173 | + engine = mdengines.detect_md_engine(m) |
109 | 174 |
|
110 | 175 | directory = '{}_{}'.format(host, m) |
111 | 176 | gpu_string = '' |
|
0 commit comments