DRY things up
This change removes a lot of boilerplate and dead code through utils.py and the cli modules. A key change in the cli modules is that we can re-use the definition of `fields` from the list action in the show action, thereby ensuring that related commands return consistent information.
This commit is contained in:
@@ -20,26 +20,19 @@ from cliff.lister import Lister
|
||||
from cliff.show import ShowOne
|
||||
from ara import app, db, models, utils
|
||||
|
||||
FIELDS = (
|
||||
('ID',),
|
||||
('Name',),
|
||||
)
|
||||
|
||||
|
||||
class HostList(Lister):
|
||||
"""Returns a list of hosts"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostList, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = models.Host.query.all()
|
||||
|
||||
fields = (
|
||||
('ID',),
|
||||
('Name',),
|
||||
)
|
||||
|
||||
return ([field[0] for field in fields],
|
||||
[[utils.get_field_attr(host, field)
|
||||
for field in fields] for host in hosts])
|
||||
return utils.fields_from_iter(FIELDS, hosts)
|
||||
|
||||
|
||||
class HostShow(ShowOne):
|
||||
@@ -49,18 +42,14 @@ class HostShow(ShowOne):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostShow, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'host_id',
|
||||
metavar='<host-id>',
|
||||
help='Host to show',
|
||||
'host',
|
||||
metavar='<host>',
|
||||
help='Host name or id to show',
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
host = models.Host.query.get(parsed_args.host_id)
|
||||
|
||||
data = {
|
||||
'ID': host.id,
|
||||
'Name': host.name
|
||||
}
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
host = (models.Host.query
|
||||
.filter((models.Host.id == parsed_args.host) |
|
||||
(models.Host.name == parsed_args.host)).one())
|
||||
return utils.fields_from_object(FIELDS, host)
|
||||
|
||||
@@ -20,31 +20,29 @@ from cliff.lister import Lister
|
||||
from cliff.show import ShowOne
|
||||
from ara import app, db, models, utils
|
||||
|
||||
FIELDS = (
|
||||
('ID',),
|
||||
('Name',),
|
||||
('Playbook',),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
|
||||
class PlayList(Lister):
|
||||
"""Returns a list of plays"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PlayList, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
plays = (models.Play.query
|
||||
.join(models.Playbook)
|
||||
.filter(models.Play.playbook_id == models.Playbook.id))
|
||||
|
||||
fields = (
|
||||
('ID',),
|
||||
('Name',),
|
||||
('Playbook', 'playbook.path'),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
return ([field[0] for field in fields],
|
||||
[[utils.get_field_attr(play, field)
|
||||
for field in fields] for play in plays])
|
||||
return utils.fields_from_iter(
|
||||
FIELDS, plays,
|
||||
xforms={
|
||||
'Playbook': lambda p: p.path,
|
||||
})
|
||||
|
||||
|
||||
class PlayShow(ShowOne):
|
||||
@@ -62,14 +60,8 @@ class PlayShow(ShowOne):
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
play = models.Play.query.get(parsed_args.play_id)
|
||||
|
||||
playbook = "{0} ({1})".format(play.playbook.path, play.playbook_id)
|
||||
data = {
|
||||
'ID': play.id,
|
||||
'Name': play.name,
|
||||
'Playbook': playbook,
|
||||
'Time Start': play.time_start,
|
||||
'Time End': play.time_end
|
||||
}
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
return utils.fields_from_object(
|
||||
FIELDS, play,
|
||||
xforms={
|
||||
'Playbook': (lambda p: '{0} ({1})'.format(p.path, p.id)),
|
||||
})
|
||||
|
||||
@@ -20,28 +20,21 @@ from cliff.lister import Lister
|
||||
from cliff.show import ShowOne
|
||||
from ara import app, db, models, utils
|
||||
|
||||
FIELDS = (
|
||||
('ID',),
|
||||
('Path',),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
|
||||
class PlaybookList(Lister):
|
||||
"""Returns a list of playbooks"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PlaybookList, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
playbooks = models.Playbook.query.all()
|
||||
|
||||
fields = (
|
||||
('ID',),
|
||||
('Path',),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
return ([field[0] for field in fields],
|
||||
[[utils.get_field_attr(playbook, field)
|
||||
for field in fields] for playbook in playbooks])
|
||||
return utils.fields_from_iter(FIELDS, playbooks)
|
||||
|
||||
|
||||
class PlaybookShow(ShowOne):
|
||||
@@ -59,12 +52,4 @@ class PlaybookShow(ShowOne):
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
playbook = models.Playbook.query.get(parsed_args.playbook_id)
|
||||
|
||||
data = {
|
||||
'ID': playbook.id,
|
||||
'Path': playbook.path,
|
||||
'Time Start': playbook.time_start,
|
||||
'Time End': playbook.time_end
|
||||
}
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
return utils.fields_from_object(FIELDS, playbook)
|
||||
|
||||
@@ -20,15 +20,24 @@ from cliff.lister import Lister
|
||||
from cliff.show import ShowOne
|
||||
from ara import app, db, models, utils
|
||||
|
||||
FIELDS = (
|
||||
('ID',),
|
||||
('Host', 'host.name'),
|
||||
('Task',),
|
||||
('Changed',),
|
||||
('Failed',),
|
||||
('Skipped',),
|
||||
('Unreachable',),
|
||||
('Ignore Errors',),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
|
||||
class ResultList(Lister):
|
||||
"""Returns a list of results"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ResultList, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
results = (models.TaskResult.query
|
||||
.join(models.Task)
|
||||
@@ -36,22 +45,11 @@ class ResultList(Lister):
|
||||
.filter(models.TaskResult.task_id == models.Task.id)
|
||||
.filter(models.TaskResult.host_id == models.Host.id))
|
||||
|
||||
fields = (
|
||||
('ID',),
|
||||
('Host', 'host.name'),
|
||||
('Task', 'task.name'),
|
||||
('Changed',),
|
||||
('Failed',),
|
||||
('Skipped',),
|
||||
('Unreachable',),
|
||||
('Ignore Errors',),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
return ([field[0] for field in fields],
|
||||
[[utils.get_field_attr(result, field)
|
||||
for field in fields] for result in results])
|
||||
return utils.fields_from_iter(
|
||||
FIELDS, results,
|
||||
xforms={
|
||||
'Task': lambda t: t.name,
|
||||
})
|
||||
|
||||
|
||||
class ResultShow(ShowOne):
|
||||
@@ -69,21 +67,8 @@ class ResultShow(ShowOne):
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
result = models.TaskResult.query.get(parsed_args.result_id)
|
||||
|
||||
host = "{0} ({1})".format(result.host.name, result.host_id)
|
||||
task = "{0} ({1})".format(result.task.name, result.task_id)
|
||||
data = {
|
||||
'ID': result.id,
|
||||
'Host': host,
|
||||
'Task': task,
|
||||
'Changed': result.changed,
|
||||
'Failed': result.failed,
|
||||
'Skipped': result.skipped,
|
||||
'Unreachable': result.unreachable,
|
||||
'Ignore Errors': result.ignore_errors,
|
||||
'Time Start': result.time_start,
|
||||
'Time End': result.time_end,
|
||||
'Result': result.result
|
||||
}
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
return utils.fields_from_object(
|
||||
FIELDS, result,
|
||||
xforms={
|
||||
'Task': lambda t: '{0} ({1})'.format(t.name, t.id),
|
||||
})
|
||||
|
||||
@@ -20,6 +20,17 @@ from cliff.lister import Lister
|
||||
from cliff.show import ShowOne
|
||||
from ara import app, db, models, utils
|
||||
|
||||
FIELDS = (
|
||||
('ID',),
|
||||
('Host', 'host.name'),
|
||||
('Playbook',),
|
||||
('Changed',),
|
||||
('Failed',),
|
||||
('Ok',),
|
||||
('Skipped',),
|
||||
('Unreachable',),
|
||||
)
|
||||
|
||||
|
||||
class StatsList(Lister):
|
||||
"""Returns a list of statistics"""
|
||||
@@ -36,20 +47,11 @@ class StatsList(Lister):
|
||||
.filter(models.Stats.playbook_id == models.Playbook.id)
|
||||
.filter(models.Stats.host_id == models.Host.id))
|
||||
|
||||
fields = (
|
||||
('ID',),
|
||||
('Host', 'host.name'),
|
||||
('Playbook', 'playbook.path'),
|
||||
('Changed',),
|
||||
('Failed',),
|
||||
('Ok',),
|
||||
('Skipped',),
|
||||
('Unreachable',),
|
||||
)
|
||||
|
||||
return ([field[0] for field in fields],
|
||||
[[utils.get_field_attr(stat, field)
|
||||
for field in fields] for stat in stats])
|
||||
return utils.fields_from_iter(
|
||||
FIELDS, stats,
|
||||
xforms={
|
||||
'Playbook': lambda p: p.path,
|
||||
})
|
||||
|
||||
|
||||
class StatsShow(ShowOne):
|
||||
@@ -67,18 +69,8 @@ class StatsShow(ShowOne):
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
stats = models.Stats.query.get(parsed_args.stats_id)
|
||||
|
||||
host = "{0} ({1})".format(stats.host.name, stats.host_id)
|
||||
playbook = "{0} ({1})".format(stats.playbook.path, stats.playbook_id)
|
||||
data = {
|
||||
'ID': stats.id,
|
||||
'Host': host,
|
||||
'Playbook': playbook,
|
||||
'Changed': stats.changed,
|
||||
'Failed': stats.failed,
|
||||
'Ok': stats.ok,
|
||||
'Skipped': stats.skipped,
|
||||
'Unreachable': stats.unreachable
|
||||
}
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
return utils.fields_from_object(
|
||||
FIELDS, stats,
|
||||
xforms={
|
||||
'Playbook': (lambda p: '{0} ({1})'.format(p.path, p.id)),
|
||||
})
|
||||
|
||||
@@ -20,15 +20,23 @@ from cliff.lister import Lister
|
||||
from cliff.show import ShowOne
|
||||
from ara import app, db, models, utils
|
||||
|
||||
FIELDS = (
|
||||
('ID',),
|
||||
('Name',),
|
||||
('Path',),
|
||||
('Playbook',),
|
||||
('Play',),
|
||||
('Action',),
|
||||
('Line', 'lineno',),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
|
||||
class TaskList(Lister):
|
||||
"""Returns a list of tasks"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(TaskList, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
tasks = (models.Task.query
|
||||
.join(models.Play)
|
||||
@@ -36,21 +44,12 @@ class TaskList(Lister):
|
||||
.filter(models.Task.play_id == models.Play.id)
|
||||
.filter(models.Task.playbook_id == models.Playbook.id))
|
||||
|
||||
fields = (
|
||||
('ID',),
|
||||
('Name',),
|
||||
('Path',),
|
||||
('Playbook', 'playbook.path'),
|
||||
('Play', 'play.name'),
|
||||
('Action',),
|
||||
('Line', 'lineno',),
|
||||
('Time Start',),
|
||||
('Time End',),
|
||||
)
|
||||
|
||||
return ([field[0] for field in fields],
|
||||
[[utils.get_field_attr(task, field)
|
||||
for field in fields] for task in tasks])
|
||||
return utils.fields_from_iter(
|
||||
FIELDS, tasks,
|
||||
xforms={
|
||||
'Playbook': lambda p: p.path,
|
||||
'Play': lambda p: p.name,
|
||||
})
|
||||
|
||||
|
||||
class TaskShow(ShowOne):
|
||||
@@ -69,18 +68,9 @@ class TaskShow(ShowOne):
|
||||
def take_action(self, parsed_args):
|
||||
task = models.Task.query.get(parsed_args.task_id)
|
||||
|
||||
playbook = "{0} ({1})".format(task.playbook.path, task.playbook_id)
|
||||
play = "{0} ({1})".format(task.play.name, task.play_id)
|
||||
data = {
|
||||
'ID': task.id,
|
||||
'Name': task.name,
|
||||
'Path': task.path,
|
||||
'Playbook': playbook,
|
||||
'Play': play,
|
||||
'Action': task.action,
|
||||
'Line': task.lineno,
|
||||
'Time Start': task.time_start,
|
||||
'Time End': task.time_end
|
||||
}
|
||||
|
||||
return zip(*sorted(six.iteritems(data)))
|
||||
return utils.fields_from_object(
|
||||
FIELDS, task,
|
||||
xforms={
|
||||
'Playbook': lambda p: '{0} ({1})'.format(p.path, p.id),
|
||||
'Play': lambda p: '{0} ({1})'.format(p.name, p.id),
|
||||
})
|
||||
|
||||
91
ara/utils.py
91
ara/utils.py
@@ -19,21 +19,6 @@ from ara import app, models, db
|
||||
from flask import url_for, Markup
|
||||
|
||||
|
||||
# Jinja filters
|
||||
@app.template_filter('datetime')
|
||||
def jinja_date_formatter(timestamp, format='%Y-%m-%d-%H:%M:%S.%f'):
|
||||
""" Reformats a datetime timestamp from str(datetime.datetime)"""
|
||||
datetime_format = "%Y-%m-%d %H:%M:%S.%f"
|
||||
timestamp = datetime.datetime.strptime(timestamp, datetime_format)
|
||||
return timestamp.strftime(format)[:-3]
|
||||
|
||||
|
||||
@app.template_filter('seconds_to_duration')
|
||||
def jinja_seconds_to_duration(seconds):
|
||||
""" Reformats an amount of seconds for friendly output"""
|
||||
return str(datetime.timedelta(seconds=float(seconds)))[:-3]
|
||||
|
||||
|
||||
@app.template_filter('to_nice_json')
|
||||
def jinja_to_nice_json(result):
|
||||
""" Formats a result """
|
||||
@@ -67,12 +52,17 @@ def make_link(view, label, **kwargs):
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def add_markup_to_context():
|
||||
def ctx_add_utility_functions():
|
||||
'''Adds some utility functions to the template context.'''
|
||||
|
||||
return dict(make_link=make_link)
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def add_nav_data():
|
||||
def ctx_add_nav_data():
|
||||
'''Makes some standard data from the database available in the
|
||||
template context.'''
|
||||
|
||||
playbook_item_limit = app.config.get('NAV_MENU_MAX_PLAYBOOKS', 10)
|
||||
host_item_limit = app.config.get('NAV_MENU_MAX_HOSTS', 10)
|
||||
|
||||
@@ -98,6 +88,51 @@ def default_data():
|
||||
return data
|
||||
|
||||
|
||||
def fields_from_iter(fields, items, xforms=None):
|
||||
'''Returns column headers and data for use by
|
||||
`cliff.lister.Lister`. In this function and in
|
||||
`fields_from_object`, fields are specified as a list of
|
||||
`(column_name, object_path)` tuples. The `object_path` can be
|
||||
omitted if it can be inferred from the column name by converting
|
||||
the name to lowercase and converting ' ' to '_'. For example:
|
||||
|
||||
fields = (
|
||||
('ID',),
|
||||
('Name',),
|
||||
('Playbook',),
|
||||
)
|
||||
|
||||
The `xforms` parameter is a dictionary maps column names to
|
||||
callables that will be used to format the corresponding value.
|
||||
For example:
|
||||
|
||||
xforms = {
|
||||
'Playbook': lambda p: p.name,
|
||||
}
|
||||
'''
|
||||
|
||||
xforms = xforms or {}
|
||||
|
||||
return (zip(*fields)[0], [
|
||||
[xform(v) for v, xform in
|
||||
[(get_field_attr(item, f[0]), f[1]) for f in
|
||||
[(field, xforms.get(field[0], lambda x: x)) for field in fields]]]
|
||||
for item in items])
|
||||
|
||||
|
||||
def fields_from_object(fields, obj, xforms=None):
|
||||
'''Returns labels and values for use by `cliff.show.ShowOne`. See
|
||||
the documentation for `fields_from_iter` for details.'''
|
||||
|
||||
xforms = xforms or {}
|
||||
|
||||
return (zip(*fields)[0],
|
||||
[xform(v) for v, xform in
|
||||
[(get_field_attr(obj, f[0]), f[1]) for f in
|
||||
[(field, xforms.get(field[0], lambda x: x))
|
||||
for field in fields]]])
|
||||
|
||||
|
||||
def status_to_query(status=None):
|
||||
"""
|
||||
Returns a dict based on status
|
||||
@@ -145,9 +180,21 @@ def get_stats_for_playbooks(playbook_uuids, **kwargs):
|
||||
|
||||
|
||||
def get_field_attr(obj, field):
|
||||
if len(field) > 1:
|
||||
attribute = field[1]
|
||||
else:
|
||||
attribute = field[0].lower().replace(' ', '_')
|
||||
'''Returns the value of an attribute path applied to an object.
|
||||
The attribute path is either made available explicitly as
|
||||
`field[1]` or implicitly by converting `field[0]` to lower case
|
||||
and converting ' ' to '_'. In other words, given:
|
||||
|
||||
return reduce(getattr, attribute.split('.'), obj)
|
||||
field = ('Name',)
|
||||
|
||||
`get_field_attribute(obj, field)` would return the value of the
|
||||
`name` attribute of `obj`. On other hand, given:
|
||||
|
||||
field = ('Name', 'playbook.name')
|
||||
|
||||
`get_field_attribute(obj, field)` would return the value of the
|
||||
`name` attribute of the `playbook` attribute of `obj`.
|
||||
'''
|
||||
|
||||
path = field[-1].lower().replace(' ', '_').split('.')
|
||||
return reduce(getattr, path, obj)
|
||||
|
||||
Reference in New Issue
Block a user