diff --git a/cylc/flow/exceptions.py b/cylc/flow/exceptions.py index 06c5bfcb15a..2a05dfb85ee 100644 --- a/cylc/flow/exceptions.py +++ b/cylc/flow/exceptions.py @@ -167,3 +167,8 @@ class CylcMissingContextPointError(CyclingError): class CylcMissingFinalCyclePointError(CyclingError): """An error denoting a missing (but required) final cycle point.""" + + +class PlatformLookupError(CylcConfigError): + """Unable to determine the correct job platform from the information + given""" diff --git a/cylc/flow/platform_lookup.py b/cylc/flow/platform_lookup.py new file mode 100644 index 00000000000..268904e03ce --- /dev/null +++ b/cylc/flow/platform_lookup.py @@ -0,0 +1,98 @@ +# THIS FILE IS PART OF THE CYLC SUITE ENGINE. +# Copyright (C) 2008-2019 NIWA & British Crown (Met Office) & Contributors. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Tests for the platform lookup. + +import re +from cylc.flow.exceptions import PlatformLookupError + + +def forward_lookup(platforms, job_platform): + """ + Find out which job platform to use given a list of possible platforms and + a task platform string. + + Verifies selected platform is present in global.rc file and returns it, + raises error if platfrom is not in global.rc or returns 'localhost' if + no platform is initally selected. + + Args: + job_platform (str): + platform item from config [runtime][TASK]platform + platforms (dictionary): + list of possible platforms defined by global.rc + + Returns: + platform (str): + string representing a platform from the global config. + + Example: + >>> platforms = { + ... 'suite server platform': None, + ... 'desktop[0-9][0-9]|laptop[0-9][0-9]': None, + ... 'sugar': { + ... 'login hosts': 'localhost', + ... 'batch system': 'slurm' + ... }, + ... 'hpc': { + ... 'login hosts': ['hpc1', 'hpc2'], + ... 'batch system': 'pbs' + ... }, + ... 'hpc1-bg': { + ... 'login hosts': 'hpc1', + ... 'batch system': 'background' + ... }, + ... 'hpc2-bg': { + ... 'login hosts': 'hpc2', + ... 'batch system': 'background' + ... } + ... } + >>> job_platform = 'desktop22' + >>> forward_lookup(platforms, job_platform) + 'desktop22' + """ + if job_platform is None: + return 'localhost' + for platform in reversed(list(platforms)): + if re.fullmatch(platform, job_platform): + return job_platform + + raise PlatformLookupError( + f"No matching platform \"{job_platform}\" found") + + +def reverse_lookup(task_job, task_remote, platforms): + """ + Find out which job platform to use given a list of possible platforms + and the task dictionary with cylc 7 definitions in it. + + Args: + task_job (dict): + Suite config [runtime][TASK][job] section + task_remote (dict): + Suite config [runtime][TASK][remote] section + platforms (dict): + Dictionary containing platfrom definitions. + + Returns: + platfrom (str): + string representing a platform from the global config. + + Examples: + Tim - write some doctests here... + + """ + raise NotImplementedError diff --git a/cylc/flow/tests/test_platform_lookup.py b/cylc/flow/tests/test_platform_lookup.py new file mode 100644 index 00000000000..064c3cceef7 --- /dev/null +++ b/cylc/flow/tests/test_platform_lookup.py @@ -0,0 +1,87 @@ +# THIS FILE IS PART OF THE CYLC SUITE ENGINE. +# Copyright (C) 2008-2019 NIWA & British Crown (Met Office) & Contributors. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Tests for the platform lookup. + +import pytest +from cylc.flow.platform_lookup import forward_lookup, reverse_lookup +from cylc.flow.exceptions import PlatformLookupError + +PLATFORMS_STANDARD = { + 'suite server platform': None, + 'desktop[0-9][0-9]|laptop[0-9][0-9]': None, + 'sugar': { + 'login hosts': 'localhost', + 'batch system': 'slurm' + }, + 'hpc': { + 'login hosts': ['hpc1', 'hpc2'], + 'batch system': 'pbs' + }, + 'hpc1-bg': { + 'login hosts': 'hpc1', + 'batch system': 'background' + }, + 'hpc2-bg': { + 'login hosts': 'hpc2', + 'batch system': 'background' + } +} + +PLATFORMS_NO_UNIQUE = { + 'sugar': { + 'login hosts': 'localhost', + 'batch system': 'slurm' + }, + 'pepper': { + 'login hosts': ['hpc1', 'hpc2'], + 'batch system': 'slurm' + }, + +} + +PLATFORMS_WITH_RE = { + 'hpc.*': {'login hosts': 'hpc1', 'batch system': 'background'}, + 'h.*': {'login hosts': 'hpc3'}, + r'vld\d{2,3}': None, + 'nu.*': {'batch system': 'slurm'} +} + + +@pytest.mark.parametrize( + "PLATFORMS, platform, expected", + [(PLATFORMS_WITH_RE, 'nutmeg', 'nutmeg'), + (PLATFORMS_WITH_RE, 'vld798', 'vld798'), + (PLATFORMS_WITH_RE, 'vld56', 'vld56'), + (PLATFORMS_NO_UNIQUE, 'sugar', 'sugar'), + (PLATFORMS_STANDARD, None, 'localhost'), + (PLATFORMS_STANDARD, 'laptop22', 'laptop22'), + (PLATFORMS_STANDARD, 'hpc1-bg', 'hpc1-bg'), + (PLATFORMS_WITH_RE, 'hpc2', 'hpc2') + ] +) +def test_basic(PLATFORMS, platform, expected): + assert forward_lookup(PLATFORMS, platform) == expected + + +def test_platform_not_there(): + with pytest.raises(PlatformLookupError): + forward_lookup(PLATFORMS_STANDARD, 'moooo') + + +def test_similar_but_not_exact_match(): + with pytest.raises(PlatformLookupError): + forward_lookup(PLATFORMS_WITH_RE, 'vld1')