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
23 changes: 18 additions & 5 deletions reframe/frontend/autodetect.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import os
import shutil
import tempfile
import traceback

import reframe as rfm
import reframe.core.runtime as runtime
Expand All @@ -19,6 +20,10 @@
from reframe.utility.cpuinfo import cpuinfo


# This is meant to be used by the unit tests
_TREAT_WARNINGS_AS_ERRORS = False


def _contents(filename):
'''Return the contents of a file.'''

Expand All @@ -36,7 +41,6 @@ def _log_contents(filename):
class _copy_reframe:
def __init__(self, prefix):
self._prefix = prefix
self._prefix = runtime().get_option('general/0/remote_workdir')
self._workdir = None

def __enter__(self):
Expand Down Expand Up @@ -82,6 +86,9 @@ def _load_info(filename, schema=None):
with open(filename) as fp:
return _validate_info(json.load(fp), schema)
except OSError as e:
if _TREAT_WARNINGS_AS_ERRORS:
raise

getlogger().warning(
f'could not load file: {filename!r}: {e}'
)
Expand All @@ -101,9 +108,14 @@ def _save_info(filename, topo_info):
with open(filename, 'w') as fp:
json.dump(topo_info, fp, indent=2)
except OSError as e:
if _TREAT_WARNINGS_AS_ERRORS:
raise

getlogger().warning(
f'could not save topology file: {filename!r}: {e}'
)
else:
getlogger().debug(f'> saved topology in {filename!r}')


def _is_part_local(part):
Expand Down Expand Up @@ -143,7 +155,11 @@ def _emit_script(job, env):
_log_contents(job.stderr)
topo_info = json.loads(_contents('topo.json'))
except Exception as e:
if _TREAT_WARNINGS_AS_ERRORS:
raise

getlogger().warning(f'failed to retrieve remote processor info: {e}')
getlogger().debug(traceback.format_exc())

return topo_info

Expand Down Expand Up @@ -226,14 +242,11 @@ def detect_topology():

_save_info(topo_file, part.processor.info)
elif detect_remote_systems:
with runtime.temp_environment(modules=temp_modules,
variables=temp_vars):
with runtime.temp_environment(modules=modules, variables=vars):
part._processor = ProcessorInfo(_remote_detect(part))

if part.processor.info:
_save_info(topo_file, part.processor.info)

getlogger().debug(f'> saved topology in {topo_file!r}')

if not found_devinfo:
getlogger().debug(f'> device auto-detection is not supported')
48 changes: 39 additions & 9 deletions unittests/test_autodetect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
import os
import pytest


import reframe.frontend.autodetect as autodetect
import unittests.utility as test_util
from reframe.core.runtime import runtime
from reframe.frontend.autodetect import detect_topology
from reframe.utility.cpuinfo import cpuinfo


@pytest.fixture
def exec_ctx(make_exec_ctx_g, tmp_path, monkeypatch):
def temp_topo(tmp_path, monkeypatch):
# Monkey-patch HOME, since topology is always written there
monkeypatch.setenv('HOME', str(tmp_path))
monkeypatch.setattr(autodetect, '_TREAT_WARNINGS_AS_ERRORS', True)

# Create a devices file manually, since it is not auto-generated
meta_prefix = tmp_path / '.reframe' / 'topology' / 'generic-default'
Expand All @@ -30,13 +31,12 @@ def exec_ctx(make_exec_ctx_g, tmp_path, monkeypatch):
}
], fp)

yield from make_exec_ctx_g()


@pytest.fixture
def invalid_topo_exec_ctx(make_exec_ctx_g, tmp_path, monkeypatch):
def invalid_topo(tmp_path, monkeypatch):
# Monkey-patch HOME, since topology is always written there
monkeypatch.setenv('HOME', str(tmp_path))
monkeypatch.setattr(autodetect, '_TREAT_WARNINGS_AS_ERRORS', True)

# Create invalid processor and devices files
meta_prefix = tmp_path / '.reframe' / 'topology' / 'generic-default'
Expand All @@ -47,11 +47,30 @@ def invalid_topo_exec_ctx(make_exec_ctx_g, tmp_path, monkeypatch):
with open(meta_prefix / 'devices.json', 'w') as fp:
fp.write('{')


@pytest.fixture
def default_exec_ctx(make_exec_ctx_g, temp_topo):
yield from make_exec_ctx_g()


@pytest.fixture
def remote_exec_ctx(make_exec_ctx, temp_topo):
if test_util.USER_CONFIG_FILE is None:
pytest.skip('no user configuration file supplied')

ctx = make_exec_ctx(test_util.USER_CONFIG_FILE,
test_util.USER_SYSTEM,
{'general/remote_detect': True})
yield ctx


@pytest.fixture
def invalid_topo_exec_ctx(make_exec_ctx_g, invalid_topo):
yield from make_exec_ctx_g()


def test_autotect(exec_ctx):
detect_topology()
def test_autotect(default_exec_ctx):
autodetect.detect_topology()
part = runtime().system.partitions[0]
assert part.processor.info == cpuinfo()
if part.processor.info:
Expand Down Expand Up @@ -80,7 +99,18 @@ def test_autotect(exec_ctx):


def test_autotect_with_invalid_files(invalid_topo_exec_ctx):
detect_topology()
autodetect.detect_topology()
part = runtime().system.partitions[0]
assert part.processor.info == cpuinfo()
assert part.devices == []


def test_remote_autodetect(remote_exec_ctx):
# All we can do with this test is to trigger the remote auto-detection
# path; since we don't know what the remote user system is, we cannot test
# if the topology is right.
partition = test_util.partition_by_scheduler()
if not partition:
pytest.skip('job submission not supported')

autodetect.detect_topology()