Skip to content

Commit

Permalink
Merge pull request #5 from eth-cscs/deploy/2.4
Browse files Browse the repository at this point in the history
Reframe 2.4 public release
  • Loading branch information
vkarak authored Jun 26, 2017
2 parents f9b7b1c + d093821 commit d1ae0c4
Show file tree
Hide file tree
Showing 31 changed files with 2,124 additions and 517 deletions.
1 change: 1 addition & 0 deletions reframe.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
import sys
import reframe.frontend.cli as cli

if __name__ == '__main__':
Expand Down
21 changes: 11 additions & 10 deletions reframe/core/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import subprocess
import reframe.utility.os as os_ext

from reframe.core.exceptions import ReframeError, CommandError, \
CompilationError
from reframe.core.exceptions import ReframeError, CommandError, CompilationError
from reframe.core.fields import *
from reframe.core.modules import *

Expand Down Expand Up @@ -40,18 +39,20 @@ def set_variable(self, name, value):

def load(self):
"""Load environment."""
for k, v in self.variables.items():
if k in os.environ:
self._saved_variables[k] = os.environ[k]
os.environ[k] = v

# conlicted module list must be filled at the time of load
# conflicted module list must be filled at the time of load
for m in self.modules:
if module_present(m):
self._preloaded.add(m)

self._conflicted += module_force_load(m)

for k, v in self.variables.items():
if k in os.environ:
self._saved_variables[k] = os.environ[k]

os.environ[k] = os.path.expandvars(v)

self.loaded = True


Expand Down Expand Up @@ -94,15 +95,15 @@ def emit_load_instructions(self, builder):
# FIXME: Does not correspond to the actual process in unload()
def emit_unload_instructions(self, builder):
"""Emit shell instructions for loading this environment."""
for k, v in self.variables.items():
builder.unset_variable(k)

for m in self.modules:
builder.verbatim('module unload %s' % m)

for m in self._conflicted:
builder.verbatim('module load %s' % m)

for k, v in self.variables.items():
builder.unset_variable(k)


def __eq__(self, other):
return \
Expand Down
27 changes: 15 additions & 12 deletions reframe/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __str__(self):
return self.message


class RegressionFatalError(ReframeError):
class ReframeFatalError(ReframeError):
pass


Expand All @@ -31,18 +31,19 @@ class ConfigurationError(ReframeError):

class CommandError(ReframeError):
def __init__(self, command, stdout, stderr, exitcode, timeout=0):
if not isinstance(command, str):
self.command = ' '.join(command)
else:
self.command = command

if timeout:
super().__init__(
"Command '%s' timed out after %d s" % (command, timeout))
"Command `%s' timed out after %d s" % (self.command, timeout))

else:
super().__init__(
"Command '%s' failed with exit code: %d" % (command, exitcode))

if not isinstance(command, str):
self.command = ' '.join(command)
else:
self.command = command
"Command `%s' failed with exit code: %d" % \
(self.command, exitcode))

self.stdout = stdout
self.stderr = stderr
Expand All @@ -51,10 +52,12 @@ def __init__(self, command, stdout, stderr, exitcode, timeout=0):


def __str__(self):
return '{command : "%s", stdout : "%s", stderr : "%s", ' \
'exitcode : %s, timeout : %d }' % \
(self.command, self.stdout, self.stderr,
self.exitcode, self.timeout)
ret = '\n' + super().__str__() + \
"\n=== STDOUT ===\n" + \
self.stdout + \
"\n=== STDERR ===\n" + \
self.stderr
return ret


class CompilationError(CommandError):
Expand Down
256 changes: 256 additions & 0 deletions reframe/core/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import logging
import os
import logging.handlers
import sys
import shutil

from datetime import datetime

from reframe.settings import settings
from reframe.core.exceptions import ConfigurationError, ReframeError

# Reframe's log levels
CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
VERBOSE = 19
DEBUG = 10
NOTSET = 0


_log_level_names = {
CRITICAL : 'critical',
ERROR : 'error',
WARNING : 'warning',
INFO : 'info',
VERBOSE : 'verbose',
DEBUG : 'debug',
NOTSET : 'undefined'
}

_log_level_values = {
'critical' : CRITICAL,
'error' : ERROR,
'warning' : WARNING,
'info' : INFO,
'verbose' : VERBOSE,
'debug' : DEBUG,
'undefined' : NOTSET,
'notset' : NOTSET
}

def _check_level(level):
if isinstance(level, int):
ret = level
elif isinstance(level, str):
norm_level = level.lower()
if norm_level not in _log_level_values:
raise ReframeError('logger level %s not available' % level)
else:
ret = _log_level_values[norm_level]
else:
raise TypeError('logger level %s not an int or a valid string' % level)

return ret


# Redefine handlers so as to use our levels

class Handler(logging.Handler):
def setLevel(self, level):
self.level = _check_level(level)


