From 386dede4a8f1ba49342e1a9e7d7850aacea55418 Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Thu, 13 Apr 2017 11:13:15 +0100 Subject: [PATCH 1/3] command: added support for sub-commands --- wa/__init__.py | 2 +- wa/framework/command.py | 51 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/wa/__init__.py b/wa/__init__.py index d005ef27..a4e286d1 100644 --- a/wa/__init__.py +++ b/wa/__init__.py @@ -1,5 +1,5 @@ from wa.framework import pluginloader, log, signal -from wa.framework.command import Command +from wa.framework.command import Command, ComplexCommand, SubCommand from wa.framework.configuration import settings from wa.framework.configuration.core import Status from wa.framework.exception import HostError, JobError, InstrumentError, ConfigError diff --git a/wa/framework/command.py b/wa/framework/command.py index 443a54f0..ba0df09f 100644 --- a/wa/framework/command.py +++ b/wa/framework/command.py @@ -15,6 +15,7 @@ import textwrap +from wa.framework.exception import CommandError from wa.framework.plugin import Plugin from wa.framework.version import get_wa_version from wa.utils.doc import format_body @@ -30,7 +31,7 @@ def init_argument_parser(parser): return parser -class Command(Plugin): +class SubCommand(object): """ Defines a Workload Automation command. This will be executed from the command line as ``wa [args ...]``. This defines the name to be @@ -39,15 +40,14 @@ class Command(Plugin): command line arguments. """ - kind = "command" help = None usage = None description = None epilog = None formatter_class = None - def __init__(self, subparsers): - super(Command, self).__init__() + def __init__(self, logger, subparsers): + self.logger = logger self.group = subparsers desc = format_body(textwrap.dedent(self.description), 80) parser_params = dict(help=(self.help or self.description), usage=self.usage, @@ -79,3 +79,46 @@ class Command(Plugin): """ raise NotImplementedError() + + +class Command(Plugin, SubCommand): + """ + Defines a Workload Automation command. This will be executed from the + command line as ``wa [args ...]``. This defines the name to be + used when invoking wa, the code that will actually be executed on + invocation and the argument parser to be used to parse the reset of the + command line arguments. + + """ + kind = "command" + + def __init__(self, subparsers): + Plugin.__init__(self) + SubCommand.__init__(self, self.logger, subparsers) + + +class ComplexCommand(Command): + """ + A command that defines sub-commands. + + """ + + subcmd_classes = [] + + def __init__(self, subparsers): + self.subcommands = [] + super(ComplexCommand, self).__init__(subparsers) + + def initialize(self, context): + subparsers = self.parser.add_subparsers(dest='what', metavar='SUBCMD') + for subcmd_cls in self.subcmd_classes: + subcmd = subcmd_cls(self.logger, subparsers) + self.subcommands.append(subcmd) + + def execute(self, state, args): + for subcmd in self.subcommands: + if subcmd.name == args.what: + subcmd.execute(state, args) + break + else: + raise CommandError('Not a valid create parameter: {}'.format(args.name)) From f1eeff726fd8ecb4e1aa0b4311519ac34aca9543 Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Mon, 24 Apr 2017 10:43:21 +0100 Subject: [PATCH 2/3] TargetDescription: generate default config Added a method to TargetDescrition to generate a dict with the default config for that description. --- wa/framework/target/descriptor.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wa/framework/target/descriptor.py b/wa/framework/target/descriptor.py index 9e01ebac..a0820b7e 100644 --- a/wa/framework/target/descriptor.py +++ b/wa/framework/target/descriptor.py @@ -82,6 +82,14 @@ class TargetDescription(object): self._set('platform_params', platform_params) self._set('conn_params', conn_params) + def get_default_config(self): + param_attrs = ['target_params', 'platform_params', 'conn_params'] + config = {} + for pattr in param_attrs: + for n, p in getattr(self, pattr).itervalues(): + config[n] = p.default + return config + def _set(self, attr, vals): if vals is None: vals = {} From 8a9ca6fdfe89c740ebe909f7b1610e08dbb96d13 Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Mon, 24 Apr 2017 10:44:35 +0100 Subject: [PATCH 3/3] commands: added create ageanda command Added the crate command with a subcommand to generate an agenda based on a list of plugins. --- wa/commands/create.py | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 wa/commands/create.py diff --git a/wa/commands/create.py b/wa/commands/create.py new file mode 100644 index 00000000..cd25587b --- /dev/null +++ b/wa/commands/create.py @@ -0,0 +1,82 @@ +import sys +from collections import OrderedDict + +from wa import ComplexCommand, SubCommand, pluginloader +from wa.framework.target.descriptor import get_target_descriptions + +from wa.utils.serializer import yaml + + +class CreateAgendaSubcommand(SubCommand): + + name = 'agenda' + description = """ + Create an agenda with the specified extensions enabled. And parameters set + to their default values. + """ + + def initialize(self, context): + self.parser.add_argument('plugins', nargs='+', + help='Plugins to be added to the agendas') + self.parser.add_argument('-i', '--iterations', type=int, default=1, + help='Sets the number of iterations for all workloads') + self.parser.add_argument('-o', '--output', metavar='FILE', + help='Output file. If not specfied, STDOUT will be used instead.') + + def execute(self, state, args): + agenda = OrderedDict() + agenda['config'] = OrderedDict(instrumentation=[], result_processors=[]) + agenda['global'] = OrderedDict(iterations=args.iterations) + agenda['workloads'] = [] + target_desc = None + + targets = {td.name: td for td in get_target_descriptions()} + + for name in args.plugins: + if name in targets: + if target_desc is not None: + raise ConfigError('Specifying multiple devices: {} and {}'.format(target_desc.name, name)) + target_desc = targets[name] + agenda['config']['device'] = name + agenda['config']['device_config'] = target_desc.get_default_config() + continue + + extcls = pluginloader.get_plugin_class(name) + config = pluginloader.get_default_config(name) + + if extcls.kind == 'workload': + entry = OrderedDict() + entry['name'] = extcls.name + if name != extcls.name: + entry['label'] = name + entry['params'] = config + agenda['workloads'].append(entry) + else: + if extcls.kind == 'instrument': + agenda['config']['instrumentation'].append(name) + if extcls.kind == 'result_processor': + agenda['config']['result_processors'].append(name) + agenda['config'][name] = config + + if args.output: + wfh = open(args.output, 'w') + else: + wfh = sys.stdout + yaml.dump(agenda, wfh, indent=4, default_flow_style=False) + if args.output: + wfh.close() + + +class CreateCommand(ComplexCommand): + + name = 'create' + description = ''' + Used to create various WA-related objects (see positional arguments list + for what objects may be created).\n\nUse "wa create -h" for + object-specific arguments. + ''' + subcmd_classes = [ + #CreateWorkloadSubcommand, + #CreatePackageSubcommand, + CreateAgendaSubcommand, + ]