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:
Lars Kellogg-Stedman
2016-05-12 21:32:50 -04:00
parent 3be8fb3e2e
commit 2f61ebbfa8
7 changed files with 177 additions and 197 deletions

View File

@@ -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)

View File

@@ -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)),
})

View File

@@ -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)

View File

@@ -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),
})

View File

@@ -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)),
})

View File

@@ -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),
})

View File

@@ -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)