class StreamHandler(Handler, logging.StreamHandler):
pass


class RotatingFileHandler(Handler, logging.handlers.RotatingFileHandler):
pass


class FileHandler(Handler, logging.FileHandler):
pass


class NullHandler(Handler, logging.NullHandler):
pass


def load_from_dict(logging_config):
if not isinstance(logging_config, dict):
raise ConfigurationError('logging configuration is not a dict')

level = logging_config.get('level', 'info').lower()
handlers_dict = logging_config.get('handlers', None)

# if not handlers_dict:
# raise ConfigurationError('no entry for handlers was found')

logger = Logger('reframe')
logger.setLevel(_log_level_values[level])

for handler in _extract_handlers(handlers_dict):
logger.addHandler(handler)

return logger


def _extract_handlers(handlers_dict):
handlers = []
if not handlers_dict:
raise ConfigurationError('no handlers are defined for logger')

for filename, handler_config in handlers_dict.items():
if not isinstance(handler_config, dict):
raise ConfigurationError(
'handler %s is not a dictionary' % filename
)

level = handler_config.get('level', 'debug').lower()
fmt = handler_config.get('format', '%(message)s')
datefmt = handler_config.get('datefmt', '%FT%T')
append = handler_config.get('append', False)
timestamp = handler_config.get('timestamp', None)

if filename == '&1':
hdlr = StreamHandler(stream=sys.stdout)
elif filename == '&2':
hdlr = StreamHandler(stream=sys.stderr)
else:
if timestamp:
basename, ext = os.path.splitext(filename)
filename = '%s_%s%s' % (
basename, datetime.now().strftime(timestamp), ext
)

hdlr = RotatingFileHandler(
filename, mode='a+' if append else 'w+'
)

hdlr.setFormatter(logging.Formatter(fmt=fmt, datefmt=datefmt))
hdlr.setLevel(level)
handlers.append(hdlr)

return handlers


class Logger(logging.Logger):
def __init__(self, name, level=logging.NOTSET):
# We will set the logger level ourselves so as to bypass the base class'
# check
super().__init__(name, logging.NOTSET)
self.level = _check_level(level)
self.check = None


def setLevel(self, level):
self.level = _check_level(level)


def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
func=None, extra=None, sinfo=None):
# Setup dynamic fields of the check
if self.check and self.check.job:
extra['check_jobid'] = self.check.job.jobid

record = super().makeRecord(name, level, fn, lno, msg, args, exc_info,
func, extra, sinfo)
try:
# Fill in our name for the record
record.levelname = _log_level_names[level]
except KeyError:
# Go with the default level name of Python logging
pass

return record


# Override all the convenience logging functions, because we want to make
# sure that they map to our level definitions

def critical(self, msg, *args, **kwargs):
return self.log(CRITICAL, msg, *args, **kwargs)


def error(self, msg, *args, **kwargs):
return self.log(ERROR, msg, *args, **kwargs)


def warning(self, msg, *args, **kwargs):
return self.log(WARNING, msg, *args, **kwargs)


def info(self, msg, *args, **kwargs):
return self.log(INFO, msg, *args, **kwargs)


def verbose(self, message, *args, **kwargs):
self.log(VERBOSE, message, *args, **kwargs)


def debug(self, message, *args, **kwargs):
self.log(DEBUG, message, *args, **kwargs)


class LoggerAdapter(logging.LoggerAdapter):
def __init__(self, logger = None, check = None):
super().__init__(
logger,
{
'check_name' : check.name if check else 'reframe',
'check_jobid' : '-1'
}
)
if self.logger:
self.logger.check = check


def setLevel(self, level):
if self.logger:
super().setLevel(level)


# Override log() function to treat `None` loggers
def log(self, level, msg, *args, **kwargs):
if self.logger:
super().log(level, msg, *args, **kwargs)


def verbose(self, message, *args, **kwargs):
self.log(VERBOSE, message, *args, **kwargs)


# A logger that doesn't log anything
null_logger = LoggerAdapter()

_logger = None
_frontend_logger = null_logger

def configure_logging(config):
global _logger
global _frontend_logger

if config == None:
_logger = None
_frontend_logger = null_logger
return

_logger = load_from_dict(config)
_frontend_logger = LoggerAdapter(_logger)


def save_log_files(dest):
os.makedirs(dest, exist_ok=True)
for hdlr in _logger.handlers:
if isinstance(hdlr, logging.FileHandler):
shutil.copy(hdlr.baseFilename, dest, follow_symlinks=True)

def getlogger(logger_kind, *args, **kwargs):
if logger_kind == 'frontend':
return _frontend_logger
elif logger_kind == 'check':
return LoggerAdapter(_logger, *args, **kwargs)
else:
raise ReframeError('unknown kind of logger: %s' % logger_kind)
Loading

0 comments on commit d1ae0c4

Please sign in to comment.