diff --git a/swh/scheduler/backend.py b/swh/scheduler/backend.py --- a/swh/scheduler/backend.py +++ b/swh/scheduler/backend.py @@ -252,7 +252,7 @@ task_keys = task_create_keys + ['id', 'current_interval', 'status'] @autocommit - def create_tasks(self, tasks, cursor=None): + def create_tasks(self, tasks, policy='recurring', cursor=None): """Create new tasks. Args: @@ -274,7 +274,7 @@ cursor.execute('select swh_scheduler_mktemp_task()') self.copy_to(tasks, 'tmp_task', self.task_create_keys, default_columns={ - 'policy': 'recurring', + 'policy': policy, 'status': 'next_run_not_scheduled' }, cursor=cursor) diff --git a/swh/scheduler/cli.py b/swh/scheduler/cli.py --- a/swh/scheduler/cli.py +++ b/swh/scheduler/cli.py @@ -68,6 +68,15 @@ return ''.join(lines) +def list_task_types(ctx, param, value): + if not value or ctx.resilient_parsing: + return + click.echo("Known task types:") + for tasktype in ctx.obj.get_task_types(): + click.echo('{type}:\n {description}'.format(**tasktype)) + ctx.exit() + + @click.group(context_settings=CONTEXT_SETTINGS) @click.option('--cls', '-c', default='local', help="Scheduler's class, default to 'local'") @@ -102,6 +111,8 @@ @cli.group('task') +@click.option('--list-types', '-l', is_flag=True, default=False, is_eager=True, + expose_value=False, callback=list_task_types) @click.pass_context def task(ctx): """Manipulate tasks.""" @@ -173,15 +184,57 @@ click.echo_via_pager('\n'.join(output)) +@task.command('add') +@click.argument('type', nargs=1, required=True) +@click.argument('options', nargs=-1) +@click.option('--policy', '-p', default='recurring', + type=click.Choice(['recurring', 'oneshot'])) +@click.option('--next-run', '-n', default=None) +@click.pass_context +def schedule_task(ctx, type, options, policy, next_run): + """Schedule one task from arguments. + + Use sample: + + swh-scheduler --database 'service=swh-scheduler' \ + task add swh-lister-pypi + + swh-scheduler --database 'service=swh-scheduler' \ + task add swh-lister-debian --policy=oneshot distribution=stretch + + """ + now = arrow.utcnow() + + args = [x for x in options if '=' not in x] + kw = dict(x.split('=', 1) for x in options if '=' in x) + task = {'type': type, + 'policy': policy, + 'arguments': { + 'args': args, + 'kwargs': kw, + }, + 'next_run': DATETIME.convert(next_run or now, + None, None), + } + created = ctx.obj.create_tasks([task]) + + output = [ + 'Created %d tasks\n' % len(created), + ] + for task in created: + output.append(pretty_print_task(task)) + + click.echo('\n'.join(output)) + + @task.command('list-pending') -@click.option('--task-type', '-t', required=True, - help='The tasks\' type concerned by the listing') +@click.argument('task-types', required=True, nargs=-1) @click.option('--limit', '-l', required=False, type=click.INT, help='The maximum number of tasks to fetch') @click.option('--before', '-b', required=False, type=DATETIME, help='List all jobs supposed to run before the given date') @click.pass_context -def list_pending_tasks(ctx, task_type, limit, before): +def list_pending_tasks(ctx, task_types, limit, before): """List the tasks that are going to be run. You can override the number of tasks to fetch @@ -189,16 +242,18 @@ """ num_tasks, num_tasks_priority = compute_nb_tasks_from(limit) - pending = ctx.obj.peek_ready_tasks( - task_type, timestamp=before, - num_tasks=num_tasks, num_tasks_priority=num_tasks_priority) - output = [ - 'Found %d tasks\n' % len(pending) - ] - for task in pending: - output.append(pretty_print_task(task)) + output = [] + for task_type in task_types: + pending = ctx.obj.peek_ready_tasks( + task_type, timestamp=before, + num_tasks=num_tasks, num_tasks_priority=num_tasks_priority) + output.append('Found %d %s tasks\n' % ( + len(pending), task_type)) - click.echo_via_pager('\n'.join(output)) + for task in pending: + output.append(pretty_print_task(task)) + + click.echo('\n'.join(output)) @task.command('archive')