Skip to content
Merged
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ interactive: True
# Runs the command in reporting mode. The standard output of the remote command will
# be dumped to the screen instead of the exit code.
report: True

# The following list forms the arguments for the command line. They are set as
# bash shell variables when running the command. This allows them to be used
# within the command
# NOTE: this key is optional
arguments:
- arg1
- arg2
...
```

# Configuring Run Parameters
Expand Down
2 changes: 1 addition & 1 deletion src/appliance_cli/command_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def add_argument(name):
is_required = False
for n in arg_n: add_argument(n)
else:
add_argument(arg_name)
add_argument(arg_n)
return args_map


Expand Down
64 changes: 41 additions & 23 deletions src/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def run():

runner_cmd = {
'help': Config.help,
**run_options
**run_options,
'arguments': Config.args
}
runner_group = {
'help': (lambda names: "Run tools in {}".format(' '.join(names))),
Expand All @@ -44,7 +45,7 @@ def run():
@command_creator.tools(run, command = runner_cmd, group = runner_group)
@cli_utils.with__node__group
@cli_utils.ignore_parent_commands
def runner(_ctx, configs, _a, options, nodes):
def runner(_ctx, configs, argv, options, nodes):
def error_if_invalid_interactive_batch():
matches = [c for c in configs if c.interactive()]
if matches and (len(configs) > 1 or len(nodes) > 1):
Expand All @@ -66,13 +67,43 @@ def is_quiet():
if first.interactive() or first.report: return True
else: return False

def argument_hash(config):
keys = config.args()
arg_hash = {}
for index, value in enumerate(argv):
arg_hash[keys[index]] = value
return arg_hash

def build_batches():
def build(config):
batch = Batch(config = config.path)
batch.build_jobs(*nodes)
batch.build_shell_variables(**argument_hash(config))
return batch
return list(map(lambda c: build(c), configs))

def get_confirmation(batches, nodes):
tool_names = '\n '.join([b.config_model.name() for b in batches])
node_names = groups_util.compress_nodes(nodes).replace('],', ']\n ')
node_tag = 'node' if len(nodes) == 1 else 'nodes'
click.echo("""
You are about to run:
{}
Over {}:
{}
""".strip().format(tool_names, node_tag, node_names))
question = "Please enter [y/n] to confirm"
affirmatives = ['y', 'ye', 'yes']
reply = click.prompt(question).lower()
if reply in affirmatives: return True

error_if_no_nodes()
batches = list(map(lambda c: Batch(config = c.path), configs))
error_if_invalid_interactive_batch()
if not (options['yes'].value or get_confirmation(batches, nodes)):
return
for batch in batches: batch.build_jobs(*nodes)
execute_batches(batches, quiet = is_quiet())

batches = build_batches()

if (options['yes'].value or get_confirmation(batches, nodes)):
execute_batches(batches, quiet = is_quiet())

def execute_batches(batches, quiet = False):
def run_print(string):
Expand Down Expand Up @@ -116,6 +147,9 @@ def remove_done_tasks():
for batch in batches:
session.add(batch)
session.commit()
# Ensure the models are loaded from the db
batch.jobs
batch.shell_variables
run_print('Executing: {}'.format(batch.name()))
tasks = map(lambda j: j.task(thread_pool = pool), batch.jobs)
loop.run_until_complete(start_tasks(tasks))
Expand All @@ -127,19 +161,3 @@ def remove_done_tasks():
session.commit()
Session.remove()
run_print('Done')

def get_confirmation(batches, nodes):
tool_names = '\n '.join([b.config_model.name() for b in batches])
node_names = groups_util.compress_nodes(nodes).replace('],', ']\n ')
node_tag = 'node' if len(nodes) == 1 else 'nodes'
click.echo("""
You are about to run:
{}
Over {}:
{}
""".strip().format(tool_names, node_tag, node_names))
question = "Please enter [y/n] to confirm"
affirmatives = ['y', 'ye', 'yes']
reply = click.prompt(question).lower()
if reply in affirmatives:
return True
15 changes: 14 additions & 1 deletion src/models/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from database import Base
from models.config import Config
from models.job import Job
from models.shell_variable import ShellVariable

class Batch(Base):

Expand All @@ -21,7 +22,12 @@ def help(self):
return self.config_model.help()

def command(self):
return self.config_model.command()
var_models = self.shell_variables
var_map = map(lambda v: '='.join([v.key, v.value]), var_models)
var_str = ' && '.join(var_map)
cmd = self.config_model.command()
if var_str: cmd = ' && '.join([var_str, cmd])
return cmd

def command_exists(self):
return self.config_model.command_exists()
Expand All @@ -31,3 +37,10 @@ def is_interactive(self):

def build_jobs(self, *nodes):
return list(map(lambda n: Job(node = n, batch = self), nodes))

def build_shell_variables(self, **variables):
def build(key):
args = { 'key': key, 'value': variables[key], 'batch': self }
return ShellVariable(**args)

return list(map(lambda k: build(k), variables.keys()))
3 changes: 3 additions & 0 deletions src/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ def help(self):
if not self.data['help']: self.data['help'] = default
return self.data['help']

def args(self):
return self.arguments or []

# TODO: Deprecated, avoid usage
def interactive_only(self):
return self.interactive()
Expand Down
24 changes: 24 additions & 0 deletions src/models/shell_variable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship, validates

from database import Base

import click

class ShellVariable(Base):


key = Column(String)
value = Column(String)
batch_id = Column(Integer, ForeignKey('batches.id'))
batch = relationship("Batch", backref="shell_variables")


@validates('value')
def validate_value(self, _, value):
try:
assert value.isalnum()
return value
except AssertionError:
raise click.ClickException('The arguments must be alphanumeric')