From 56251bdf8281420a3209bf7ce749c0bf8413963d Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Wed, 24 Jul 2019 18:23:10 +0200 Subject: [PATCH 1/3] Add config option to modify the environment per system --- reframe/core/config.py | 8 +++ reframe/core/systems.py | 16 ++++- reframe/frontend/cli.py | 3 + unittests/resources/settings.py | 7 +- unittests/test_config.py | 110 +++++++++++++++++--------------- 5 files changed, 88 insertions(+), 56 deletions(-) diff --git a/reframe/core/config.py b/reframe/core/config.py index 491404a820..f5a0d89b3e 100644 --- a/reframe/core/config.py +++ b/reframe/core/config.py @@ -161,9 +161,17 @@ def create_env(system, partition, name): if sys_resourcesdir: sys_resourcesdir = os_ext.expandvars(sys_resourcesdir) + # Create the preload environment for the system + sys_preload_env = m_env.Environment( + name='__rfm_env_%s' % sys_name, + modules=config.get('modules', []), + variables=config.get('variables', {}) + ) + system = System(name=sys_name, descr=sys_descr, hostnames=sys_hostnames, + preload_env=sys_preload_env, prefix=sys_prefix, stagedir=sys_stagedir, outputdir=sys_outputdir, diff --git a/reframe/core/systems.py b/reframe/core/systems.py index 193231f3c6..b9ae7a9fc7 100644 --- a/reframe/core/systems.py +++ b/reframe/core/systems.py @@ -166,7 +166,7 @@ class System: _partitions = fields.TypedField('_partitions', typ.List[SystemPartition]) _modules_system = fields.TypedField('_modules_system', typ.Str[r'(\w|-)+'], type(None)) - + _preload_env = fields.TypedField('_preload_env', Environment, type(None)) _prefix = fields.TypedField('_prefix', str) _stagedir = fields.TypedField('_stagedir', str, type(None)) _outputdir = fields.TypedField('_outputdir', str, type(None)) @@ -174,13 +174,14 @@ class System: _resourcesdir = fields.TypedField('_resourcesdir', str) def __init__(self, name, descr=None, hostnames=[], partitions=[], - prefix='.', stagedir=None, outputdir=None, perflogdir=None, - resourcesdir='.', modules_system=None): + preload_env=None, prefix='.', stagedir=None, outputdir=None, + perflogdir=None, resourcesdir='.', modules_system=None): self._name = name self._descr = descr or name self._hostnames = list(hostnames) self._partitions = list(partitions) self._modules_system = modules_system + self._preload_env = preload_env self._prefix = prefix self._stagedir = stagedir self._outputdir = outputdir @@ -211,6 +212,15 @@ def modules_system(self): """The modules system name associated with this system.""" return self._modules_system + @property + def preload_environ(self): + """The environment to load whenever ReFrame runs on this system. + + .. note:: + .. versionadded:: 2.19 + """ + return self._preload_env + @property def prefix(self): """The ReFrame prefix associated with this system.""" diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py index af883f8408..f9dec3642f 100644 --- a/reframe/frontend/cli.py +++ b/reframe/frontend/cli.py @@ -494,6 +494,9 @@ def main(): if options.purge_env: rt.modules_system.unload_all() + # Load the environment for the current system + rt.system.preload_environ.load() + for m in options.user_modules: try: rt.modules_system.load_module(m, force=True) diff --git a/unittests/resources/settings.py b/unittests/resources/settings.py index 1d6713e37d..6046aebb9d 100644 --- a/unittests/resources/settings.py +++ b/unittests/resources/settings.py @@ -34,18 +34,19 @@ class ReframeSettings: 'prefix': '.rfm_testing', 'resourcesdir': '.rfm_testing/resources', 'perflogdir': '.rfm_testing/perflogs', + 'modules': ['foo/1.0'], + 'variables': {'FOO_CMD': 'foobar'}, 'partitions': { 'login': { 'scheduler': 'local', - 'modules': [], - 'access': [], 'resources': {}, 'environs': ['PrgEnv-cray', 'PrgEnv-gnu', 'builtin-gcc'], 'descr': 'Login nodes' }, 'gpu': { 'scheduler': 'nativeslurm', - 'modules': [], + 'modules': ['foogpu'], + 'variables': {'FOO_GPU': 'yes'}, 'resources': { 'gpu': ['--gres=gpu:{num_gpus_per_node}'], 'datawarp': [ diff --git a/unittests/test_config.py b/unittests/test_config.py index a1799d88e1..0124ed3d4b 100644 --- a/unittests/test_config.py +++ b/unittests/test_config.py @@ -4,6 +4,7 @@ import reframe.core.config as config import unittests.fixtures as fixtures from reframe.core.exceptions import ConfigError +import pytest class TestSiteConfigurationFromDict(unittest.TestCase): @@ -18,87 +19,94 @@ def get_partition(self, system, name): def test_load_success(self): self.site_config.load_from_dict(self.dict_config) - self.assertEqual(3, len(self.site_config.systems)) + assert len(self.site_config.systems) == 3 system = self.site_config.systems['testsys'] - self.assertEqual(2, len(system.partitions)) - self.assertEqual('.rfm_testing', system.prefix) - self.assertEqual('.rfm_testing/resources', system.resourcesdir) - self.assertEqual('.rfm_testing/perflogs', system.perflogdir) + assert len(system.partitions) == 2 + assert system.prefix == '.rfm_testing' + assert system.resourcesdir == '.rfm_testing/resources' + assert system.perflogdir == '.rfm_testing/perflogs' + assert system.preload_environ.modules == ['foo/1.0'] + assert system.preload_environ.variables == {'FOO_CMD': 'foobar'} part_login = self.get_partition(system, 'login') part_gpu = self.get_partition(system, 'gpu') - self.assertIsNotNone(part_login) - self.assertIsNotNone(part_gpu) - self.assertEqual('testsys:login', part_login.fullname) - self.assertEqual('testsys:gpu', part_gpu.fullname) - self.assertEqual(3, len(part_login.environs)) - self.assertEqual(2, len(part_gpu.environs)) + assert part_login is not None + assert part_gpu is not None + assert part_login.fullname == 'testsys:login' + assert part_gpu.fullname == 'testsys:gpu' + assert len(part_login.environs) == 3 + assert len(part_gpu.environs) == 2 + + # Check local partition environment + assert part_gpu.local_env.modules == ['foogpu'] + assert part_gpu.local_env.variables == {'FOO_GPU': 'yes'} # Check that PrgEnv-gnu on login partition is resolved to the special # version defined in the 'dom:login' section env_login = part_login.environment('PrgEnv-gnu') - self.assertEqual('gcc', env_login.cc) - self.assertEqual('g++', env_login.cxx) - self.assertEqual('gfortran', env_login.ftn) + assert env_login.cc == 'gcc' + assert env_login.cxx == 'g++' + assert env_login.ftn == 'gfortran' # Check that the PrgEnv-gnu of the gpu partition is resolved to the # default one env_gpu = part_gpu.environment('PrgEnv-gnu') - self.assertEqual('cc', env_gpu.cc) - self.assertEqual('CC', env_gpu.cxx) - self.assertEqual('ftn', env_gpu.ftn) + assert env_gpu.cc == 'cc' + assert env_gpu.cxx == 'CC' + assert env_gpu.ftn == 'ftn' # Check resource instantiation - self.assertEqual(['--gres=gpu:16'], - part_gpu.get_resource('gpu', num_gpus_per_node=16)) - self.assertEqual(['#DW jobdw capacity=100GB', - '#DW stage_in source=/foo'], - part_gpu.get_resource('datawarp', + resource_spec = part_gpu.get_resource('gpu', num_gpus_per_node=16) + assert (resource_spec == ['--gres=gpu:16']) + + resources_spec = part_gpu.get_resource('datawarp', capacity='100GB', - stagein_src='/foo')) + stagein_src='/foo') + assert (resources_spec == ['#DW jobdw capacity=100GB', + '#DW stage_in source=/foo']) def test_load_failure_empty_dict(self): dict_config = {} - self.assertRaises(ValueError, - self.site_config.load_from_dict, dict_config) + with pytest.raises(ValueError): + self.site_config.load_from_dict(dict_config) def test_load_failure_no_environments(self): dict_config = {'systems': {}} - self.assertRaises(ValueError, - self.site_config.load_from_dict, dict_config) + with pytest.raises(ValueError): + self.site_config.load_from_dict(dict_config) def test_load_failure_no_systems(self): dict_config = {'environments': {}} - self.assertRaises(ValueError, - self.site_config.load_from_dict, dict_config) + with pytest.raises(ValueError): + self.site_config.load_from_dict(dict_config) def test_load_failure_environments_no_scoped_dict(self): self.dict_config['environments'] = { 'testsys': 'PrgEnv-gnu' } - self.assertRaises(TypeError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(TypeError): + self.site_config.load_from_dict(self.dict_config) def test_load_failure_partitions_nodict(self): self.dict_config['systems']['testsys']['partitions'] = ['gpu'] - self.assertRaises(ConfigError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(ConfigError): + self.site_config.load_from_dict(self.dict_config) def test_load_failure_systems_nodict(self): self.dict_config['systems']['testsys'] = ['gpu'] - self.assertRaises(TypeError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(TypeError): + self.site_config.load_from_dict(self.dict_config) def test_load_failure_partitions_nodict(self): self.dict_config['systems']['testsys']['partitions']['login'] = 'foo' - self.assertRaises(TypeError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(TypeError): + self.site_config.load_from_dict(self.dict_config) def test_load_failure_partconfig_nodict(self): self.dict_config['systems']['testsys']['partitions']['login'] = 'foo' - self.assertRaises(TypeError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(TypeError): + self.site_config.load_from_dict(self.dict_config) def test_load_failure_unresolved_environment(self): self.dict_config['environments'] = { @@ -109,13 +117,13 @@ def test_load_failure_unresolved_environment(self): } } } - self.assertRaises(ConfigError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(ConfigError): + self.site_config.load_from_dict(self.dict_config) def test_load_failure_envconfig_nodict(self): self.dict_config['environments']['*']['PrgEnv-gnu'] = 'foo' - self.assertRaises(TypeError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(TypeError): + self.site_config.load_from_dict(self.dict_config) def test_load_failure_envconfig_notype(self): self.dict_config['environments'] = { @@ -125,8 +133,8 @@ def test_load_failure_envconfig_notype(self): } } } - self.assertRaises(ConfigError, - self.site_config.load_from_dict, self.dict_config) + with pytest.raises(ConfigError): + self.site_config.load_from_dict(self.dict_config) class TestConfigLoading(unittest.TestCase): @@ -134,12 +142,14 @@ def test_load_normal_config(self): config.load_settings_from_file('unittests/resources/settings.py') def test_load_unknown_file(self): - self.assertRaises(ConfigError, config.load_settings_from_file, 'foo') + with pytest.raises(ConfigError): + config.load_settings_from_file('foo') def test_load_no_settings(self): - self.assertRaises(ConfigError, - config.load_settings_from_file, 'unittests') + with pytest.raises(ConfigError): + config.load_settings_from_file('unittests') def test_load_invalid_settings(self): - self.assertRaises(ConfigError, config.load_settings_from_file, - 'unittests/resources/invalid_settings.py') + with pytest.raises(ConfigError): + config.load_settings_from_file( + 'unittests/resources/invalid_settings.py') From 08991bbda2f7315db9c93a180151c7f7b6ceeb54 Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Thu, 25 Jul 2019 18:29:52 +0200 Subject: [PATCH 2/3] Improve error message --- reframe/frontend/cli.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py index f9dec3642f..8b8ba63e76 100644 --- a/reframe/frontend/cli.py +++ b/reframe/frontend/cli.py @@ -495,7 +495,12 @@ def main(): rt.modules_system.unload_all() # Load the environment for the current system - rt.system.preload_environ.load() + try: + rt.system.preload_environ.load() + except EnvironError as e: + printer.error("failed to load current's system environment; " + "please check your configuration") + raise for m in options.user_modules: try: From 8ec260129a821cf0aa1af12ed988a1a18ca4f4a3 Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Thu, 25 Jul 2019 23:17:46 +0200 Subject: [PATCH 3/3] Add docs for the new configuration parameters. --- docs/configure.rst | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/configure.rst b/docs/configure.rst index 035652aa14..0ab47940ca 100644 --- a/docs/configure.rst +++ b/docs/configure.rst @@ -100,6 +100,11 @@ The valid attributes of a system are the following: - ``tmod``: The classic Tcl implementation of the `environment modules `__ (versions older than 3.2 are not supported). - ``tmod4``: The version 4 of the Tcl implementation of the `environment modules `__ (versions older than 4.1 are not supported). - ``lmod``: The Lua implementation of the `environment modules `__. + +* ``modules``: Modules to be loaded always when running on this system. + These modules modify the ReFrame environment. + This is useful when for example a particular module is needed to submit jobs on a specific system. +* ``variables``: Environment variables to be set always when running on this system. * ``prefix``: Default regression prefix for this system (default ``.``). * ``stagedir``: Default stage directory for this system (default :class:`None`). * ``outputdir``: Default output directory for this system (default :class:`None`). @@ -114,6 +119,11 @@ For a more detailed description of the ``prefix``, ``stagedir``, ``outputdir`` a .. versionadded:: 2.8 The ``modules_system`` key was introduced for specifying custom modules systems for different systems. +.. note:: + .. versionadded:: 2.19 + The ``modules`` and ``variables`` configuration parameters were introduced at the system level. + + .. warning:: .. versionchanged:: 2.18 The ``logdir`` key is no more supported; please use ``perflogdir`` instead. @@ -218,9 +228,15 @@ The available partition attributes are the following: A new syntax for the ``scheduler`` values was introduced as well as more parallel program launchers. The old values for the ``scheduler`` key will continue to be supported. +.. note:: .. versionchanged:: 2.9 - Better support for custom job resources. + Better support for custom job resources. +.. note:: + .. versionchanged:: 2.14 + The ``modules`` and ``variables`` partition configuration parameters do not affect the ReFrame environment anymore. + They essentially define an environment to be always emitted when building and/or running the test on this partition. + If you want to modify the environment ReFrame runs in for a particular system, define these parameters inside the `system configuration <#system-configuration>`__. Supported scheduler backends