From 4a4587188fd7edc1a99a36b995ef65c95bebba35 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Tue, 2 Jul 2019 10:32:14 +0200 Subject: [PATCH 01/27] add container support to the pipeline --- reframe/core/containers.py | 4 ++-- reframe/core/pipeline.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 4db50b6dd9..111f9764f2 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -26,7 +26,7 @@ def __init__(self): self.requires_mpi = False self.commands = [] self.mount_points = [] - self.workdir = None + self.workdir = '/rfm_workdir' @abc.abstractmethod def emit_prepare_cmds(self): @@ -79,7 +79,7 @@ def emit_prepare_cmds(self): def emit_launch_cmds(self): super().emit_launch_cmds() docker_opts = ['-v "%s":"%s"' % mp for mp in self.mount_points] - run_cmd = 'docker run %s %s bash -c ' % (' '.join(docker_opts), + run_cmd = 'docker run --rm %s %s bash -c ' % (' '.join(docker_opts), self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 6e1faa9c8e..87d9de34ce 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -21,6 +21,7 @@ import reframe.utility.os_ext as os_ext import reframe.utility.typecheck as typ from reframe.core.buildsystems import BuildSystem, BuildSystemField +from reframe.core.containers import ContainerPlatform, ContainerPlatformField from reframe.core.deferrable import deferrable, _DeferredExpression, evaluate from reframe.core.environments import Environment, EnvironmentSnapshot from reframe.core.exceptions import (BuildError, DependencyError, @@ -160,6 +161,14 @@ class RegressionTest: #: .. versionadded:: 2.14 build_system = BuildSystemField('build_system', type(None)) + #: The container platform to be used for this test. + #: + #: :type: :class:`str` or :class:`reframe.core.containers.ContainerPlatform`. + #: :default: :class:`None`. + #: + #: .. versionadded:: 2.19 + container_platform = ContainerPlatformField('container_platform', type(None)) + #: List of shell commands to be executed before compiling. #: #: These commands are executed during the compilation phase and from @@ -632,6 +641,9 @@ def __init__(self, name=None, prefix=None): self._compile_proc = None self.build_system = None + # Container platform + self.container_platform = None + # Performance logging self._perf_logger = logging.null_logger @@ -1050,11 +1062,14 @@ def run(self): if not self.current_system or not self._current_partition: raise PipelineError('no system or system partition is set') - exec_cmd = [self.job.launcher.run_command(self.job), - self.executable, *self.executable_opts] - commands = [*self.pre_run, ' '.join(exec_cmd), *self.post_run] + if not self.exec_cmd: + self.exec_cmd = [self.job.launcher.run_command(self.job), + self.executable, *self.executable_opts] + + commands = [*self.pre_run, ' '.join(self.exec_cmd), *self.post_run] environs = [self._current_partition.local_env, self._current_environ, self._user_environ] + with os_ext.change_dir(self._stagedir): try: self._job.prepare(commands, environs, login=True) @@ -1270,6 +1285,15 @@ def run(self): self._copy_to_stagedir(os.path.join(self._prefix, self.sourcesdir)) + if self.container_platform: + self.container_platform.validate() + if self.container_platform.emit_prepare_cmds(): + self.pre_run += [self.container_platform.emit_prepare_cmds()] + + self.container_platform.mount_points = [ + (self._stagedir, self.container_platform.workdir)] + self.exec_cmd = [self.container_platform.emit_launch_cmds()] + super().run() From fbc5bc6404750e969e31eaecaceaffec1f1565f5 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Wed, 3 Jul 2019 14:11:58 +0200 Subject: [PATCH 02/27] fix style --- reframe/core/containers.py | 4 ++-- reframe/core/pipeline.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 111f9764f2..28b697435c 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -26,7 +26,7 @@ def __init__(self): self.requires_mpi = False self.commands = [] self.mount_points = [] - self.workdir = '/rfm_workdir' + self.workdir = '/rfm_workdir' @abc.abstractmethod def emit_prepare_cmds(self): @@ -80,7 +80,7 @@ def emit_launch_cmds(self): super().emit_launch_cmds() docker_opts = ['-v "%s":"%s"' % mp for mp in self.mount_points] run_cmd = 'docker run --rm %s %s bash -c ' % (' '.join(docker_opts), - self.image) + self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 87d9de34ce..2b6a7800cd 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1064,7 +1064,7 @@ def run(self): if not self.exec_cmd: self.exec_cmd = [self.job.launcher.run_command(self.job), - self.executable, *self.executable_opts] + self.executable, *self.executable_opts] commands = [*self.pre_run, ' '.join(self.exec_cmd), *self.post_run] environs = [self._current_partition.local_env, From f1c4af7d37db1415aaab36df3c669080f9e52d4a Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Wed, 3 Jul 2019 15:07:41 +0200 Subject: [PATCH 03/27] fix bugs --- reframe/core/pipeline.py | 2 +- unittests/test_containers.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 2b6a7800cd..549c5b41a0 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1062,7 +1062,7 @@ def run(self): if not self.current_system or not self._current_partition: raise PipelineError('no system or system partition is set') - if not self.exec_cmd: + if not self.container_platform: self.exec_cmd = [self.job.launcher.run_command(self.job), self.executable, *self.executable_opts] diff --git a/unittests/test_containers.py b/unittests/test_containers.py index 96b5f2f346..8b0469ab29 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -59,10 +59,10 @@ def create_container_platform(self): @property def exp_cmd_mount_points(self): - return ('docker run -v "/path/one":"/one" -v "/path/two":"/two" ' + return ('docker run --rm -v "/path/one":"/one" -v "/path/two":"/two" ' "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property def exp_cmd_custom_registry(self): - return ('docker run -v "/path/one":"/one" registry/custom/name:tag ' + return ('docker run --rm -v "/path/one":"/one" registry/custom/name:tag ' "bash -c 'cd /stagedir; cmd'") From 1809cd814f20ae52c495937790f638d72af8e6af Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Thu, 4 Jul 2019 08:38:39 +0200 Subject: [PATCH 04/27] fix offline comments --- reframe/core/pipeline.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 549c5b41a0..b41702089a 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1062,11 +1062,11 @@ def run(self): if not self.current_system or not self._current_partition: raise PipelineError('no system or system partition is set') - if not self.container_platform: - self.exec_cmd = [self.job.launcher.run_command(self.job), - self.executable, *self.executable_opts] + # if not self.container_platform: + exec_cmd = [self.job.launcher.run_command(self.job), + self.executable, *self.executable_opts] - commands = [*self.pre_run, ' '.join(self.exec_cmd), *self.post_run] + commands = [*self.pre_run, ' '.join(exec_cmd), *self.post_run] environs = [self._current_partition.local_env, self._current_environ, self._user_environ] @@ -1287,12 +1287,13 @@ def run(self): if self.container_platform: self.container_platform.validate() + self.container_platform.mount_points = [ + (self._stagedir, self.container_platform.workdir)] + self.executable = self.container_platform.emit_launch_cmds() if self.container_platform.emit_prepare_cmds(): self.pre_run += [self.container_platform.emit_prepare_cmds()] - self.container_platform.mount_points = [ - (self._stagedir, self.container_platform.workdir)] - self.exec_cmd = [self.container_platform.emit_launch_cmds()] + self.executable_opts = [] super().run() From 7827509766279d254ef317e7fadf6f1231280707 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Thu, 4 Jul 2019 16:28:10 +0200 Subject: [PATCH 05/27] fix comments --- reframe/core/pipeline.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index b41702089a..906d1d05f8 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -161,14 +161,6 @@ class RegressionTest: #: .. versionadded:: 2.14 build_system = BuildSystemField('build_system', type(None)) - #: The container platform to be used for this test. - #: - #: :type: :class:`str` or :class:`reframe.core.containers.ContainerPlatform`. - #: :default: :class:`None`. - #: - #: .. versionadded:: 2.19 - container_platform = ContainerPlatformField('container_platform', type(None)) - #: List of shell commands to be executed before compiling. #: #: These commands are executed during the compilation phase and from @@ -640,8 +632,6 @@ def __init__(self, name=None, prefix=None): self._build_job = None self._compile_proc = None self.build_system = None - - # Container platform self.container_platform = None # Performance logging @@ -1062,14 +1052,11 @@ def run(self): if not self.current_system or not self._current_partition: raise PipelineError('no system or system partition is set') - # if not self.container_platform: exec_cmd = [self.job.launcher.run_command(self.job), self.executable, *self.executable_opts] - commands = [*self.pre_run, ' '.join(exec_cmd), *self.post_run] environs = [self._current_partition.local_env, self._current_environ, self._user_environ] - with os_ext.change_dir(self._stagedir): try: self._job.prepare(commands, environs, login=True) @@ -1260,6 +1247,17 @@ class RunOnlyRegressionTest(RegressionTest): module. """ + #: The container platform to be used for this test. + #: + #: If the `self.container_platform.name` is defined on the test, both + #: `self.executable` and `self.executable_opts` are ignored. + #: + #: :type: :class:`str` or :class:`reframe.core.containers.ContainerPlatform`. + #: :default: :class:`None`. + #: + #: .. versionadded:: 2.19 + container_platform = ContainerPlatformField('container_platform', type(None)) + def compile(self): """The compilation phase of the regression test pipeline. @@ -1288,12 +1286,14 @@ def run(self): if self.container_platform: self.container_platform.validate() self.container_platform.mount_points = [ - (self._stagedir, self.container_platform.workdir)] + (self._stagedir, self.container_platform.workdir) + ] + # We replace executable and executable_opts in the case of containers. self.executable = self.container_platform.emit_launch_cmds() - if self.container_platform.emit_prepare_cmds(): - self.pre_run += [self.container_platform.emit_prepare_cmds()] - self.executable_opts = [] + emit_prepare_cmds = self.container_platform.emit_prepare_cmds() + if emit_prepare_cmds: + self.pre_run += [emit_prepare_cmds] super().run() From f3fadeeb7876f5616a34b44a2718569030ad4284 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Fri, 5 Jul 2019 11:04:35 +0200 Subject: [PATCH 06/27] add container_platform to cscs config --- config/cscs.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/config/cscs.py b/config/cscs.py index 7da16e7cd0..295a3b5962 100644 --- a/config/cscs.py +++ b/config/cscs.py @@ -69,6 +69,11 @@ class ReframeSettings: }, 'compute': { 'scheduler': 'nativeslurm', + 'container_platforms': { + 'ShifterNG': { + 'modules': ['shifter-ng'] + } + }, 'environs': ['PrgEnv-cray', 'PrgEnv-gnu', 'PrgEnv-intel', 'PrgEnv-pgi'], 'descr': 'Intel Xeon Phi', @@ -95,6 +100,14 @@ class ReframeSettings: 'gpu': { 'scheduler': 'nativeslurm', + 'container_platforms': { + 'ShifterNG': { + 'modules': ['shifter-ng'] + }, + 'Singularity': { + 'modules': ['singularity'] + } + }, 'modules': ['daint-gpu'], 'access': ['--constraint=gpu'], 'environs': ['PrgEnv-cray', 'PrgEnv-gnu', @@ -108,6 +121,14 @@ class ReframeSettings: 'mc': { 'scheduler': 'nativeslurm', + 'container_platforms': { + 'ShifterNG': { + 'modules': ['shifter-ng'] + }, + 'Singularity': { + 'modules': ['singularity'] + } + }, 'modules': ['daint-mc'], 'access': ['--constraint=mc'], 'environs': ['PrgEnv-cray', 'PrgEnv-gnu', @@ -140,6 +161,14 @@ class ReframeSettings: 'gpu': { 'scheduler': 'nativeslurm', + 'container_platforms': { + 'ShifterNG': { + 'modules': ['shifter-ng'] + }, + 'Singularity': { + 'modules': ['singularity'] + } + }, 'modules': ['daint-gpu'], 'access': ['--constraint=gpu'], 'environs': ['PrgEnv-cray', 'PrgEnv-gnu', @@ -153,6 +182,14 @@ class ReframeSettings: 'mc': { 'scheduler': 'nativeslurm', + 'container_platforms': { + 'ShifterNG': { + 'modules': ['shifter-ng'] + }, + 'Singularity': { + 'modules': ['singularity'] + } + }, 'modules': ['daint-mc'], 'access': ['--constraint=mc'], 'environs': ['PrgEnv-cray', 'PrgEnv-gnu', From e2ceff88f374e6e95640948c9853dcd929653c65 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Fri, 5 Jul 2019 16:03:41 +0200 Subject: [PATCH 07/27] fix comments --- reframe/core/pipeline.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 5419f41148..5b833e6adf 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -633,7 +633,6 @@ def __init__(self, name=None, prefix=None): self._build_job = None self._compile_proc = None self.build_system = None - self.container_platform = None # Performance logging self._perf_logger = logging.null_logger @@ -1250,7 +1249,7 @@ class RunOnlyRegressionTest(RegressionTest): #: The container platform to be used for this test. #: - #: If the `self.container_platform.name` is defined on the test, both + #: If the `self.container_platform` is defined on the test, both #: `self.executable` and `self.executable_opts` are ignored. #: #: :type: :class:`str` or :class:`reframe.core.containers.ContainerPlatform`. @@ -1259,6 +1258,10 @@ class RunOnlyRegressionTest(RegressionTest): #: .. versionadded:: 2.19 container_platform = ContainerPlatformField('container_platform', type(None)) + def __init__(self, name=None, prefix=None): + self.container_platform = None + super().__init__(name, prefix) + def compile(self): """The compilation phase of the regression test pipeline. @@ -1289,12 +1292,13 @@ def run(self): self.container_platform.mount_points = [ (self._stagedir, self.container_platform.workdir) ] + # We replace executable and executable_opts in the case of containers. self.executable = self.container_platform.emit_launch_cmds() self.executable_opts = [] - emit_prepare_cmds = self.container_platform.emit_prepare_cmds() - if emit_prepare_cmds: - self.pre_run += [emit_prepare_cmds] + pre_run_container = self.container_platform.emit_prepare_cmds() + if pre_run_container: + self.pre_run += pre_run_container super().run() From f4f6d49aa74d28868737fc96d062c22922de14c2 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Mon, 8 Jul 2019 08:46:13 +0200 Subject: [PATCH 08/27] fix comments --- reframe/core/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 5b833e6adf..006f75f9be 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1259,8 +1259,8 @@ class RunOnlyRegressionTest(RegressionTest): container_platform = ContainerPlatformField('container_platform', type(None)) def __init__(self, name=None, prefix=None): - self.container_platform = None super().__init__(name, prefix) + self.container_platform = None def compile(self): """The compilation phase of the regression test pipeline. From f40496a9af704f6e14b9cef46e43e32b56107b18 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Mon, 29 Jul 2019 11:14:57 +0200 Subject: [PATCH 09/27] add config for container platform --- reframe/core/config.py | 25 ++++++++++++++++--------- reframe/core/pipeline.py | 11 +++++++++++ reframe/core/systems.py | 9 +++++++++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/reframe/core/config.py b/reframe/core/config.py index 491404a820..ae4ea81192 100644 --- a/reframe/core/config.py +++ b/reframe/core/config.py @@ -191,14 +191,21 @@ def create_env(system, partition, name): part_access = partconfig.get('access', []) part_resources = partconfig.get('resources', {}) part_max_jobs = partconfig.get('max_jobs', 1) - system.add_partition(SystemPartition(name=part_name, - descr=part_descr, - scheduler=part_scheduler, - launcher=part_launcher, - access=part_access, - environs=part_environs, - resources=part_resources, - local_env=part_local_env, - max_jobs=part_max_jobs)) + part = SystemPartition(name=part_name, + descr=part_descr, + scheduler=part_scheduler, + launcher=part_launcher, + access=part_access, + environs=part_environs, + resources=part_resources, + local_env=part_local_env, + max_jobs=part_max_jobs) + for cp, env_spec in partconfig.get('container_platforms', {}).items(): + cp_env = m_env.Environment(name='__rfm_env_%s' % cp, + modules=env_spec.get('modules', []), + variables=env_spec.get('variables', {})) + part.add_container_env(cp, cp_env) + + system.add_partition(part) self._systems[sys_name] = system diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 006f75f9be..b9e08dc725 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -540,6 +540,7 @@ class RegressionTest: _current_environ = fields.TypedField('_current_environ', Environment, type(None)) _user_environ = fields.TypedField('_user_environ', Environment, type(None)) + _extra_env = fields.TypedField('_extra_env', Environment, type(None)) _job = fields.TypedField('_job', Job, type(None)) _build_job = fields.TypedField('_build_job', Job, type(None)) @@ -618,6 +619,7 @@ def __init__(self, name=None, prefix=None): self._current_partition = None self._current_environ = None self._user_environ = None + self._extra_env = None # Associated job self._job = None @@ -1049,6 +1051,7 @@ def run(self): This call is non-blocking. It simply submits the job associated with this test and returns. """ + if not self.current_system or not self._current_partition: raise PipelineError('no system or system partition is set') @@ -1057,6 +1060,9 @@ def run(self): commands = [*self.pre_run, ' '.join(exec_cmd), *self.post_run] environs = [self._current_partition.local_env, self._current_environ, self._user_environ] + if self._extra_env: + environs.append(self._extra_env) + with os_ext.change_dir(self._stagedir): try: self._job.prepare(commands, environs, login=True) @@ -1288,6 +1294,11 @@ def run(self): self.sourcesdir)) if self.container_platform: + try: + self._extra_env = self._current_partition.container_environs[self.container_platform.__class__.__name__] + except KeyError as e: + self.logger.debug('no configuration found for container platform: %s' % e) + self.container_platform.validate() self.container_platform.mount_points = [ (self._stagedir, self.container_platform.workdir) diff --git a/reframe/core/systems.py b/reframe/core/systems.py index 193231f3c6..36ef9fb39e 100644 --- a/reframe/core/systems.py +++ b/reframe/core/systems.py @@ -19,6 +19,7 @@ class SystemPartition: _environs = fields.TypedField('_environs', typ.List[Environment]) _resources = fields.TypedField('_resources', typ.Dict[str, typ.List[str]]) _local_env = fields.TypedField('_local_env', Environment, type(None)) + _container_environs = fields.TypedField('_container_environs', typ.Dict[str, Environment]) # maximum concurrent jobs _max_jobs = fields.TypedField('_max_jobs', int) @@ -35,6 +36,7 @@ def __init__(self, name, descr=None, scheduler=None, launcher=None, self._resources = dict(resources) self._max_jobs = max_jobs self._local_env = local_env + self._container_environs = {} # Parent system self._system = None @@ -52,6 +54,10 @@ def descr(self): def environs(self): return utility.SequenceView(self._environs) + @property + def container_environs(self): + return utility.MappingView(self._container_environs) + @property def fullname(self): """Return the fully-qualified name of this partition. @@ -111,6 +117,9 @@ def launcher(self): """ return self._launcher + def add_container_env(self, env_name, environ): + self._container_environs[env_name] = environ + # Instantiate managed resource `name` with `value`. def get_resource(self, name, **values): ret = [] From 46a1cb6f489b01ea245778364c16d207b3b01acf Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Wed, 18 Sep 2019 13:51:38 +0200 Subject: [PATCH 10/27] moving logic to base class --- reframe/core/pipeline.py | 59 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index b9e08dc725..270216d86a 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -192,6 +192,17 @@ class RegressionTest: #: :default: ``[]`` executable_opts = fields.TypedField('executable_opts', typ.List[str]) + #: The container platform to be used for this test. + #: + #: If the `self.container_platform` is defined on the test, both + #: `self.executable` and `self.executable_opts` are ignored. + #: + #: :type: :class:`str` or :class:`reframe.core.containers.ContainerPlatform`. + #: :default: :class:`None`. + #: + #: .. versionadded:: 2.19 + container_platform = ContainerPlatformField('container_platform', type(None)) + #: List of shell commands to execute before launching this job. #: #: These commands do not execute in the context of ReFrame. @@ -578,6 +589,7 @@ def __init__(self, name=None, prefix=None): self.tags = set() self.maintainers = [] self._perfvalues = {} + self.container_platform = None # Strict performance check, if applicable self.strict_check = True @@ -1055,6 +1067,24 @@ def run(self): if not self.current_system or not self._current_partition: raise PipelineError('no system or system partition is set') + if self.container_platform: + try: + self._extra_env = self._current_partition.container_environs[self.container_platform.__class__.__name__] + except KeyError as e: + self.logger.debug('no configuration found for container platform: %s' % e) + + self.container_platform.validate() + self.container_platform.mount_points = [ + (self._stagedir, self.container_platform.workdir) + ] + + # We replace executable and executable_opts in the case of containers. + self.executable = self.container_platform.emit_launch_cmds() + self.executable_opts = [] + pre_run_container = self.container_platform.emit_prepare_cmds() + if pre_run_container: + self.pre_run += pre_run_container + exec_cmd = [self.job.launcher.run_command(self.job), self.executable, *self.executable_opts] commands = [*self.pre_run, ' '.join(exec_cmd), *self.post_run] @@ -1253,20 +1283,9 @@ class RunOnlyRegressionTest(RegressionTest): module. """ - #: The container platform to be used for this test. - #: - #: If the `self.container_platform` is defined on the test, both - #: `self.executable` and `self.executable_opts` are ignored. - #: - #: :type: :class:`str` or :class:`reframe.core.containers.ContainerPlatform`. - #: :default: :class:`None`. - #: - #: .. versionadded:: 2.19 - container_platform = ContainerPlatformField('container_platform', type(None)) def __init__(self, name=None, prefix=None): super().__init__(name, prefix) - self.container_platform = None def compile(self): """The compilation phase of the regression test pipeline. @@ -1293,24 +1312,6 @@ def run(self): self._copy_to_stagedir(os.path.join(self._prefix, self.sourcesdir)) - if self.container_platform: - try: - self._extra_env = self._current_partition.container_environs[self.container_platform.__class__.__name__] - except KeyError as e: - self.logger.debug('no configuration found for container platform: %s' % e) - - self.container_platform.validate() - self.container_platform.mount_points = [ - (self._stagedir, self.container_platform.workdir) - ] - - # We replace executable and executable_opts in the case of containers. - self.executable = self.container_platform.emit_launch_cmds() - self.executable_opts = [] - pre_run_container = self.container_platform.emit_prepare_cmds() - if pre_run_container: - self.pre_run += pre_run_container - super().run() From a0c19fdbcbefe6b3458dd8ee1f4cccf989557567 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Wed, 18 Sep 2019 17:07:03 +0200 Subject: [PATCH 11/27] add shifter and singularity --- reframe/core/containers.py | 61 ++++++++++++++++++++++++++++++++++-- reframe/core/pipeline.py | 16 +++++----- unittests/test_containers.py | 40 ++++++++++++++++++++++- 3 files changed, 106 insertions(+), 11 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 28b697435c..e23701cd20 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -78,8 +78,8 @@ def emit_prepare_cmds(self): def emit_launch_cmds(self): super().emit_launch_cmds() - docker_opts = ['-v "%s":"%s"' % mp for mp in self.mount_points] - run_cmd = 'docker run --rm %s %s bash -c ' % (' '.join(docker_opts), + run_opts = ['-v "%s":"%s"' % mp for mp in self.mount_points] + run_cmd = 'docker run --rm %s %s bash -c ' % (' '.join(run_opts), self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" @@ -88,6 +88,63 @@ def validate(self): super().validate() +class ShifterNG(ContainerPlatform): + """An implementation of ContainerPlatform to run containers with + ShifterNG.""" + def __init__(self): + self.with_mpi = False + super().__init__() + + def emit_prepare_cmds(self): + pass + + def emit_launch_cmds(self): + super().emit_launch_cmds() + run_opts = ['--mount=type=bind,source="%s",destination="%s"' % + mp for mp in self.mount_points] + if self.with_mpi: + mpi_opt = '--mpi ' + else: + mpi_opt = '' + + run_cmd = 'shifter run %s%s %s bash -c ' % (mpi_opt, + ' '.join(run_opts), + self.image) + return run_cmd + "'" + '; '.join( + ['cd ' + self.workdir] + self.commands) + "'" + + def validate(self): + super().validate() + + +class Singularity(ContainerPlatform): + """An implementation of ContainerPlatform to run containers with + Singularity.""" + def __init__(self): + self.with_cuda = False + super().__init__() + + def emit_prepare_cmds(self): + pass + + def emit_launch_cmds(self): + super().emit_launch_cmds() + exec_opts = ['-B"%s","%s"' % mp for mp in self.mount_points] + if self.with_cuda: + cuda_opt = '--nv ' + else: + cuda_opt = '' + + run_cmd = 'singularity exec %s%s %s bash -c ' % (cuda_opt, + ' '.join(exec_opts), + self.image) + return run_cmd + "'" + '; '.join( + ['cd ' + self.workdir] + self.commands) + "'" + + def validate(self): + super().validate() + + class ContainerPlatformField(fields.TypedField): """A field representing a container platforms. diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 270216d86a..f381012f29 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -551,7 +551,6 @@ class RegressionTest: _current_environ = fields.TypedField('_current_environ', Environment, type(None)) _user_environ = fields.TypedField('_user_environ', Environment, type(None)) - _extra_env = fields.TypedField('_extra_env', Environment, type(None)) _job = fields.TypedField('_job', Job, type(None)) _build_job = fields.TypedField('_build_job', Job, type(None)) @@ -631,7 +630,6 @@ def __init__(self, name=None, prefix=None): self._current_partition = None self._current_environ = None self._user_environ = None - self._extra_env = None # Associated job self._job = None @@ -1069,9 +1067,12 @@ def run(self): if self.container_platform: try: - self._extra_env = self._current_partition.container_environs[self.container_platform.__class__.__name__] + cp_name = self.container_platform.__class__.__name__ + cp_env = self._current_partition.container_environs[cp_name] except KeyError as e: - self.logger.debug('no configuration found for container platform: %s' % e) + cp_env = None + self.logger.debug('no configuration found for container ' + 'platform: %s' % e) self.container_platform.validate() self.container_platform.mount_points = [ @@ -1090,8 +1091,9 @@ def run(self): commands = [*self.pre_run, ' '.join(exec_cmd), *self.post_run] environs = [self._current_partition.local_env, self._current_environ, self._user_environ] - if self._extra_env: - environs.append(self._extra_env) + if cp_env: + environs = [self._current_partition.local_env, + self._current_environ, cp_env, self._user_environ] with os_ext.change_dir(self._stagedir): try: @@ -1282,8 +1284,6 @@ class RunOnlyRegressionTest(RegressionTest): This class is also directly available under the top-level :mod:`reframe` module. """ - - def __init__(self, name=None, prefix=None): super().__init__(name, prefix) diff --git a/unittests/test_containers.py b/unittests/test_containers.py index 8b0469ab29..738be04fc1 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -64,5 +64,43 @@ def exp_cmd_mount_points(self): @property def exp_cmd_custom_registry(self): - return ('docker run --rm -v "/path/one":"/one" registry/custom/name:tag ' + return ('docker run --rm -v "/path/one":"/one" ' + 'registry/custom/name:tag ' + "bash -c 'cd /stagedir; cmd'") + + +class TestShifterNG(_ContainerPlatformTest, unittest.TestCase): + def create_container_platform(self): + return containers.ShifterNG() + + @property + def exp_cmd_mount_points(self): + return ('shifter run ' + '--mount=type=bind,source="/path/one",destination="/one" ' + '--mount=type=bind,source="/path/two",destination="/two" ' + "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") + + @property + def exp_cmd_custom_registry(self): + self.container_platform.with_mpi = True + return ('shifter run --mpi ' + '--mount=type=bind,source="/path/one",destination="/one" ' + 'registry/custom/name:tag ' + "bash -c 'cd /stagedir; cmd'") + + +class TestSingularity(_ContainerPlatformTest, unittest.TestCase): + def create_container_platform(self): + return containers.Singularity() + + @property + def exp_cmd_mount_points(self): + return ('singularity exec -B"/path/one","/one" -B"/path/two","/two" ' + "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") + + @property + def exp_cmd_custom_registry(self): + self.container_platform.with_cuda = True + return ('singularity exec --nv -B"/path/one","/one" ' + 'registry/custom/name:tag ' "bash -c 'cd /stagedir; cmd'") From 4f895f4b4840cc0b637dfc349e4c38e2b76b71bc Mon Sep 17 00:00:00 2001 From: rafael Date: Thu, 19 Sep 2019 12:10:00 +0200 Subject: [PATCH 12/27] adding sarus --- config/cscs.py | 3 +++ reframe/core/containers.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/config/cscs.py b/config/cscs.py index 86ef464881..dbdc98b442 100644 --- a/config/cscs.py +++ b/config/cscs.py @@ -190,6 +190,9 @@ class ReframeSettings: }, 'Singularity': { 'modules': ['singularity'] + }, + 'Sarus': { + 'modules': ['sarus/cle7up01-test'] } }, 'modules': ['daint-mc'], diff --git a/reframe/core/containers.py b/reframe/core/containers.py index e23701cd20..4f1b06b6cc 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -145,6 +145,35 @@ def validate(self): super().validate() +class Sarus(ContainerPlatform): + """An implementation of ContainerPlatform to run containers with + Sarus.""" + def __init__(self): + self.with_mpi = False + super().__init__() + + def emit_prepare_cmds(self): + pass + + def emit_launch_cmds(self): + super().emit_launch_cmds() + run_opts = ['--mount=type=bind,source="%s",destination="%s"' % + mp for mp in self.mount_points] + if self.with_mpi: + mpi_opt = '--mpi ' + else: + mpi_opt = '' + + run_cmd = 'sarus run %s%s %s bash -c ' % (mpi_opt, + ' '.join(run_opts), + self.image) + return run_cmd + "'" + '; '.join( + ['cd ' + self.workdir] + self.commands) + "'" + + def validate(self): + super().validate() + + class ContainerPlatformField(fields.TypedField): """A field representing a container platforms. From ff80b7fa51df43a9af482b4def8515ea06ff1678 Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Thu, 19 Sep 2019 13:49:53 +0200 Subject: [PATCH 13/27] fix bug --- reframe/core/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index f381012f29..4d485f5dc9 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1091,7 +1091,7 @@ def run(self): commands = [*self.pre_run, ' '.join(exec_cmd), *self.post_run] environs = [self._current_partition.local_env, self._current_environ, self._user_environ] - if cp_env: + if self.container_platform and cp_env: environs = [self._current_partition.local_env, self._current_environ, cp_env, self._user_environ] From 257a32bd6cd025085ad3122738d670004292eeae Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Fri, 20 Sep 2019 10:48:30 +0200 Subject: [PATCH 14/27] bug fix --- reframe/core/pipeline.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 0e36640717..8b2d25c137 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1358,9 +1358,6 @@ class RunOnlyRegressionTest(RegressionTest): This class is also directly available under the top-level :mod:`reframe` module. """ - def __init__(self, name=None, prefix=None): - super().__init__(name, prefix) - def compile(self): """The compilation phase of the regression test pipeline. From 6c97bd20866df3300cc8dd71d6a659e68be3d68a Mon Sep 17 00:00:00 2001 From: Rafael Sarmiento Date: Thu, 26 Sep 2019 14:50:14 +0200 Subject: [PATCH 15/27] fix some of the comments --- config/cscs.py | 5 +- reframe/core/containers.py | 104 +++++++++++++++-------------------- reframe/core/pipeline.py | 16 +++--- unittests/test_containers.py | 50 ++++++++++++----- unittests/test_pipeline.py | 16 +++++- 5 files changed, 105 insertions(+), 86 deletions(-) diff --git a/config/cscs.py b/config/cscs.py index 5237736a2e..3ee08f29b2 100644 --- a/config/cscs.py +++ b/config/cscs.py @@ -168,6 +168,9 @@ class ReframeSettings: }, 'Singularity': { 'modules': ['singularity'] + }, + 'Sarus': { + 'modules': ['sarus/cle7up01-test'] } }, 'modules': ['daint-gpu'], @@ -334,7 +337,7 @@ class ReframeSettings: 'modules': [], 'access': [], 'environs': ['builtin-gcc'], - 'descr': 'Login nodes' + 'descr': 'Login nodes', } } } diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 4f1b06b6cc..484f11bc45 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -14,7 +14,24 @@ class ContainerPlatform(abc.ABC): registry = fields.TypedField('registry', str, type(None)) image = fields.TypedField('image', str, type(None)) + + #: Add to the launch command an option to enable MPI support. + #: + #: Some container platforms like ShifterNG require a command-line + #: argument to enable the MPI support. + #: + #: :type: boolean + #: :default: :class:`False` requires_mpi = fields.TypedField('requires_mpi', bool) + + #: Add to the launch command an option to enable CUDA support. + #: + #: Some container platforms like Singularity require a command-line + #: argument to enable CUDA support. + #: + #: :type: boolean + #: :default: :class:`False` + requires_cuda = fields.TypedField('requires_cuda', bool) commands = fields.TypedField('commands', typ.List[str]) mount_points = fields.TypedField('mount_points', typ.List[typ.Tuple[str, str]]) @@ -24,6 +41,7 @@ def __init__(self): self.registry = None self.image = None self.requires_mpi = False + self.requires_cuda = False self.commands = [] self.mount_points = [] self.workdir = '/rfm_workdir' @@ -53,7 +71,6 @@ def emit_launch_cmds(self): if self.registry: self.image = '/'.join([self.registry, self.image]) - @abc.abstractmethod def validate(self): """Validates this container platform. @@ -71,10 +88,10 @@ def validate(self): class Docker(ContainerPlatform): - """An implementation of ContainerPlatform to run containers with Docker.""" + """An implementation of ContainerPlatform for running containers with Docker.""" def emit_prepare_cmds(self): - pass + return [] def emit_launch_cmds(self): super().emit_launch_cmds() @@ -84,95 +101,62 @@ def emit_launch_cmds(self): return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" - def validate(self): - super().validate() - class ShifterNG(ContainerPlatform): - """An implementation of ContainerPlatform to run containers with + """An implementation of ContainerPlatform for running containers with ShifterNG.""" def __init__(self): - self.with_mpi = False + self.requires_mpi = False super().__init__() def emit_prepare_cmds(self): - pass + return [] def emit_launch_cmds(self): super().emit_launch_cmds() - run_opts = ['--mount=type=bind,source="%s",destination="%s"' % + self._run_opts = ['--mount=type=bind,source="%s",destination="%s"' % mp for mp in self.mount_points] - if self.with_mpi: - mpi_opt = '--mpi ' - else: - mpi_opt = '' - - run_cmd = 'shifter run %s%s %s bash -c ' % (mpi_opt, - ' '.join(run_opts), - self.image) + if self.requires_mpi: + self._run_opts.append('--mpi') + + run_cmd = 'shifter run %s %s bash -c ' % (' '.join(self._run_opts), + self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" - def validate(self): - super().validate() - - -class Singularity(ContainerPlatform): - """An implementation of ContainerPlatform to run containers with - Singularity.""" - def __init__(self): - self.with_cuda = False - super().__init__() - - def emit_prepare_cmds(self): - pass +class Sarus(ShifterNG): + """An implementation of ContainerPlatform for running containers with + Sarus.""" def emit_launch_cmds(self): super().emit_launch_cmds() - exec_opts = ['-B"%s","%s"' % mp for mp in self.mount_points] - if self.with_cuda: - cuda_opt = '--nv ' - else: - cuda_opt = '' - - run_cmd = 'singularity exec %s%s %s bash -c ' % (cuda_opt, - ' '.join(exec_opts), - self.image) + run_cmd = 'sarus run %s %s bash -c ' % (' '.join(self._run_opts), + self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" - def validate(self): - super().validate() - -class Sarus(ContainerPlatform): - """An implementation of ContainerPlatform to run containers with - Sarus.""" +class Singularity(ContainerPlatform): + """An implementation of ContainerPlatform for running containers with + Singularity.""" def __init__(self): - self.with_mpi = False + self.requires_cuda = False super().__init__() def emit_prepare_cmds(self): - pass + return [] def emit_launch_cmds(self): super().emit_launch_cmds() - run_opts = ['--mount=type=bind,source="%s",destination="%s"' % - mp for mp in self.mount_points] - if self.with_mpi: - mpi_opt = '--mpi ' - else: - mpi_opt = '' + exec_opts = ['-B"%s","%s"' % mp for mp in self.mount_points] + if self.requires_cuda: + exec_opts.append('--nv') - run_cmd = 'sarus run %s%s %s bash -c ' % (mpi_opt, - ' '.join(run_opts), - self.image) + run_cmd = 'singularity exec %s %s bash -c ' % (' '.join(exec_opts), + self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" - def validate(self): - super().validate() - class ContainerPlatformField(fields.TypedField): """A field representing a container platforms. diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 8b2d25c137..8bf584eac8 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1104,24 +1104,23 @@ def run(self): if self.container_platform: try: - cp_name = self.container_platform.__class__.__name__ + cp_name = type(self.container_platform).__name__ cp_env = self._current_partition.container_environs[cp_name] except KeyError as e: - cp_env = None - self.logger.debug('no configuration found for container ' - 'platform: %s' % e) + raise PipelineError('no configuration found for container ' + 'platform: %s' % e) from None self.container_platform.validate() - self.container_platform.mount_points = [ + self.container_platform.mount_points += [ (self._stagedir, self.container_platform.workdir) ] # We replace executable and executable_opts in the case of containers. self.executable = self.container_platform.emit_launch_cmds() self.executable_opts = [] - pre_run_container = self.container_platform.emit_prepare_cmds() - if pre_run_container: - self.pre_run += pre_run_container + prepare_container = self.container_platform.emit_prepare_cmds() + if prepare_container: + self.pre_run += prepare_container exec_cmd = [self.job.launcher.run_command(self.job), self.executable, *self.executable_opts] @@ -1358,6 +1357,7 @@ class RunOnlyRegressionTest(RegressionTest): This class is also directly available under the top-level :mod:`reframe` module. """ + def compile(self): """The compilation phase of the regression test pipeline. diff --git a/unittests/test_containers.py b/unittests/test_containers.py index 738be04fc1..b9efccde76 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -13,12 +13,12 @@ def create_container_platform(self): @property @abc.abstractmethod - def exp_cmd_mount_points(self): + def expected_cmd_mount_points(self): pass @property @abc.abstractmethod - def exp_cmd_custom_registry(self): + def expected_cmd_custom_registry(self): pass def setUp(self): @@ -30,7 +30,7 @@ def test_mount_points(self): ('/path/two', '/two')] self.container_platform.commands = ['cmd1', 'cmd2'] self.container_platform.workdir = '/stagedir' - assert (self.exp_cmd_mount_points == + assert (self.expected_cmd_mount_points == self.container_platform.emit_launch_cmds()) def test_missing_image(self): @@ -49,7 +49,7 @@ def test_custom_registry(self): self.container_platform.commands = ['cmd'] self.container_platform.mount_points = [('/path/one', '/one')] self.container_platform.workdir = '/stagedir' - assert (self.exp_cmd_custom_registry == + assert (self.expected_cmd_custom_registry == self.container_platform.emit_launch_cmds()) @@ -58,12 +58,12 @@ def create_container_platform(self): return containers.Docker() @property - def exp_cmd_mount_points(self): + def expected_cmd_mount_points(self): return ('docker run --rm -v "/path/one":"/one" -v "/path/two":"/two" ' "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property - def exp_cmd_custom_registry(self): + def expected_cmd_custom_registry(self): return ('docker run --rm -v "/path/one":"/one" ' 'registry/custom/name:tag ' "bash -c 'cd /stagedir; cmd'") @@ -74,18 +74,38 @@ def create_container_platform(self): return containers.ShifterNG() @property - def exp_cmd_mount_points(self): + def expected_cmd_mount_points(self): return ('shifter run ' '--mount=type=bind,source="/path/one",destination="/one" ' '--mount=type=bind,source="/path/two",destination="/two" ' "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property - def exp_cmd_custom_registry(self): - self.container_platform.with_mpi = True - return ('shifter run --mpi ' + def expected_cmd_custom_registry(self): + self.container_platform.requires_mpi = True + return ('shifter run ' '--mount=type=bind,source="/path/one",destination="/one" ' - 'registry/custom/name:tag ' + '--mpi registry/custom/name:tag ' + "bash -c 'cd /stagedir; cmd'") + + +class TestSarus(_ContainerPlatformTest, unittest.TestCase): + def create_container_platform(self): + return containers.Sarus() + + @property + def expected_cmd_mount_points(self): + return ('sarus run ' + '--mount=type=bind,source="/path/one",destination="/one" ' + '--mount=type=bind,source="/path/two",destination="/two" ' + "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") + + @property + def expected_cmd_custom_registry(self): + self.container_platform.requires_mpi = True + return ('sarus run ' + '--mount=type=bind,source="/path/one",destination="/one" ' + '--mpi registry/custom/name:tag ' "bash -c 'cd /stagedir; cmd'") @@ -94,13 +114,13 @@ def create_container_platform(self): return containers.Singularity() @property - def exp_cmd_mount_points(self): + def expected_cmd_mount_points(self): return ('singularity exec -B"/path/one","/one" -B"/path/two","/two" ' "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property - def exp_cmd_custom_registry(self): - self.container_platform.with_cuda = True - return ('singularity exec --nv -B"/path/one","/one" ' + def expected_cmd_custom_registry(self): + self.container_platform.requires_cuda = True + return ('singularity exec -B"/path/one","/one" --nv ' 'registry/custom/name:tag ' "bash -c 'cd /stagedir; cmd'") diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index ea5d93b008..5d9aaee928 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -104,6 +104,18 @@ def test_hellocheck(self): test.valid_prog_environs = [self.prgenv.name] self._run_test(test) + @fixtures.switch_to_user_runtime + def test_containers(self): + #self.setup_remote_execution() + print('xxx:', self.partition) #.container_environs.__dict__) + self.setup_local_execution() + test = self.loader.load_from_file( + 'unittests/resources/checks_unlisted/containers.py')[0] + + # Use test environment for the regression check + # test.valid_prog_environs = [self.prgenv.name] + self._run_test(test) + @fixtures.switch_to_user_runtime def test_hellocheck_make(self): self.setup_remote_execution() @@ -119,7 +131,7 @@ def test_hellocheck_local(self): 'unittests/resources/checks/hellocheck.py')[0] # Use test environment for the regression check - test.valid_prog_environs = [self.prgenv.name] + # test.valid_prog_environs = [self.prgenv.name] # Test also the prebuild/postbuild functionality test.prebuild_cmd = ['touch prebuild', 'mkdir prebuild_dir'] @@ -128,7 +140,7 @@ def test_hellocheck_local(self): 'prebuild_dir', 'postbuild_dir'] # Force local execution of the test - test.local = True + # test.local = True self._run_test(test) def test_hellocheck_local_prepost_run(self): From 845684db17868442c402bfbb0a325cd4f4c4f55c Mon Sep 17 00:00:00 2001 From: rafael Date: Thu, 26 Sep 2019 18:01:24 +0200 Subject: [PATCH 16/27] fix wrong mount syntax in singularity --- reframe/core/containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 484f11bc45..2f7f6b1056 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -148,7 +148,7 @@ def emit_prepare_cmds(self): def emit_launch_cmds(self): super().emit_launch_cmds() - exec_opts = ['-B"%s","%s"' % mp for mp in self.mount_points] + exec_opts = ['-B"%s:%s"' % mp for mp in self.mount_points] if self.requires_cuda: exec_opts.append('--nv') From 10c82ae8b87da7968915e6f652215536f888e068 Mon Sep 17 00:00:00 2001 From: rafael Date: Fri, 27 Sep 2019 17:15:11 +0200 Subject: [PATCH 17/27] fix comments --- config/cscs.py | 6 -- reframe/core/containers.py | 18 ++--- .../resources/checks_unlisted/containers.py | 16 ++++ unittests/test_containers.py | 57 ++++++++++----- unittests/test_pipeline.py | 73 ++++++++++++++++--- 5 files changed, 124 insertions(+), 46 deletions(-) create mode 100644 unittests/resources/checks_unlisted/containers.py diff --git a/config/cscs.py b/config/cscs.py index 3ee08f29b2..6499181098 100644 --- a/config/cscs.py +++ b/config/cscs.py @@ -163,9 +163,6 @@ class ReframeSettings: 'gpu': { 'scheduler': 'nativeslurm', 'container_platforms': { - 'ShifterNG': { - 'modules': ['shifter-ng'] - }, 'Singularity': { 'modules': ['singularity'] }, @@ -188,9 +185,6 @@ class ReframeSettings: 'mc': { 'scheduler': 'nativeslurm', 'container_platforms': { - 'ShifterNG': { - 'modules': ['shifter-ng'] - }, 'Singularity': { 'modules': ['singularity'] }, diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 2f7f6b1056..88ac72c434 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -12,13 +12,12 @@ class ContainerPlatform(abc.ABC): :func:`emit_prepare_cmds` and :func:`emit_launch_cmds` abstract functions. """ - registry = fields.TypedField('registry', str, type(None)) image = fields.TypedField('image', str, type(None)) #: Add to the launch command an option to enable MPI support. #: #: Some container platforms like ShifterNG require a command-line - #: argument to enable the MPI support. + #: argument to enable the MPI support. #: #: :type: boolean #: :default: :class:`False` @@ -27,7 +26,7 @@ class ContainerPlatform(abc.ABC): #: Add to the launch command an option to enable CUDA support. #: #: Some container platforms like Singularity require a command-line - #: argument to enable CUDA support. + #: argument to enable CUDA support. #: #: :type: boolean #: :default: :class:`False` @@ -38,7 +37,6 @@ class ContainerPlatform(abc.ABC): workdir = fields.TypedField('workdir', str, type(None)) def __init__(self): - self.registry = None self.image = None self.requires_mpi = False self.requires_cuda = False @@ -68,9 +66,6 @@ def emit_launch_cmds(self): This method is relevant only to developers of new container platforms. """ - if self.registry: - self.image = '/'.join([self.registry, self.image]) - def validate(self): """Validates this container platform. @@ -110,12 +105,12 @@ def __init__(self): super().__init__() def emit_prepare_cmds(self): - return [] + return ['shifter pull %s' % self.image] def emit_launch_cmds(self): super().emit_launch_cmds() self._run_opts = ['--mount=type=bind,source="%s",destination="%s"' % - mp for mp in self.mount_points] + mp for mp in self.mount_points] if self.requires_mpi: self._run_opts.append('--mpi') @@ -128,6 +123,9 @@ def emit_launch_cmds(self): class Sarus(ShifterNG): """An implementation of ContainerPlatform for running containers with Sarus.""" + def emit_prepare_cmds(self): + return ['sarus pull %s' % self.image] + def emit_launch_cmds(self): super().emit_launch_cmds() run_cmd = 'sarus run %s %s bash -c ' % (' '.join(self._run_opts), @@ -144,7 +142,7 @@ def __init__(self): super().__init__() def emit_prepare_cmds(self): - return [] + return [] def emit_launch_cmds(self): super().emit_launch_cmds() diff --git a/unittests/resources/checks_unlisted/containers.py b/unittests/resources/checks_unlisted/containers.py new file mode 100644 index 0000000000..365c2ff6f6 --- /dev/null +++ b/unittests/resources/checks_unlisted/containers.py @@ -0,0 +1,16 @@ +import reframe as rfm +import reframe.utility.sanity as sn + + +@rfm.simple_test +class ContainerCheck(rfm.RunOnlyRegressionTest): + def __init__(self): + self.descr = 'Run commands inside a container' + self.valid_systems = ['dom:gpu', 'daint:gpu'] + self.valid_prog_environs = ['*'] + self.sanity_patterns = sn.all([ + #sn.assert_found(r'^' + self.container_platform.workdir, + # self.stdout), + sn.assert_found(r'^container_test.txt', self.stdout), + sn.assert_found(r'18.04.3 LTS \(Bionic Beaver\)', self.stdout), + ]) diff --git a/unittests/test_containers.py b/unittests/test_containers.py index b9efccde76..55fde38295 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -18,7 +18,12 @@ def expected_cmd_mount_points(self): @property @abc.abstractmethod - def expected_cmd_custom_registry(self): + def expected_cmd_prepare(self): + pass + + @property + @abc.abstractmethod + def expected_cmd_with_run_opts(self): pass def setUp(self): @@ -43,13 +48,17 @@ def test_missing_commands(self): with pytest.raises(ContainerError): self.container_platform.validate() - def test_custom_registry(self): - self.container_platform.registry = 'registry/custom' + def test_prepare_command(self): + self.container_platform.image = 'name:tag' + assert (self.expected_cmd_prepare == + self.container_platform.emit_prepare_cmds()) + + def test_run_opts(self): self.container_platform.image = 'name:tag' self.container_platform.commands = ['cmd'] self.container_platform.mount_points = [('/path/one', '/one')] self.container_platform.workdir = '/stagedir' - assert (self.expected_cmd_custom_registry == + assert (self.expected_cmd_with_run_opts == self.container_platform.emit_launch_cmds()) @@ -63,10 +72,13 @@ def expected_cmd_mount_points(self): "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property - def expected_cmd_custom_registry(self): + def expected_cmd_prepare(self): + return [] + + @property + def expected_cmd_with_run_opts(self): return ('docker run --rm -v "/path/one":"/one" ' - 'registry/custom/name:tag ' - "bash -c 'cd /stagedir; cmd'") + "name:tag bash -c 'cd /stagedir; cmd'") class TestShifterNG(_ContainerPlatformTest, unittest.TestCase): @@ -81,12 +93,15 @@ def expected_cmd_mount_points(self): "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property - def expected_cmd_custom_registry(self): + def expected_cmd_prepare(self): + return ['shifter pull name:tag'] + + @property + def expected_cmd_with_run_opts(self): self.container_platform.requires_mpi = True return ('shifter run ' '--mount=type=bind,source="/path/one",destination="/one" ' - '--mpi registry/custom/name:tag ' - "bash -c 'cd /stagedir; cmd'") + "--mpi name:tag bash -c 'cd /stagedir; cmd'") class TestSarus(_ContainerPlatformTest, unittest.TestCase): @@ -101,12 +116,15 @@ def expected_cmd_mount_points(self): "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property - def expected_cmd_custom_registry(self): + def expected_cmd_prepare(self): + return ['sarus pull name:tag'] + + @property + def expected_cmd_with_run_opts(self): self.container_platform.requires_mpi = True return ('sarus run ' '--mount=type=bind,source="/path/one",destination="/one" ' - '--mpi registry/custom/name:tag ' - "bash -c 'cd /stagedir; cmd'") + "--mpi name:tag bash -c 'cd /stagedir; cmd'") class TestSingularity(_ContainerPlatformTest, unittest.TestCase): @@ -115,12 +133,15 @@ def create_container_platform(self): @property def expected_cmd_mount_points(self): - return ('singularity exec -B"/path/one","/one" -B"/path/two","/two" ' + return ('singularity exec -B"/path/one:/one" -B"/path/two:/two" ' "name:tag bash -c 'cd /stagedir; cmd1; cmd2'") @property - def expected_cmd_custom_registry(self): + def expected_cmd_prepare(self): + return [] + + @property + def expected_cmd_with_run_opts(self): self.container_platform.requires_cuda = True - return ('singularity exec -B"/path/one","/one" --nv ' - 'registry/custom/name:tag ' - "bash -c 'cd /stagedir; cmd'") + return ('singularity exec -B"/path/one:/one" --nv ' + "name:tag bash -c 'cd /stagedir; cmd'") diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index 5d9aaee928..73475808b6 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -104,18 +104,6 @@ def test_hellocheck(self): test.valid_prog_environs = [self.prgenv.name] self._run_test(test) - @fixtures.switch_to_user_runtime - def test_containers(self): - #self.setup_remote_execution() - print('xxx:', self.partition) #.container_environs.__dict__) - self.setup_local_execution() - test = self.loader.load_from_file( - 'unittests/resources/checks_unlisted/containers.py')[0] - - # Use test environment for the regression check - # test.valid_prog_environs = [self.prgenv.name] - self._run_test(test) - @fixtures.switch_to_user_runtime def test_hellocheck_make(self): self.setup_remote_execution() @@ -803,3 +791,64 @@ def extract_perf(patt, tag): self.assertIn('v1', log_output) self.assertIn('v2', log_output) self.assertIn('v3', log_output) + + +class _TestContainersBase(TestRegressionTest): + def setup_test(self, platform): + self.setup_remote_execution() + test = self.loader.load_from_file( + 'unittests/resources/checks_unlisted/containers.py')[0] + test.valid_prog_environs = [self.prgenv.name] + test.container_platform = platform + test.container_platform.commands = [ + 'pwd', 'ls', 'cat /etc/os-release'] + test.container_platform.workdir = '/workdir' + return test + + def run_test(self, test): + try: + self._run_test(test) + except PipelineError: + self.skipTest('no configuration found for container platform: %s' % + type(test.container_platform).__name__) + + +class TestContainerPlatformsTest(TestRegressionTest): + def setup_test(self, platform, image): + self.setup_remote_execution() + test = self.loader.load_from_file( + 'unittests/resources/checks_unlisted/containers.py')[0] + test.valid_prog_environs = [self.prgenv.name] + test.container_platform = platform + test.container_platform.image = image + test.container_platform.commands = [ + 'pwd', 'ls', 'cat /etc/os-release'] + test.container_platform.workdir = '/workdir' + return test + + def run_test(self, test): + try: + self._run_test(test) + except PipelineError: + self.skipTest('no configuration found for container platform: %s' % + type(test.container_platform).__name__) + + @fixtures.switch_to_user_runtime + def test_singularity(self): + test = self.setup_test('Singularity', 'docker://ubuntu:18.04') + self.run_test(test) + + @fixtures.switch_to_user_runtime + def test_docker(self): + test = self.setup_test('Docker', 'ubuntu:18.04') + self.run_test(test) + + @fixtures.switch_to_user_runtime + def test_shifter(self): + test = self.setup_test('ShifterNG', 'ubuntu:18.04') + self.run_test(test) + + @fixtures.switch_to_user_runtime + def test_sarus(self): + test = self.setup_test('Sarus', 'ubuntu:18.04') + self.run_test(test) From dab07dba314b6bcde9f6f6e653fbf13b44133d80 Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 30 Sep 2019 17:37:04 +0200 Subject: [PATCH 18/27] fix comments --- reframe/core/containers.py | 4 +-- .../resources/checks_unlisted/containers.py | 8 ------ unittests/test_pipeline.py | 27 +++++-------------- 3 files changed, 9 insertions(+), 30 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 88ac72c434..cdd51e2499 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -14,7 +14,7 @@ class ContainerPlatform(abc.ABC): image = fields.TypedField('image', str, type(None)) - #: Add to the launch command an option to enable MPI support. + #: Add an option to the launch command to enable MPI support. #: #: Some container platforms like ShifterNG require a command-line #: argument to enable the MPI support. @@ -23,7 +23,7 @@ class ContainerPlatform(abc.ABC): #: :default: :class:`False` requires_mpi = fields.TypedField('requires_mpi', bool) - #: Add to the launch command an option to enable CUDA support. + #: Add an option to the launch command to enable CUDA support. #: #: Some container platforms like Singularity require a command-line #: argument to enable CUDA support. diff --git a/unittests/resources/checks_unlisted/containers.py b/unittests/resources/checks_unlisted/containers.py index 365c2ff6f6..dbcfbddff0 100644 --- a/unittests/resources/checks_unlisted/containers.py +++ b/unittests/resources/checks_unlisted/containers.py @@ -6,11 +6,3 @@ class ContainerCheck(rfm.RunOnlyRegressionTest): def __init__(self): self.descr = 'Run commands inside a container' - self.valid_systems = ['dom:gpu', 'daint:gpu'] - self.valid_prog_environs = ['*'] - self.sanity_patterns = sn.all([ - #sn.assert_found(r'^' + self.container_platform.workdir, - # self.stdout), - sn.assert_found(r'^container_test.txt', self.stdout), - sn.assert_found(r'18.04.3 LTS \(Bionic Beaver\)', self.stdout), - ]) diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index 73475808b6..3ec6cf777d 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -793,37 +793,24 @@ def extract_perf(patt, tag): self.assertIn('v3', log_output) -class _TestContainersBase(TestRegressionTest): - def setup_test(self, platform): - self.setup_remote_execution() - test = self.loader.load_from_file( - 'unittests/resources/checks_unlisted/containers.py')[0] - test.valid_prog_environs = [self.prgenv.name] - test.container_platform = platform - test.container_platform.commands = [ - 'pwd', 'ls', 'cat /etc/os-release'] - test.container_platform.workdir = '/workdir' - return test - - def run_test(self, test): - try: - self._run_test(test) - except PipelineError: - self.skipTest('no configuration found for container platform: %s' % - type(test.container_platform).__name__) - - class TestContainerPlatformsTest(TestRegressionTest): def setup_test(self, platform, image): self.setup_remote_execution() test = self.loader.load_from_file( 'unittests/resources/checks_unlisted/containers.py')[0] test.valid_prog_environs = [self.prgenv.name] + test.valid_systems = ['*'] test.container_platform = platform test.container_platform.image = image test.container_platform.commands = [ 'pwd', 'ls', 'cat /etc/os-release'] test.container_platform.workdir = '/workdir' + test.sanity_patterns = sn.all([ + sn.assert_found(r'^' + test.container_platform.workdir, + test.stdout), + sn.assert_found(r'^container_test.txt', test.stdout), + sn.assert_found(r'18.04.3 LTS \(Bionic Beaver\)', test.stdout), + ]) return test def run_test(self, test): From e776916b74897e7d607455d54952d025ef8ec49d Mon Sep 17 00:00:00 2001 From: rafael Date: Fri, 4 Oct 2019 17:53:47 +0200 Subject: [PATCH 19/27] fix comments --- reframe/core/containers.py | 60 +++++++++---------- .../resources/checks_unlisted/containers.py | 8 --- unittests/test_containers.py | 6 +- unittests/test_pipeline.py | 11 ++-- 4 files changed, 37 insertions(+), 48 deletions(-) delete mode 100644 unittests/resources/checks_unlisted/containers.py diff --git a/reframe/core/containers.py b/reframe/core/containers.py index cdd51e2499..bdc0423ce5 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -13,24 +13,6 @@ class ContainerPlatform(abc.ABC): """ image = fields.TypedField('image', str, type(None)) - - #: Add an option to the launch command to enable MPI support. - #: - #: Some container platforms like ShifterNG require a command-line - #: argument to enable the MPI support. - #: - #: :type: boolean - #: :default: :class:`False` - requires_mpi = fields.TypedField('requires_mpi', bool) - - #: Add an option to the launch command to enable CUDA support. - #: - #: Some container platforms like Singularity require a command-line - #: argument to enable CUDA support. - #: - #: :type: boolean - #: :default: :class:`False` - requires_cuda = fields.TypedField('requires_cuda', bool) commands = fields.TypedField('commands', typ.List[str]) mount_points = fields.TypedField('mount_points', typ.List[typ.Tuple[str, str]]) @@ -38,8 +20,8 @@ class ContainerPlatform(abc.ABC): def __init__(self): self.image = None - self.requires_mpi = False - self.requires_cuda = False + self.with_mpi = False + self.with_cuda = False self.commands = [] self.mount_points = [] self.workdir = '/rfm_workdir' @@ -83,7 +65,7 @@ def validate(self): class Docker(ContainerPlatform): - """An implementation of ContainerPlatform for running containers with Docker.""" + """An implementation of :class:`ContainerPlatform` for running containers with Docker.""" def emit_prepare_cmds(self): return [] @@ -98,10 +80,17 @@ def emit_launch_cmds(self): class ShifterNG(ContainerPlatform): - """An implementation of ContainerPlatform for running containers with + """An implementation of :class:`ContainerPlatform` for running containers with ShifterNG.""" + + #: Add an option to the launch command to enable MPI support. + #: + #: :type: boolean + #: :default: :class:`False` + with_mpi = fields.TypedField('with_mpi', bool) + def __init__(self): - self.requires_mpi = False + self.with_mpi = False super().__init__() def emit_prepare_cmds(self): @@ -109,36 +98,43 @@ def emit_prepare_cmds(self): def emit_launch_cmds(self): super().emit_launch_cmds() - self._run_opts = ['--mount=type=bind,source="%s",destination="%s"' % + self.run_opts = ['--mount=type=bind,source="%s",destination="%s"' % mp for mp in self.mount_points] - if self.requires_mpi: - self._run_opts.append('--mpi') + if self.with_mpi: + self.run_opts.append('--mpi') - run_cmd = 'shifter run %s %s bash -c ' % (' '.join(self._run_opts), + run_cmd = 'shifter run %s %s bash -c ' % (' '.join(self.run_opts), self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" class Sarus(ShifterNG): - """An implementation of ContainerPlatform for running containers with + """An implementation of :class:`ContainerPlatform` for running containers with Sarus.""" def emit_prepare_cmds(self): return ['sarus pull %s' % self.image] def emit_launch_cmds(self): super().emit_launch_cmds() - run_cmd = 'sarus run %s %s bash -c ' % (' '.join(self._run_opts), + run_cmd = 'sarus run %s %s bash -c ' % (' '.join(self.run_opts), self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" class Singularity(ContainerPlatform): - """An implementation of ContainerPlatform for running containers with + """An implementation of :class:`ContainerPlatform` for running containers with Singularity.""" + + #: Add an option to the launch command to enable CUDA support. + #: + #: :type: boolean + #: :default: :class:`False` + with_cuda = fields.TypedField('with_cuda', bool) + def __init__(self): - self.requires_cuda = False + self.with_cuda = False super().__init__() def emit_prepare_cmds(self): @@ -147,7 +143,7 @@ def emit_prepare_cmds(self): def emit_launch_cmds(self): super().emit_launch_cmds() exec_opts = ['-B"%s:%s"' % mp for mp in self.mount_points] - if self.requires_cuda: + if self.with_cuda: exec_opts.append('--nv') run_cmd = 'singularity exec %s %s bash -c ' % (' '.join(exec_opts), diff --git a/unittests/resources/checks_unlisted/containers.py b/unittests/resources/checks_unlisted/containers.py deleted file mode 100644 index dbcfbddff0..0000000000 --- a/unittests/resources/checks_unlisted/containers.py +++ /dev/null @@ -1,8 +0,0 @@ -import reframe as rfm -import reframe.utility.sanity as sn - - -@rfm.simple_test -class ContainerCheck(rfm.RunOnlyRegressionTest): - def __init__(self): - self.descr = 'Run commands inside a container' diff --git a/unittests/test_containers.py b/unittests/test_containers.py index 55fde38295..4a86431f15 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -98,7 +98,7 @@ def expected_cmd_prepare(self): @property def expected_cmd_with_run_opts(self): - self.container_platform.requires_mpi = True + self.container_platform.with_mpi = True return ('shifter run ' '--mount=type=bind,source="/path/one",destination="/one" ' "--mpi name:tag bash -c 'cd /stagedir; cmd'") @@ -121,7 +121,7 @@ def expected_cmd_prepare(self): @property def expected_cmd_with_run_opts(self): - self.container_platform.requires_mpi = True + self.container_platform.with_mpi = True return ('sarus run ' '--mount=type=bind,source="/path/one",destination="/one" ' "--mpi name:tag bash -c 'cd /stagedir; cmd'") @@ -142,6 +142,6 @@ def expected_cmd_prepare(self): @property def expected_cmd_with_run_opts(self): - self.container_platform.requires_cuda = True + self.container_platform.with_cuda = True return ('singularity exec -B"/path/one:/one" --nv ' "name:tag bash -c 'cd /stagedir; cmd'") diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index 1a483727d7..9458a59e65 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -119,7 +119,7 @@ def test_hellocheck_local(self): 'unittests/resources/checks/hellocheck.py')[0] # Use test environment for the regression check - # test.valid_prog_environs = [self.prgenv.name] + test.valid_prog_environs = [self.prgenv.name] # Test also the prebuild/postbuild functionality test.prebuild_cmd = ['touch prebuild', 'mkdir prebuild_dir'] @@ -128,7 +128,7 @@ def test_hellocheck_local(self): 'prebuild_dir', 'postbuild_dir'] # Force local execution of the test - # test.local = True + test.local = True self._run_test(test) def test_hellocheck_local_prepost_run(self): @@ -796,8 +796,9 @@ def extract_perf(patt, tag): class TestContainerPlatformsTest(TestRegressionTest): def setup_test(self, platform, image): self.setup_remote_execution() - test = self.loader.load_from_file( - 'unittests/resources/checks_unlisted/containers.py')[0] + test = rfm.RunOnlyRegressionTest() + test.name = 'Test%s' % platform + test._prefix = 'unittests/resources/checks' test.valid_prog_environs = [self.prgenv.name] test.valid_systems = ['*'] test.container_platform = platform @@ -808,7 +809,7 @@ def setup_test(self, platform, image): test.sanity_patterns = sn.all([ sn.assert_found(r'^' + test.container_platform.workdir, test.stdout), - sn.assert_found(r'^container_test.txt', test.stdout), + sn.assert_found(r'^hello.c', test.stdout), sn.assert_found(r'18.04.3 LTS \(Bionic Beaver\)', test.stdout), ]) return test From c2f1c63acf298fe2090199fa7dc8a3173457ee6e Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Thu, 10 Oct 2019 14:40:10 +0200 Subject: [PATCH 20/27] Fix PEP8 issues --- reframe/core/config.py | 12 ++++++++---- reframe/core/containers.py | 4 ++-- reframe/core/systems.py | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/reframe/core/config.py b/reframe/core/config.py index 375cd7c659..f4344ab47b 100644 --- a/reframe/core/config.py +++ b/reframe/core/config.py @@ -208,10 +208,14 @@ def create_env(system, partition, name): resources=part_resources, local_env=part_local_env, max_jobs=part_max_jobs) - for cp, env_spec in partconfig.get('container_platforms', {}).items(): - cp_env = m_env.Environment(name='__rfm_env_%s' % cp, - modules=env_spec.get('modules', []), - variables=env_spec.get('variables', {})) + + container_platforms = partconfig.get('container_platforms', {}) + for cp, env_spec in container_platforms.items(): + cp_env = m_env.Environment( + name='__rfm_env_%s' % cp, + modules=env_spec.get('modules', []), + variables=env_spec.get('variables', {}) + ) part.add_container_env(cp, cp_env) system.add_partition(part) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index d07a8b33ce..349b903748 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -112,8 +112,8 @@ def emit_launch_cmds(self): class Sarus(ShifterNG): - '''An implementation of :class:`ContainerPlatform` for running containers with - Sarus.''' + '''An implementation of :class:`ContainerPlatform` for running containers + with Sarus.''' def emit_prepare_cmds(self): return ['sarus pull %s' % self.image] diff --git a/reframe/core/systems.py b/reframe/core/systems.py index 341766f469..77a443631c 100644 --- a/reframe/core/systems.py +++ b/reframe/core/systems.py @@ -19,7 +19,8 @@ class SystemPartition: _environs = fields.TypedField('_environs', typ.List[Environment]) _resources = fields.TypedField('_resources', typ.Dict[str, typ.List[str]]) _local_env = fields.TypedField('_local_env', Environment, type(None)) - _container_environs = fields.TypedField('_container_environs', typ.Dict[str, Environment]) + _container_environs = fields.TypedField('_container_environs', + typ.Dict[str, Environment]) # maximum concurrent jobs _max_jobs = fields.TypedField('_max_jobs', int) From 0f5a95b5e6dbbece22e0a4e4e33d16286c62be33 Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Fri, 11 Oct 2019 01:08:14 +0200 Subject: [PATCH 21/27] Rename some functions of the container platforms interface Plus some other minor fixes. --- reframe/core/containers.py | 53 ++++++++++++++++-------------------- reframe/core/pipeline.py | 7 +++-- unittests/test_containers.py | 6 ++-- 3 files changed, 31 insertions(+), 35 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 349b903748..01fae17df6 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -9,7 +9,7 @@ class ContainerPlatform(abc.ABC): '''The abstract base class of any container platform. Concrete container platforms inherit from this class and must override the - :func:`emit_prepare_cmds` and :func:`emit_launch_cmds` abstract functions. + :func:`emit_prepare_commands()` and :func:`launch_command` abstract functions. ''' image = fields.TypedField('image', str, type(None)) @@ -27,7 +27,7 @@ def __init__(self): self.workdir = '/rfm_workdir' @abc.abstractmethod - def emit_prepare_cmds(self): + def emit_prepare_commands(self): '''Returns commands that are necessary before running with this container platform. @@ -39,7 +39,7 @@ def emit_prepare_cmds(self): ''' @abc.abstractmethod - def emit_launch_cmds(self): + def launch_command(self): '''Returns the command for running with this container platform. :raises: `ContainerError` in case of errors. @@ -69,11 +69,11 @@ class Docker(ContainerPlatform): '''An implementation of :class:`ContainerPlatform` for running containers with Docker.''' - def emit_prepare_cmds(self): + def emit_prepare_commands(self): return [] - def emit_launch_cmds(self): - super().emit_launch_cmds() + def launch_command(self): + super().launch_command() run_opts = ['-v "%s":"%s"' % mp for mp in self.mount_points] run_cmd = 'docker run --rm %s %s bash -c ' % (' '.join(run_opts), self.image) @@ -92,21 +92,22 @@ class ShifterNG(ContainerPlatform): with_mpi = fields.TypedField('with_mpi', bool) def __init__(self): - self.with_mpi = False super().__init__() + self.with_mpi = False + self._command = 'shifter' - def emit_prepare_cmds(self): - return ['shifter pull %s' % self.image] + def emit_prepare_commands(self): + return [self._command + ' pull %s' % self.image] - def emit_launch_cmds(self): - super().emit_launch_cmds() - self.run_opts = ['--mount=type=bind,source="%s",destination="%s"' % - mp for mp in self.mount_points] + def launch_command(self): + super().launch_command() + run_opts = ['--mount=type=bind,source="%s",destination="%s"' % + mp for mp in self.mount_points] if self.with_mpi: - self.run_opts.append('--mpi') + run_opts.append('--mpi') - run_cmd = 'shifter run %s %s bash -c ' % (' '.join(self.run_opts), - self.image) + run_cmd = self._command + ' run %s %s bash -c ' % (' '.join(run_opts), + self.image) return run_cmd + "'" + '; '.join( ['cd ' + self.workdir] + self.commands) + "'" @@ -115,15 +116,9 @@ class Sarus(ShifterNG): '''An implementation of :class:`ContainerPlatform` for running containers with Sarus.''' - def emit_prepare_cmds(self): - return ['sarus pull %s' % self.image] - - def emit_launch_cmds(self): - super().emit_launch_cmds() - run_cmd = 'sarus run %s %s bash -c ' % (' '.join(self.run_opts), - self.image) - return run_cmd + "'" + '; '.join( - ['cd ' + self.workdir] + self.commands) + "'" + def __init__(self): + super().__init__() + self._command = 'sarus' class Singularity(ContainerPlatform): @@ -137,14 +132,14 @@ class Singularity(ContainerPlatform): with_cuda = fields.TypedField('with_cuda', bool) def __init__(self): - self.with_cuda = False super().__init__() + self.with_cuda = False - def emit_prepare_cmds(self): + def emit_prepare_commands(self): return [] - def emit_launch_cmds(self): - super().emit_launch_cmds() + def launch_command(self): + super().launch_command() exec_opts = ['-B"%s:%s"' % mp for mp in self.mount_points] if self.with_cuda: exec_opts.append('--nv') diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index e691a13ccc..29fde05363 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -227,7 +227,8 @@ class RegressionTest(metaclass=RegressionTestMeta): #: If the `self.container_platform` is defined on the test, both #: `self.executable` and `self.executable_opts` are ignored. #: - #: :type: :class:`str` or :class:`reframe.core.containers.ContainerPlatform`. + #: :type: :class:`str` or + #: :class:`reframe.core.containers.ContainerPlatform`. #: :default: :class:`None`. #: #: .. versionadded:: 2.19 @@ -1130,9 +1131,9 @@ def run(self): ] # We replace executable and executable_opts in the case of containers. - self.executable = self.container_platform.emit_launch_cmds() + self.executable = self.container_platform.launch_command() self.executable_opts = [] - prepare_container = self.container_platform.emit_prepare_cmds() + prepare_container = self.container_platform.emit_prepare_commands() if prepare_container: self.pre_run += prepare_container diff --git a/unittests/test_containers.py b/unittests/test_containers.py index 4a86431f15..7a10a51013 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -36,7 +36,7 @@ def test_mount_points(self): self.container_platform.commands = ['cmd1', 'cmd2'] self.container_platform.workdir = '/stagedir' assert (self.expected_cmd_mount_points == - self.container_platform.emit_launch_cmds()) + self.container_platform.launch_command()) def test_missing_image(self): self.container_platform.commands = ['cmd'] @@ -51,7 +51,7 @@ def test_missing_commands(self): def test_prepare_command(self): self.container_platform.image = 'name:tag' assert (self.expected_cmd_prepare == - self.container_platform.emit_prepare_cmds()) + self.container_platform.emit_prepare_commands()) def test_run_opts(self): self.container_platform.image = 'name:tag' @@ -59,7 +59,7 @@ def test_run_opts(self): self.container_platform.mount_points = [('/path/one', '/one')] self.container_platform.workdir = '/stagedir' assert (self.expected_cmd_with_run_opts == - self.container_platform.emit_launch_cmds()) + self.container_platform.launch_command()) class TestDocker(_ContainerPlatformTest, unittest.TestCase): From 9d30bdc138190579c3db8f7e7930e182f0539c68 Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Fri, 11 Oct 2019 17:53:18 +0200 Subject: [PATCH 22/27] Fix PEP8 issues --- reframe/core/containers.py | 3 ++- reframe/core/pipeline.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 01fae17df6..e069f902f0 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -9,7 +9,8 @@ class ContainerPlatform(abc.ABC): '''The abstract base class of any container platform. Concrete container platforms inherit from this class and must override the - :func:`emit_prepare_commands()` and :func:`launch_command` abstract functions. + :func:`emit_prepare_commands()` and :func:`launch_command()` abstract + methods. ''' image = fields.TypedField('image', str, type(None)) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 3fe446d65d..97df625489 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1099,7 +1099,7 @@ def run(self): (self._stagedir, self.container_platform.workdir) ] - # We replace executable and executable_opts in the case of containers. + # We replace executable and executable_opts in case of containers self.executable = self.container_platform.launch_command() self.executable_opts = [] prepare_container = self.container_platform.emit_prepare_commands() From 27bc77cd517f90ca26e1ad02fd8098165ad0ef9e Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Sat, 12 Oct 2019 00:32:25 +0200 Subject: [PATCH 23/27] Enhance unit tests --- config/cscs.py | 6 -- reframe/core/pipeline.py | 5 +- unittests/test_containers.py | 66 ++++++++++++++- unittests/test_pipeline.py | 152 +++++++++++++++++++++++------------ 4 files changed, 167 insertions(+), 62 deletions(-) diff --git a/config/cscs.py b/config/cscs.py index 54b54bafdd..f4792df03f 100644 --- a/config/cscs.py +++ b/config/cscs.py @@ -165,9 +165,6 @@ class ReframeSettings: 'Singularity': { 'modules': ['singularity'] }, - 'Sarus': { - 'modules': ['sarus/cle7up01-test'] - } }, 'modules': ['daint-gpu'], 'access': ['--constraint=gpu'], @@ -187,9 +184,6 @@ class ReframeSettings: 'Singularity': { 'modules': ['singularity'] }, - 'Sarus': { - 'modules': ['sarus/cle7up01-test'] - } }, 'modules': ['daint-mc'], 'access': ['--constraint=mc'], diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 97df625489..24016e76dd 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1091,8 +1091,9 @@ def run(self): cp_name = type(self.container_platform).__name__ cp_env = self._current_partition.container_environs[cp_name] except KeyError as e: - raise PipelineError('no configuration found for container ' - 'platform: %s' % e) from None + raise PipelineError( + 'container platform not configured ' + 'on the current partition: %s' % e) from None self.container_platform.validate() self.container_platform.mount_points += [ diff --git a/unittests/test_containers.py b/unittests/test_containers.py index 7a10a51013..ae616e6687 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -98,7 +98,26 @@ def expected_cmd_prepare(self): @property def expected_cmd_with_run_opts(self): - self.container_platform.with_mpi = True + return ('shifter run ' + '--mount=type=bind,source="/path/one",destination="/one" ' + "name:tag bash -c 'cd /stagedir; cmd'") + + +class TestShifterNGWithMPI(TestShifterNG): + def create_container_platform(self): + ret = containers.ShifterNG() + ret.with_mpi = True + return ret + + @property + def expected_cmd_mount_points(self): + return ('shifter run ' + '--mount=type=bind,source="/path/one",destination="/one" ' + '--mount=type=bind,source="/path/two",destination="/two" ' + "--mpi name:tag bash -c 'cd /stagedir; cmd1; cmd2'") + + @property + def expected_cmd_with_run_opts(self): return ('shifter run ' '--mount=type=bind,source="/path/one",destination="/one" ' "--mpi name:tag bash -c 'cd /stagedir; cmd'") @@ -119,6 +138,26 @@ def expected_cmd_mount_points(self): def expected_cmd_prepare(self): return ['sarus pull name:tag'] + @property + def expected_cmd_with_run_opts(self): + return ('sarus run ' + '--mount=type=bind,source="/path/one",destination="/one" ' + "name:tag bash -c 'cd /stagedir; cmd'") + + +class TestSarusWithMPI(TestSarus): + def create_container_platform(self): + ret = containers.Sarus() + ret.with_mpi = True + return ret + + @property + def expected_cmd_mount_points(self): + return ('sarus run ' + '--mount=type=bind,source="/path/one",destination="/one" ' + '--mount=type=bind,source="/path/two",destination="/two" ' + "--mpi name:tag bash -c 'cd /stagedir; cmd1; cmd2'") + @property def expected_cmd_with_run_opts(self): self.container_platform.with_mpi = True @@ -142,6 +181,27 @@ def expected_cmd_prepare(self): @property def expected_cmd_with_run_opts(self): - self.container_platform.with_cuda = True - return ('singularity exec -B"/path/one:/one" --nv ' + return ('singularity exec -B"/path/one:/one" ' "name:tag bash -c 'cd /stagedir; cmd'") + + +class TestSingularityWithCuda(TestSingularity): + def create_container_platform(self): + ret = containers.Singularity() + ret.with_cuda = True + return ret + + @property + def expected_cmd_mount_points(self): + return ('singularity exec -B"/path/one:/one" -B"/path/two:/two" ' + "--nv name:tag bash -c 'cd /stagedir; cmd1; cmd2'") + + @property + def expected_cmd_prepare(self): + return [] + + @property + def expected_cmd_with_run_opts(self): + self.container_platform.with_cuda = True + return ('singularity exec -B"/path/one:/one" ' + "--nv name:tag bash -c 'cd /stagedir; cmd'") diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index e906cb83b8..e61417c80b 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -14,6 +14,26 @@ from reframe.frontend.loader import RegressionCheckLoader +def _setup_local_execution(): + partition = rt.runtime().system.partition('login') + environ = partition.environment('builtin-gcc') + return partition, environ + + +def _setup_remote_execution(): + partition = fixtures.partition_with_scheduler() + if partition is None: + pytest.skip('job submission not supported') + + try: + environ = partition.environs[0] + except IndexError: + pytest.skip('no environments configured for partition: %s' % + partition.fullname) + + return partition, environ + + def _run(test, partition, prgenv): test.setup(partition, prgenv) test.compile() @@ -26,23 +46,8 @@ def _run(test, partition, prgenv): class TestRegressionTest(unittest.TestCase): - def setup_local_execution(self): - self.partition = rt.runtime().system.partition('login') - self.prgenv = self.partition.environment('builtin-gcc') - - def setup_remote_execution(self): - self.partition = fixtures.partition_with_scheduler() - if self.partition is None: - self.skipTest('job submission not supported') - - try: - self.prgenv = self.partition.environs[0] - except IndexError: - self.skipTest('no environments configured for partition: %s' % - self.partition.fullname) - def setUp(self): - self.setup_local_execution() + self.partition, self.prgenv = _setup_local_execution() self.loader = RegressionCheckLoader(['unittests/resources/checks']) # Set runtime prefix @@ -93,7 +98,7 @@ def _run_test(self, test, compile_only=False): @fixtures.switch_to_user_runtime def test_hellocheck(self): - self.setup_remote_execution() + self.partition, self.prgenv = _setup_remote_execution() test = self.loader.load_from_file( 'unittests/resources/checks/hellocheck.py')[0] @@ -103,7 +108,7 @@ def test_hellocheck(self): @fixtures.switch_to_user_runtime def test_hellocheck_make(self): - self.setup_remote_execution() + self.partition, self.prgenv = _setup_remote_execution() test = self.loader.load_from_file( 'unittests/resources/checks/hellocheck_make.py')[0] @@ -833,50 +838,95 @@ def extract_perf(patt, tag): self.assertIn('v3', log_output) -class TestContainerPlatformsTest(TestRegressionTest): - def setup_test(self, platform, image): - self.setup_remote_execution() - test = rfm.RunOnlyRegressionTest() - test.name = 'Test%s' % platform - test._prefix = 'unittests/resources/checks' - test.valid_prog_environs = [self.prgenv.name] - test.valid_systems = ['*'] - test.container_platform = platform - test.container_platform.image = image - test.container_platform.commands = [ - 'pwd', 'ls', 'cat /etc/os-release'] - test.container_platform.workdir = '/workdir' - test.sanity_patterns = sn.all([ - sn.assert_found(r'^' + test.container_platform.workdir, - test.stdout), - sn.assert_found(r'^hello.c', test.stdout), - sn.assert_found(r'18.04.3 LTS \(Bionic Beaver\)', test.stdout), - ]) +class TestRegressionTestWithContainer(unittest.TestCase): + def temp_prefix(self): + # Set runtime prefix + rt.runtime().resources.prefix = tempfile.mkdtemp(dir='unittests') + + def create_test(self, platform, image): + class ContainerTest(rfm.RunOnlyRegressionTest): + def __init__(self, platform): + self._prefix = 'unittests/resources/checks' + self.valid_prog_environs = ['*'] + self.valid_systems = ['*'] + self.container_platform = platform + self.container_platform.image = image + self.container_platform.commands = [ + 'pwd', 'ls', 'cat /etc/os-release' + ] + self.container_platform.workdir = '/workdir' + self.sanity_patterns = sn.all([ + sn.assert_found( + r'^' + self.container_platform.workdir, self.stdout), + sn.assert_found(r'^hello.c', self.stdout), + sn.assert_found( + r'18\.04\.\d+ LTS \(Bionic Beaver\)', self.stdout), + ]) + + test = ContainerTest(platform) return test - def run_test(self, test): - try: - self._run_test(test) - except PipelineError: - self.skipTest('no configuration found for container platform: %s' % - type(test.container_platform).__name__) + def _skip_if_not_configured(self, partition, platform): + if platform not in partition.container_environs.keys(): + pytest.skip('%s is not configured on the system' % platform) @fixtures.switch_to_user_runtime def test_singularity(self): - test = self.setup_test('Singularity', 'docker://ubuntu:18.04') - self.run_test(test) + partition, environ = _setup_remote_execution() + self._skip_if_not_configured(partition, 'Singularity') + with tempfile.TemporaryDirectory(dir='unittests') as dirname: + rt.runtime().resources.prefix = dirname + _run(self.create_test('Singularity', 'docker://ubuntu:18.04'), + partition, environ) @fixtures.switch_to_user_runtime def test_docker(self): - test = self.setup_test('Docker', 'ubuntu:18.04') - self.run_test(test) + partition, environ = _setup_local_execution() + self._skip_if_not_configured(partition, 'Docker') + with tempfile.TemporaryDirectory(dir='unittests') as dirname: + rt.runtime().resources.prefix = dirname + _run(self.create_test('Docker', 'ubuntu:18.04'), + partition, environ) @fixtures.switch_to_user_runtime def test_shifter(self): - test = self.setup_test('ShifterNG', 'ubuntu:18.04') - self.run_test(test) + partition, environ = _setup_remote_execution() + self._skip_if_not_configured(partition, 'ShifterNG') + with tempfile.TemporaryDirectory(dir='unittests') as dirname: + rt.runtime().resources.prefix = dirname + _run(self.create_test('ShifterNG', 'ubuntu:18.04'), + partition, environ) @fixtures.switch_to_user_runtime def test_sarus(self): - test = self.setup_test('Sarus', 'ubuntu:18.04') - self.run_test(test) + partition, environ = _setup_remote_execution() + self._skip_if_not_configured(partition, 'Sarus') + with tempfile.TemporaryDirectory(dir='unittests') as dirname: + rt.runtime().resources.prefix = dirname + _run(self.create_test('Sarus', 'ubuntu:18.04'), + partition, environ) + + def test_unknown_platform(self): + partition, environ = _setup_local_execution() + with pytest.raises(ValueError): + with tempfile.TemporaryDirectory(dir='unittests') as dirname: + rt.runtime().resources.prefix = dirname + _run(self.create_test('foo', 'ubuntu:18.04'), + partition, environ) + + def test_not_configured_platform(self): + partition, environ = _setup_local_execution() + platform = None + for cp in ['Docker', 'Singularity', 'Sarus', 'ShifterNG']: + if cp not in partition.container_environs.keys(): + platform = cp + break + + if platform is None: + pytest.skip('cannot find a not configured supported platform') + + with pytest.raises(PipelineError): + with tempfile.TemporaryDirectory(dir='unittests') as dirname: + rt.runtime().resources.prefix = dirname + _run(self.create_test(platform, 'ubuntu:18.04'), + partition, environ) From 5907b36f09d82945e0527287a2477a4edcb0c5f8 Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Sun, 13 Oct 2019 20:43:15 +0200 Subject: [PATCH 24/27] Fix unit tests for Docker backend --- unittests/test_pipeline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index e61417c80b..ed25ec9ad6 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -20,8 +20,8 @@ def _setup_local_execution(): return partition, environ -def _setup_remote_execution(): - partition = fixtures.partition_with_scheduler() +def _setup_remote_execution(scheduler=None): + partition = fixtures.partition_with_scheduler(scheduler) if partition is None: pytest.skip('job submission not supported') @@ -881,7 +881,7 @@ def test_singularity(self): @fixtures.switch_to_user_runtime def test_docker(self): - partition, environ = _setup_local_execution() + partition, environ = _setup_remote_execution('local') self._skip_if_not_configured(partition, 'Docker') with tempfile.TemporaryDirectory(dir='unittests') as dirname: rt.runtime().resources.prefix = dirname From cf33912584c9b9d6d15da95c311a56714261cdea Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Mon, 14 Oct 2019 11:28:02 +0200 Subject: [PATCH 25/27] Remove unnecessary member variables --- reframe/core/containers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index e069f902f0..763629edf9 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -21,8 +21,6 @@ class ContainerPlatform(abc.ABC): def __init__(self): self.image = None - self.with_mpi = False - self.with_cuda = False self.commands = [] self.mount_points = [] self.workdir = '/rfm_workdir' From afac2ff5a0f1be0db4cf3b1bf0b43a380da4e16b Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Mon, 14 Oct 2019 12:05:05 +0200 Subject: [PATCH 26/27] Skip Singularity unit test on Cray CLE6 --- unittests/test_pipeline.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index ed25ec9ad6..5da21f5b92 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -1,5 +1,6 @@ import os import pytest +import re import tempfile import unittest @@ -45,6 +46,15 @@ def _run(test, partition, prgenv): test.cleanup(remove_files=True) +def _cray_cle_version(): + completed = os_ext.run_command('cat /etc/opt/cray/release/cle-release') + matched = re.match('^RELEASE=(\S+)', completed.stdout) + if matched is None: + return None + + return matched.group(1) + + class TestRegressionTest(unittest.TestCase): def setUp(self): self.partition, self.prgenv = _setup_local_execution() @@ -872,6 +882,10 @@ def _skip_if_not_configured(self, partition, platform): @fixtures.switch_to_user_runtime def test_singularity(self): + cle_version = _cray_cle_version() + if cle_version is not None and cle_version.startswith('6.0'): + pytest.skip('test not supported on Cray CLE6') + partition, environ = _setup_remote_execution() self._skip_if_not_configured(partition, 'Singularity') with tempfile.TemporaryDirectory(dir='unittests') as dirname: From b099fb46245d14e675ad5dae0dcfd7c39d9f8a02 Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Mon, 14 Oct 2019 12:55:38 +0200 Subject: [PATCH 27/27] Fix unit tests deprecation warning --- unittests/test_pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index 5da21f5b92..e74fa96d9b 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -48,7 +48,7 @@ def _run(test, partition, prgenv): def _cray_cle_version(): completed = os_ext.run_command('cat /etc/opt/cray/release/cle-release') - matched = re.match('^RELEASE=(\S+)', completed.stdout) + matched = re.match(r'^RELEASE=(\S+)', completed.stdout) if matched is None: return None