Changeset View
Changeset View
Standalone View
Standalone View
swh/scheduler/cli.py
# Copyright (C) 2016-2018 The Software Heritage developers | # Copyright (C) 2016-2018 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import arrow | import arrow | ||||
import click | import click | ||||
import csv | import csv | ||||
import itertools | import itertools | ||||
import json | import json | ||||
import locale | import locale | ||||
import logging | import logging | ||||
import time | import time | ||||
import datetime | |||||
from swh.core import utils, config | from swh.core import utils, config | ||||
from . import compute_nb_tasks_from | from . import compute_nb_tasks_from | ||||
from .backend_es import SWHElasticSearchClient | from .backend_es import SWHElasticSearchClient | ||||
from . import get_scheduler, DEFAULT_CONFIG | from . import get_scheduler, DEFAULT_CONFIG | ||||
locale.setlocale(locale.LC_ALL, '') | locale.setlocale(locale.LC_ALL, '') | ||||
Show All 9 Lines | def convert(self, value, param, ctx): | ||||
return value | return value | ||||
DATETIME = DateTimeType() | DATETIME = DateTimeType() | ||||
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) | CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) | ||||
def pretty_print_list(list, indent): | def format_dict(d): | ||||
ret = {} | |||||
for k, v in d.items(): | |||||
if isinstance(v, (arrow.Arrow, datetime.date, datetime.datetime)): | |||||
v = arrow.get(v).format() | |||||
elif isinstance(v, dict): | |||||
v = format_dict(v) | |||||
ret[k] = v | |||||
return ret | |||||
def pretty_print_list(list, indent=0): | |||||
"""Pretty-print a list""" | """Pretty-print a list""" | ||||
return ''.join('%s%s\n' % (' ' * indent, item) for item in list) | return ''.join('%s%s\n' % (' ' * indent, item) for item in list) | ||||
def pretty_print_dict(dict, indent): | def pretty_print_dict(dict, indent=0): | ||||
"""Pretty-print a list""" | """Pretty-print a list""" | ||||
return ''.join('%s%s: %s\n' % | return ''.join('%s%s: %s\n' % | ||||
(' ' * indent, click.style(key, bold=True), value) | (' ' * indent, click.style(key, bold=True), value) | ||||
for key, value in dict.items()) | for key, value in dict.items()) | ||||
def pretty_print_run(run, indent=4): | |||||
fmt = ('{indent}{backend_id} [{status}]\n' | |||||
'{indent} scheduled: {scheduled} [{started}:{ended}]') | |||||
return fmt.format(indent=' '*indent, **format_dict(run)) | |||||
def pretty_print_task(task, full=False): | def pretty_print_task(task, full=False): | ||||
"""Pretty-print a task | """Pretty-print a task | ||||
If 'full' is True, also print the status and priority fields. | If 'full' is True, also print the status and priority fields. | ||||
""" | """ | ||||
next_run = arrow.get(task['next_run']) | next_run = arrow.get(task['next_run']) | ||||
lines = [ | lines = [ | ||||
'%s %s\n' % (click.style('Task', bold=True), task['id']), | '%s %s\n' % (click.style('Task', bold=True), task['id']), | ||||
▲ Show 20 Lines • Show All 255 Lines • ▼ Show 20 Lines | |||||
@click.option('--priority', '-P', default=None, multiple=True, | @click.option('--priority', '-P', default=None, multiple=True, | ||||
type=click.Choice(['all', 'low', 'normal', 'high']), | type=click.Choice(['all', 'low', 'normal', 'high']), | ||||
help='List tasks whose priority is PRIORITY.') | help='List tasks whose priority is PRIORITY.') | ||||
@click.option('--before', '-b', required=False, type=DATETIME, | @click.option('--before', '-b', required=False, type=DATETIME, | ||||
metavar='DATETIME', | metavar='DATETIME', | ||||
help='Limit to tasks supposed to run before the given date.') | help='Limit to tasks supposed to run before the given date.') | ||||
@click.option('--after', '-a', required=False, type=DATETIME, | @click.option('--after', '-a', required=False, type=DATETIME, | ||||
metavar='DATETIME', | metavar='DATETIME', | ||||
help='Limit to tasks supposed to run after the given date.') | help='Limit to tasks supposed to run after the given date.') | ||||
@click.option('--list-runs', '-r', is_flag=True, default=False, | |||||
vlorentz: `help='Also list past executions of each task.'` | |||||
help='Also list past executions of each task.') | |||||
@click.pass_context | @click.pass_context | ||||
def list_tasks(ctx, task_id, task_type, limit, status, policy, priority, | def list_tasks(ctx, task_id, task_type, limit, status, policy, priority, | ||||
before, after): | before, after, list_runs): | ||||
"""List tasks. | """List tasks. | ||||
""" | """ | ||||
scheduler = ctx.obj['scheduler'] | scheduler = ctx.obj['scheduler'] | ||||
if not scheduler: | if not scheduler: | ||||
raise ValueError('Scheduler class (local/remote) must be instantiated') | raise ValueError('Scheduler class (local/remote) must be instantiated') | ||||
if not task_type: | if not task_type: | ||||
task_type = [x['type'] for x in scheduler.get_task_types()] | task_type = [x['type'] for x in scheduler.get_task_types()] | ||||
Show All 10 Lines | if priority and 'all' in priority: | ||||
priority = None | priority = None | ||||
output = [] | output = [] | ||||
tasks = scheduler.search_tasks( | tasks = scheduler.search_tasks( | ||||
task_id=task_id, | task_id=task_id, | ||||
task_type=task_type, | task_type=task_type, | ||||
status=status, priority=priority, policy=policy, | status=status, priority=priority, policy=policy, | ||||
before=before, after=after, | before=before, after=after, | ||||
limit=limit) | limit=limit) | ||||
if list_runs: | |||||
runs = {t['id']: [] for t in tasks} | |||||
for r in scheduler.get_task_runs([task['id'] for task in tasks]): | |||||
Not Done Inline ActionsI'm not a huge fan of using the name run both for the boolean option and for storing the dictionary. vlorentz: I'm not a huge fan of using the name `run` both for the boolean option and for storing the… | |||||
Not Done Inline Actionsruns * vlorentz: `runs` * | |||||
Done Inline ActionsI generally agree with that, but in some cases (like here), I see no real harm in doing so. The boolean value of runs remains valid all the way along, and finding a variable name is way too hard :-) douardda: I generally agree with that, but in some cases (like here), I see no real harm in doing so. The… | |||||
runs[r['task']].append(r) | |||||
else: | |||||
runs = {} | |||||
output.append('Found %d tasks\n' % ( | output.append('Found %d tasks\n' % ( | ||||
len(tasks))) | len(tasks))) | ||||
for task in tasks: | for task in tasks: | ||||
output.append(pretty_print_task(task, full=True)) | output.append(pretty_print_task(task, full=True)) | ||||
if runs.get(task['id']): | |||||
output.append(click.style(' Executions:', bold=True)) | |||||
for run in runs[task['id']]: | |||||
output.append(pretty_print_run(run, indent=4)) | |||||
click.echo('\n'.join(output)) | click.echo('\n'.join(output)) | ||||
@task.command('respawn') | @task.command('respawn') | ||||
@click.argument('task-ids', required=True, nargs=-1) | @click.argument('task-ids', required=True, nargs=-1) | ||||
@click.option('--next-run', '-n', required=False, type=DATETIME, | @click.option('--next-run', '-n', required=False, type=DATETIME, | ||||
metavar='DATETIME', default=None, | metavar='DATETIME', default=None, | ||||
▲ Show 20 Lines • Show All 315 Lines • Show Last 20 Lines |
help='Also list past executions of each task.'