Skip to content
This repository was archived by the owner on Jan 28, 2022. It is now read-only.
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
29 changes: 25 additions & 4 deletions docs/campaign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ All methods are being used:
* **tags** expects a list of tag names

* **constraint** expects a string. This tag does not references node
names explicitely but instead delegates it to SLURM. The value of the
names explicitly but instead delegates it to SLURM. The value of the
constraint tag is given to the sbatch options through the
*--constraint* option.

Expand Down Expand Up @@ -275,7 +275,7 @@ options, which are passed to the `srun` command. Note that only the long form
option names should be used (i.e. `--nodes` instead of `-N`). These options overwrite
the global options provided in the :ref:`process <campaign-process>` section. To disable
a global srun option simply declare the option without providing a value. if an option without
value (e.g. `--exclusvie`) is to be used in `srun`, the key should be assigned to `true`.
value (e.g. `--exclusive`) is to be used in `srun`, the key should be assigned to `true`.

.. code-block:: yaml
:emphasize-lines: 4,7,8
Expand All @@ -290,6 +290,27 @@ value (e.g. `--exclusvie`) is to be used in `srun`, the key should be assigned t
exclusive: true
type: osu

spack (optional)
~~~~~~~~~~~~~~~~
Dictionary to specify spack related configuration. Supported attributes are:

* **specs**: list of spack specs to install before executing benchmarks.
`bin` directory of install directories are be prepended to `PATH`.

For instance:

.. code-block:: yaml
:emphasize-lines: 5-7

benchmarks:
'*':
test01:
type: stream
spack:
specs:
Copy link
Contributor

Choose a reason for hiding this comment

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

As mentioned also in the other repo. do we really need this additional level. It seems redundant, or do you think in the future we might have other spack functions that would go in there?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you for the feedback. I agree this extra level is a pain, but as I am not expert at spack, I prefered to leave space for future changes.

- stream@intel+openmp


attempts (optional)
~~~~~~~~~~~~~~~~~~~
Dictionary to specify the number of times a command must be executed before
Expand All @@ -310,7 +331,7 @@ the ``fixed`` option.
fixed: 2

All executions are present in the report but only metrics of the last run are reported. The
``sorted`` key allows to change this behavior to reorder the runs according to criterias.
``sorted`` key allows to change this behavior to reorder the runs according to criteria.

.. code-block:: yaml
:emphasize-lines: 6-8
Expand Down Expand Up @@ -462,7 +483,7 @@ the binaries.
type: slurm
commands:
sbatch: /opt/slurm/bin/sbatch
srun: /opt/slrum/bin/sbatch
srun: /opt/slurm/bin/sbatch

srun and sbatch (optional)
~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
31 changes: 29 additions & 2 deletions hpcbench/driver/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from hpcbench.toolbox.environment_modules import Module
from hpcbench.toolbox.functools_ext import listify
from hpcbench.toolbox.process import find_executable
from hpcbench.toolbox.spack import SpackCmd


class BenchmarkDriver(Enumerator):
Expand Down Expand Up @@ -138,6 +139,14 @@ def children(self):
if 'modules' in self.config:
yaml_modules = self.config['modules'] or []
cmd.execution['modules'] = list(yaml_modules)
# Update spack config according to YAML
# if None, then reset spack config
if 'spack' in self.config:
spack_c = self.config['spack']
if spack_c is None:
cmd.execution['spack'] = {}
else:
cmd.execution.setdefault('spack', {}).update(spack_c)
# Enrich `metas` if specified in YAML
if 'metas' in self.campaign:
metas = dict(self.campaign.metas)
Expand Down Expand Up @@ -226,12 +235,30 @@ def _module_env(self, execution):
finally:
os.environ = env

@contextlib.contextmanager
def _spack_env(self, execution):
env = copy.copy(os.environ)
try:
spack = SpackCmd()
for spec in execution.get('spack', {}).get('specs', []):
spack.install(spec)
install_dir = spack.install_dir(spec)
bin_dir = osp.join(install_dir, 'bin')
if osp.exists(bin_dir):
path = os.environ.get('PATH', '')
path = bin_dir + os.pathsep + path
os.environ['PATH'] = path
yield
finally:
os.environ = env

