1
0
mirror of https://github.com/ARM-software/workload-automation.git synced 2024-10-05 18:31:12 +01:00

Fixing things up to a point where "list" and "show" commands work.

This commit is contained in:
Sergei Trofimov 2017-02-09 09:09:00 +00:00
parent 92aeca8125
commit dc6d9676f2
15 changed files with 115 additions and 149 deletions

View File

@ -78,7 +78,7 @@ params = dict(
'pyYAML', # YAML-formatted agenda parsing 'pyYAML', # YAML-formatted agenda parsing
'requests', # Fetch assets over HTTP 'requests', # Fetch assets over HTTP
'devlib', # Interacting with devices 'devlib', # Interacting with devices
'louie' # Handles signal callbacks 'louie' # callbacks dispatch
], ],
extras_require={ extras_require={
'other': ['jinja2', 'pandas>=0.13.1'], 'other': ['jinja2', 'pandas>=0.13.1'],

View File

@ -22,9 +22,9 @@ import wlauto
from wlauto import Command, settings from wlauto import Command, settings
from wlauto.core.execution import Executor from wlauto.core.execution import Executor
from wlauto.utils.log import add_log_file from wlauto.utils.log import add_log_file
from wlauto.core.configuration import RunConfiguration, WAConfiguration from wlauto.core.configuration import RunConfiguration
from wlauto.core import pluginloader from wlauto.core import pluginloader
from wlauto.core.configuration_parsers import Agenda, ConfigFile, EnvrironmentVars, CommandLineArgs from wlauto.core.configuration.parsers import AgendaParser, ConfigParser, CommandLineArgsParser
class RunCommand(Command): class RunCommand(Command):
@ -74,7 +74,7 @@ class RunCommand(Command):
# STAGE 1: Gather configuratation # STAGE 1: Gather configuratation
env = EnvrironmentVars() env = EnvironmentVars()
args = CommandLineArgs(args) args = CommandLineArgs(args)
# STAGE 2.1a: Early WAConfiguration, required to find config files # STAGE 2.1a: Early WAConfiguration, required to find config files

View File

@ -21,7 +21,7 @@ from wlauto.core.version import get_wa_version
def init_argument_parser(parser): def init_argument_parser(parser):
parser.add_argument('-c', '--config', help='specify an additional config.py', action='append') parser.add_argument('-c', '--config', help='specify an additional config.py', action='append', default=[])
parser.add_argument('-v', '--verbose', action='count', parser.add_argument('-v', '--verbose', action='count',
help='The scripts will produce verbose output.') help='The scripts will produce verbose output.')
parser.add_argument('--version', action='version', version='%(prog)s {}'.format(get_wa_version())) parser.add_argument('--version', action='version', version='%(prog)s {}'.format(get_wa_version()))

View File

@ -13,7 +13,6 @@
# limitations under the License. # limitations under the License.
# #
from wlauto.core.configuration.configuration import (settings, from wlauto.core.configuration.configuration import (settings,
WAConfiguration,
RunConfiguration, RunConfiguration,
JobGenerator, JobGenerator,
ConfigurationPoint) ConfigurationPoint)

View File

@ -492,36 +492,11 @@ class CpuFreqParameters(object):
class Configuration(object): class Configuration(object):
config_points = [] config_points = []
name = "" name = ''
# The below line must be added to all subclasses # The below line must be added to all subclasses
configuration = {cp.name: cp for cp in config_points} configuration = {cp.name: cp for cp in config_points}
def __init__(self):
# Load default values for configuration points
for confpoint in self.configuration.itervalues():
confpoint.set_value(self, check_mandatory=False)
def set(self, name, value, check_mandatory=True):
if name not in self.configuration:
raise ConfigError('Unknown {} configuration "{}"'.format(self.name, name))
self.configuration[name].set_value(self, value, check_mandatory=check_mandatory)
def update_config(self, values, check_mandatory=True):
for k, v in values.iteritems():
self.set(k, v, check_mandatory=check_mandatory)
def validate(self):
for cfg_point in self.configuration.itervalues():
cfg_point.validate(self)
def to_pod(self):
pod = {}
for cfg_point_name in self.configuration.iterkeys():
value = getattr(self, cfg_point_name, None)
if value is not None:
pod[cfg_point_name] = value
return pod
@classmethod @classmethod
# pylint: disable=unused-argument # pylint: disable=unused-argument
def from_pod(cls, pod, plugin_cache): def from_pod(cls, pod, plugin_cache):
@ -535,11 +510,46 @@ class Configuration(object):
instance.validate() instance.validate()
return instance return instance
def __init__(self):
for confpoint in self.config_points:
confpoint.set_value(self, check_mandatory=False)
def set(self, name, value, check_mandatory=True):
if name not in self.configuration:
raise ConfigError('Unknown {} configuration "{}"'.format(self.name, name))
self.configuration[name].set_value(self, value, check_mandatory=check_mandatory)
def update_config(self, values, check_mandatory=True):
for k, v in values.iteritems():
self.set(k, v, check_mandatory=check_mandatory)
def validate(self):
for cfg_point in self.config_points:
cfg_point.validate(self)
def to_pod(self):
pod = {}
for cfg_point_name in self.configuration.iterkeys():
value = getattr(self, cfg_point_name, None)
if value is not None:
pod[cfg_point_name] = value
return pod
# This configuration for the core WA framework # This configuration for the core WA framework
class WAConfiguration(Configuration): class WAConfiguration(Configuration):
name = "WA Configuration" name = "WA Configuration"
plugin_packages = [
'wlauto.commands',
'wlauto.workloads',
'wlauto.instrumentation',
'wlauto.result_processors',
'wlauto.managers',
'wlauto.resource_getters',
]
config_points = [ config_points = [
ConfigurationPoint( ConfigurationPoint(
'user_directory', 'user_directory',
@ -550,48 +560,6 @@ class WAConfiguration(Configuration):
kind=str, kind=str,
default=os.path.join(os.path.expanduser('~'), '.workload_automation'), default=os.path.join(os.path.expanduser('~'), '.workload_automation'),
), ),
ConfigurationPoint(
'plugin_packages',
kind=list_of_strings,
default=[
'wlauto.commands',
'wlauto.workloads',
'wlauto.instrumentation',
'wlauto.result_processors',
'wlauto.managers',
'wlauto.resource_getters',
],
description="""
List of packages that will be scanned for WA plugins.
""",
),
ConfigurationPoint(
'plugin_paths',
kind=list_of_strings,
default=[
'workloads',
'instruments',
'targets',
'processors',
# Legacy
'managers',
'result_processors',
],
description="""
List of paths that will be scanned for WA plugins.
""",
merge=True
),
ConfigurationPoint(
'plugin_ignore_paths',
kind=list_of_strings,
default=[],
description="""
List of (sub)paths that will be ignored when scanning
``plugin_paths`` for WA plugins.
""",
),
ConfigurationPoint( ConfigurationPoint(
'assets_repository', 'assets_repository',
description=""" description="""
@ -623,7 +591,7 @@ class WAConfiguration(Configuration):
Verbosity of console output. Verbosity of console output.
""", """,
), ),
ConfigurationPoint( # TODO: Needs some format for dates ect/ comes from cfg ConfigurationPoint( # TODO: Needs some format for dates etc/ comes from cfg
'default_output_directory', 'default_output_directory',
default="wa_output", default="wa_output",
description=""" description="""
@ -636,7 +604,19 @@ class WAConfiguration(Configuration):
@property @property
def dependencies_directory(self): def dependencies_directory(self):
return "{}/dependencies/".format(self.user_directory) return os.path.join(self.user_directory, 'dependencies')
@property
def plugins_directory(self):
return os.path.join(self.user_directory, 'plugins')
def __init__(self, environ):
super(WAConfiguration, self).__init__()
user_directory = environ.pop('WA_USER_DIRECTORY', '')
if user_directory:
self.set('user_directory', user_directory)
# This is generic top-level configuration for WA runs. # This is generic top-level configuration for WA runs.
@ -1029,4 +1009,4 @@ class JobGenerator(object):
yield job_spec yield job_spec
settings = WAConfiguration() settings = WAConfiguration(os.environ)

View File

@ -283,23 +283,11 @@ class AgendaParser(object):
raise ConfigError('Error in "{}":\n\t{}'.format(source, str(e))) raise ConfigError('Error in "{}":\n\t{}'.format(source, str(e)))
class EnvironmentVarsParser(object):
def __init__(self, wa_config, environ):
user_directory = environ.pop('WA_USER_DIRECTORY', '')
if user_directory:
wa_config.set('user_directory', user_directory)
plugin_paths = environ.pop('WA_PLUGIN_PATHS', '')
if plugin_paths:
wa_config.set('plugin_paths', plugin_paths.split(os.pathsep))
ext_paths = environ.pop('WA_EXTENSION_PATHS', '')
if ext_paths:
wa_config.set('plugin_paths', ext_paths.split(os.pathsep))
# Command line options are parsed in the "run" command. This is used to send # Command line options are parsed in the "run" command. This is used to send
# certain arguments to the correct configuration points and keep a record of # certain arguments to the correct configuration points and keep a record of
# how WA was invoked # how WA was invoked
class CommandLineArgsParser(object): class CommandLineArgsParser(object):
def __init__(self, cmd_args, wa_config, jobs_config): def __init__(self, cmd_args, wa_config, jobs_config):
wa_config.set("verbosity", cmd_args.verbosity) wa_config.set("verbosity", cmd_args.verbosity)
# TODO: Is this correct? Does there need to be a third output dir param # TODO: Is this correct? Does there need to be a third output dir param

View File

@ -24,6 +24,7 @@ import warnings
from wlauto.core.configuration import settings from wlauto.core.configuration import settings
from wlauto.core import pluginloader from wlauto.core import pluginloader
from wlauto.core.command import init_argument_parser from wlauto.core.command import init_argument_parser
from wlauto.core.host import init_user_directory
from wlauto.exceptions import WAError, ConfigError from wlauto.exceptions import WAError, ConfigError
from wlauto.utils.misc import get_traceback from wlauto.utils.misc import get_traceback
from wlauto.utils.log import init_logging from wlauto.utils.log import init_logging
@ -45,7 +46,11 @@ def load_commands(subparsers):
def main(): def main():
if not os.path.exists(settings.user_directory):
init_user_directory()
try: try:
description = ("Execute automated workloads on a remote device and process " description = ("Execute automated workloads on a remote device and process "
"the resulting output.\n\nUse \"wa <subcommand> -h\" to see " "the resulting output.\n\nUse \"wa <subcommand> -h\" to see "
"help for individual subcommands.") "help for individual subcommands.")
@ -57,10 +62,7 @@ def main():
commands = load_commands(parser.add_subparsers(dest='command')) # each command will add its own subparser commands = load_commands(parser.add_subparsers(dest='command')) # each command will add its own subparser
args = parser.parse_args() args = parser.parse_args()
#TODO: Set this stuff properly, i.e dont use settings (if possible) settings.set("verbosity", args.verbose)
#settings.set("verbosity", args.verbose)
#settings.load_user_config()
#settings.debug = args.debug
for config in args.config: for config in args.config:
if not os.path.exists(config): if not os.path.exists(config):

33
wlauto/core/host.py Normal file
View File

@ -0,0 +1,33 @@
import os
from wlauto.core.configuration import settings
def init_user_directory(overwrite_existing=False): # pylint: disable=R0914
"""
Initialise a fresh user directory.
"""
if os.path.exists(settings.user_directory):
if not overwrite_existing:
raise RuntimeError('Environment {} already exists.'.format(settings.user_directory))
shutil.rmtree(settings.user_directory)
os.makedirs(settings.user_directory)
os.makedirs(settings.dependencies_directory)
os.makedirs(settings.plugins_directory)
# TODO: generate default config.yaml here
if os.getenv('USER') == 'root':
# If running with sudo on POSIX, change the ownership to the real user.
real_user = os.getenv('SUDO_USER')
if real_user:
import pwd # done here as module won't import on win32
user_entry = pwd.getpwnam(real_user)
uid, gid = user_entry.pw_uid, user_entry.pw_gid
os.chown(settings.user_directory, uid, gid)
# why, oh why isn't there a recusive=True option for os.chown?
for root, dirs, files in os.walk(settings.user_directory):
for d in dirs:
os.chown(os.path.join(root, d), uid, gid)
for f in files:
os.chown(os.path.join(root, f), uid, gid)

View File

@ -25,13 +25,14 @@ from collections import OrderedDict, defaultdict
from itertools import chain from itertools import chain
from copy import copy from copy import copy
from wlauto.exceptions import NotFoundError, LoaderError, ValidationError, ConfigError from wlauto.exceptions import NotFoundError, LoaderError, ValidationError, ConfigError, HostError
from wlauto.utils.misc import (ensure_directory_exists as _d, from wlauto.utils.misc import (ensure_directory_exists as _d,
walk_modules, load_class, merge_dicts_simple, get_article) walk_modules, load_class, merge_dicts_simple, get_article)
from wlauto.core.configuration import settings from wlauto.core.configuration import settings
from wlauto.utils.types import identifier, boolean from wlauto.utils.types import identifier, boolean
from wlauto.core.configuration.configuration import ConfigurationPoint as Parameter from wlauto.core.configuration.configuration import ConfigurationPoint as Parameter
MODNAME_TRANS = string.maketrans(':/\\.', '____') MODNAME_TRANS = string.maketrans(':/\\.', '____')
@ -697,10 +698,9 @@ class PluginLoader(object):
for package in packages: for package in packages:
for module in walk_modules(package): for module in walk_modules(package):
self._discover_in_module(module) self._discover_in_module(module)
except ImportError as e: except HostError as e:
source = getattr(e, 'path', package)
message = 'Problem loading plugins from {}: {}' message = 'Problem loading plugins from {}: {}'
raise LoaderError(message.format(source, e.message)) raise LoaderError(message.format(e.module, str(e.orig_exc)))
def _discover_from_paths(self, paths, ignore_paths): def _discover_from_paths(self, paths, ignore_paths):
paths = paths or [] paths = paths or []

View File

@ -38,8 +38,7 @@ class __LoaderWrapper(object):
from wlauto.core.plugin import PluginLoader from wlauto.core.plugin import PluginLoader
from wlauto.core.configuration import settings from wlauto.core.configuration import settings
self._loader = PluginLoader(settings.plugin_packages, self._loader = PluginLoader(settings.plugin_packages,
settings.plugin_paths, [settings.plugins_directory], [])
settings.plugin_ignore_paths)
def update(self, packages=None, paths=None, ignore_paths=None): def update(self, packages=None, paths=None, ignore_paths=None):
if not self._loader: if not self._loader:

View File

@ -14,7 +14,9 @@
# #
from wlauto.utils.misc import get_traceback, TimeoutError # NOQA pylint: disable=W0611 from wlauto.utils.misc import get_traceback
from devlib.exception import DevlibError, HostError, TargetError, TimeoutError
class WAError(Exception): class WAError(Exception):

View File

@ -8,8 +8,8 @@ from mock.mock import Mock, MagicMock, call
from wlauto.exceptions import ConfigError from wlauto.exceptions import ConfigError
from wlauto.core.configuration.parsers import * # pylint: disable=wildcard-import from wlauto.core.configuration.parsers import * # pylint: disable=wildcard-import
from wlauto.core.configuration.parsers import _load_file, _collect_valid_id, _resolve_params_alias from wlauto.core.configuration.parsers import _load_file, _collect_valid_id, _resolve_params_alias
from wlauto.core.configuration import (WAConfiguration, RunConfiguration, JobGenerator, from wlauto.core.configuration import RunConfiguration, JobGenerator, PluginCache, ConfigurationPoint
PluginCache, ConfigurationPoint) from wlauto.core.configuration.configuration import WAConfiguration
from wlauto.utils.types import toggle_set, reset_counter from wlauto.utils.types import toggle_set, reset_counter
@ -125,9 +125,6 @@ class TestFunctions(TestCase):
with self.assertRaises(ConfigError): with self.assertRaises(ConfigError):
_resolve_params_alias(test, "new_name") _resolve_params_alias(test, "new_name")
def test_construct_valid_entry(self):
raise Exception()
class TestConfigParser(TestCase): class TestConfigParser(TestCase):
@ -362,44 +359,6 @@ class TestAgendaParser(TestCase):
assert_equal(workload['workload_name'], "test") assert_equal(workload['workload_name'], "test")
class TestEnvironmentVarsParser(TestCase):
def test_environmentvarsparser(self):
wa_config = Mock(spec=WAConfiguration)
calls = [call('user_directory', '/testdir'),
call('plugin_paths', ['/test', '/some/other/path', '/testy/mc/test/face'])]
# Valid env vars
valid_environ = {"WA_USER_DIRECTORY": "/testdir",
"WA_PLUGIN_PATHS": "/test:/some/other/path:/testy/mc/test/face"}
EnvironmentVarsParser(wa_config, valid_environ)
wa_config.set.assert_has_calls(calls)
# Alternative env var name
wa_config.reset_mock()
alt_valid_environ = {"WA_USER_DIRECTORY": "/testdir",
"WA_EXTENSION_PATHS": "/test:/some/other/path:/testy/mc/test/face"}
EnvironmentVarsParser(wa_config, alt_valid_environ)
wa_config.set.assert_has_calls(calls)
# Test that WA_EXTENSION_PATHS gets merged with WA_PLUGIN_PATHS.
# Also checks that other enviroment variables don't cause errors
wa_config.reset_mock()
calls = [call('user_directory', '/testdir'),
call('plugin_paths', ['/test', '/some/other/path']),
call('plugin_paths', ['/testy/mc/test/face'])]
ext_and_plgin = {"WA_USER_DIRECTORY": "/testdir",
"WA_PLUGIN_PATHS": "/test:/some/other/path",
"WA_EXTENSION_PATHS": "/testy/mc/test/face",
"RANDOM_VAR": "random_value"}
EnvironmentVarsParser(wa_config, ext_and_plgin)
# If any_order=True then the calls can be in any order, but they must all appear
wa_config.set.assert_has_calls(calls, any_order=True)
# No WA enviroment variables present
wa_config.reset_mock()
EnvironmentVarsParser(wa_config, {"RANDOM_VAR": "random_value"})
wa_config.set.assert_not_called()
class TestCommandLineArgsParser(TestCase): class TestCommandLineArgsParser(TestCase):

View File

@ -492,7 +492,7 @@ def merge_config_values(base, other):
are treated as atomic, and not mergeable. are treated as atomic, and not mergeable.
s: A sequence. Anything iterable that is not a dict or s: A sequence. Anything iterable that is not a dict or
a string (strings are considered scalars). a string (strings are considered scalars).
m: A key-value mapping. ``dict`` and it's derivatives. m: A key-value mapping. ``dict`` and its derivatives.
n: ``None``. n: ``None``.
o: A mergeable object; this is an object that implements both o: A mergeable object; this is an object that implements both
``merge_with`` and ``merge_into`` methods. ``merge_with`` and ``merge_into`` methods.

View File

@ -51,7 +51,7 @@ import yaml as _yaml
import dateutil.parser import dateutil.parser
from wlauto.exceptions import SerializerSyntaxError from wlauto.exceptions import SerializerSyntaxError
from wlauto.utils.types import regex_type from wlauto.utils.types import regex_type, none_type
from wlauto.utils.misc import isiterable from wlauto.utils.misc import isiterable
@ -70,12 +70,14 @@ POD_TYPES = [
tuple, tuple,
dict, dict,
set, set,
basestring, str,
unicode,
int, int,
float, float,
bool, bool,
datetime, datetime,
regex_type regex_type,
none_type,
] ]
class WAJSONEncoder(_json.JSONEncoder): class WAJSONEncoder(_json.JSONEncoder):
@ -257,3 +259,4 @@ def _read_pod(fh, fmt=None):
def is_pod(obj): def is_pod(obj):
return type(obj) in POD_TYPES return type(obj) in POD_TYPES

View File

@ -169,6 +169,7 @@ list_or_bool = list_or(boolean)
regex_type = type(re.compile('')) regex_type = type(re.compile(''))
none_type = type(None)
def regex(value): def regex(value):