Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion docs/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ The valid attributes of a system are the following:
- ``tmod``: The classic Tcl implementation of the `environment modules <https://sourceforge.net/projects/modules/files/Modules/modules-3.2.10/>`__ (versions older than 3.2 are not supported).
- ``tmod4``: The version 4 of the Tcl implementation of the `environment modules <http://modules.sourceforge.net/>`__ (versions older than 4.1 are not supported).
- ``lmod``: The Lua implementation of the `environment modules <https://lmod.readthedocs.io/en/latest/>`__.

* ``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`).
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions reframe/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 13 additions & 3 deletions reframe/core/systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,22 @@ 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))
_perflogdir = fields.TypedField('_perflogdir', str, type(None))
_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
Expand Down Expand Up @@ -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."""
Expand Down
8 changes: 8 additions & 0 deletions reframe/frontend/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,14 @@ def main():
if options.purge_env:
rt.modules_system.unload_all()

# Load the environment for the current system
try:
rt.system.preload_environ.load()
except EnvironError as e:
printer.error("failed to load current's system environment; "
"please check your configuration")
raise
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we raising empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this means raise the current exception. But I should remove e, which I don't use.


for m in options.user_modules:
try:
rt.modules_system.load_module(m, force=True)
Expand Down
7 changes: 4 additions & 3 deletions unittests/resources/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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': [
Expand Down
110 changes: 60 additions & 50 deletions unittests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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'] = {
Expand All @@ -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'] = {
Expand All @@ -125,21 +133,23 @@ 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):
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')