def _execute(self, **kwargs):
runs = dict()
for command, run_dir in self.children:
if _HAS_MAGIC and 'shell' not in command.execution:
with self._module_env(command.execution):
self._add_build_info(command.execution)
exc = command.execution
with self._spack_env(exc), self._module_env(exc):
self._add_build_info(exc)
else:
self.logger.info(
"No build information recorded " "(libmagic available: %s)",
Expand Down
13 changes: 10 additions & 3 deletions hpcbench/driver/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,21 @@ def popen(self, stdout, stderr):
return subprocess.Popen([self._executor_script], stdout=stdout, stderr=stderr)

def __execute(self, stdout, stderr):
with self.module_env():
with self.module_env(), self.spack_env():
self.benchmark.pre_execute(self.execution, self.exec_context)
exit_status = self.popen(stdout, stderr).wait()
with self.module_env():
with self.module_env(), self.spack_env():
self.benchmark.post_execute(self.execution, self.exec_context)
return exit_status

def module_env(self):
category_driver = self.parent.parent
return category_driver._module_env(self.execution)

def spack_env(self):
category_driver = self.parent.parent
return category_driver._spack_env(self.execution)

@write_yaml_report
@Enumerator.call_decorator
def __call__(self, **kwargs):
Expand Down Expand Up @@ -216,7 +220,10 @@ def srun(self):
:rtype: string
"""
commands = self.campaign.process.get('commands', {})
return find_executable(commands.get('srun', 'srun'))
srun = find_executable(commands.get('srun', 'srun'))
if six.PY2:
srun = srun.encode('utf-8')
return srun

@classmethod
def common_srun_options(cls, campaign):
Expand Down
24 changes: 24 additions & 0 deletions hpcbench/toolbox/spack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from subprocess import check_call, check_output

from . process import find_executable


class SpackCmd:
@property
def spack(self):
return find_executable('spack', required=False)

def install(self, *args):
self.cmd('install', '--show-log-on-error', '--verbose', *args)

def install_dir(self, *args):
output = self.cmd('location', '--install-dir', *args, output=True)
return output.strip().decode('utf-8')

def cmd(self, *args, **kwargs):
command = [self.spack] + list(args)
if kwargs.get('output', False):
func = check_output
else:
func = check_call
return func(command)
6 changes: 5 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ def get_campaign_file(cls):

@classmethod
def setUpClass(cls):
cls.TEST_DIR = tempfile.mkdtemp(prefix='hpcbench-ut')
cls.TEST_DIR = cls.mkdtemp()
with pushd(cls.TEST_DIR):
cls.driver = bensh.main(cls.get_campaign_file())
cls.CAMPAIGN_PATH = osp.join(cls.TEST_DIR, cls.driver.campaign_path)
if cls.check_campaign_consistency:
cls._check_campaign_consistency()

@classmethod
def mkdtemp(cls):
return tempfile.mkdtemp(prefix='hpcbench-ut')

@classmethod
def _check_campaign_consistency(cls):
dirs = [cls.CAMPAIGN_PATH]
Expand Down
4 changes: 2 additions & 2 deletions tests/test_benwait.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import os
import os.path as osp
import shutil
import tempfile
import unittest

import mock
Expand All @@ -11,6 +10,7 @@

from hpcbench.campaign import ReportNode, YAML_REPORT_FILE
from hpcbench.cli.benwait import main as benwait, wait_for_completion
from . import DriverTestCase


class sacct:
Expand Down Expand Up @@ -64,7 +64,7 @@ def tearDownClass(cls):

@classmethod
def _create_fake_campaign(cls):
campaign = tempfile.mkdtemp()
campaign = DriverTestCase.mkdtemp()
with open(osp.join(campaign, YAML_REPORT_FILE), 'w') as ostr:
yaml.dump(dict(children=['sbatch1', 'sbatch2']), ostr)
for jobid in range(1, 3):
Expand Down
5 changes: 2 additions & 3 deletions tests/test_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
import inspect
import os.path as osp
import shutil
import tempfile
import unittest

from hpcbench.cli import bencsv, bensh
from hpcbench.export.csvexport import CSVExporter
from hpcbench.toolbox.contextlib_ext import pushd
from . import FakeBenchmark
from . import DriverTestCase, FakeBenchmark


class TestCSV(unittest.TestCase):
Expand All @@ -17,7 +16,7 @@ class TestCSV(unittest.TestCase):
PERFORMANCE_METRIC = 'metrics.0.measurement.performance'

def setUp(self):
self.temp_dir = tempfile.mkdtemp(prefix='hpcbench-ut')
self.temp_dir = DriverTestCase.mkdtemp()

def tearDown(self):
shutil.rmtree(self.temp_dir)
Expand Down
4 changes: 3 additions & 1 deletion tests/test_cwd.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import socket
import os
import unittest

from hpcbench.campaign import ReportNode
Expand All @@ -8,7 +9,8 @@
class CwdTest(DriverTestCase, unittest.TestCase):
def test(self):
host = socket.gethostname()
path = '/tmp/hpcbench-ut/test_cwd/' + host + '/*'
tempdir = os.path.realpath('/tmp')
path = tempdir + '/hpcbench-ut/test_cwd/' + host + '/*'
report = ReportNode(CwdTest.CAMPAIGN_PATH)
count = 0
for metrics in report.collect('metrics'):
Expand Down
4 changes: 2 additions & 2 deletions tests/test_merge.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import inspect
import os.path as osp
import shutil
import tempfile
import unittest

from hpcbench.campaign import merge_campaigns
from hpcbench.cli import benmerge, bensh
from hpcbench.toolbox.contextlib_ext import pushd
from . import DriverTestCase


class TestMerge(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp(prefix='hpcbench-ut')
self.temp_dir = DriverTestCase.mkdtemp()

def tearDown(self):
shutil.rmtree(self.temp_dir)
Expand Down
3 changes: 1 addition & 2 deletions tests/test_slurm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import shutil
import stat
import subprocess
import tempfile
import textwrap
import unittest

Expand All @@ -24,7 +23,7 @@ class TestSlurm(DriverTestCase, unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.SLURM_ALLOC_NODE = 'n3'
cls.SLURM_UT_DIR = tempfile.mkdtemp(prefix='hpcbench-ut')
cls.SLURM_UT_DIR = cls.mkdtemp()
sbatch_ut = osp.join(cls.SLURM_UT_DIR, 'sbatch-ut')
with open(sbatch_ut, 'w') as ostr:
ostr.write(
Expand Down
53 changes: 53 additions & 0 deletions tests/test_spack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import mock
import os
import os.path as osp
import shutil
import sys
import unittest

from hpcbench.campaign import ReportNode
from . import DriverTestCase


FOO_INSTALL_DIR = DriverTestCase.mkdtemp()


def co_mock(command):
if command[1:4] == ['location', '--install-dir', 'foo@dev']:
return (FOO_INSTALL_DIR + '\n').encode()
raise NotImplementedError


def cc_mock(command):
if command[1] == 'install' and command[-1] == 'foo@dev':
return
with open('/tmp/foo.txt', 'w') as ostr:
ostr.write(repr(command))
raise NotImplementedError


CO_MOCK = mock.Mock(side_effect=co_mock)
CC_MOCK = mock.Mock(side_effect=cc_mock)


class TestSpack(DriverTestCase, unittest.TestCase):
@classmethod
@mock.patch('hpcbench.toolbox.spack.check_output', new=CO_MOCK)
@mock.patch('hpcbench.toolbox.spack.check_call', new=CC_MOCK)
def setUpClass(cls):
# prepare spack install directory of 'foo' package
bin_dir = osp.join(FOO_INSTALL_DIR, 'bin')
foo_python = osp.join(bin_dir, 'foo-python')
os.mkdir(bin_dir)
os.symlink(sys.executable, foo_python)
super(cls, cls).setUpClass()

@classmethod
def tearDownClass(cls):
shutil.rmtree(FOO_INSTALL_DIR)
super(cls, cls).tearDownClass()

def test(self):
report = ReportNode(self.CAMPAIGN_PATH)
data = list(report.collect('command_succeeded'))
self.assertEqual(data, [True] * 3)
7 changes: 7 additions & 0 deletions tests/test_spack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
benchmarks:
'*':
bench-name:
type: fake
spack: {specs: [foo@dev]}
attributes:
executable: foo-python
3 changes: 1 addition & 2 deletions tests/test_srun.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import os.path as osp
import shutil
import stat
import tempfile
import textwrap
import unittest

Expand All @@ -15,7 +14,7 @@ class TestSrun(DriverTestCase, unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.SLURM_ALLOC_NODE = 'n3'
cls.SLURM_UT_DIR = tempfile.mkdtemp(prefix='hpcbench-ut')
cls.SLURM_UT_DIR = cls.mkdtemp()
srun_ut = osp.join(cls.SLURM_UT_DIR, 'srun-ut')
with open(srun_ut, 'w') as ostr:
ostr.write(
Expand Down