|
1 |
| -#!/bin/bash -e |
2 |
| -#worker="${worker:-"openqaworker4"}" |
3 |
| -host="${host:-"openqa.opensuse.org"}" |
4 |
| -failed_since="${failed_since:-"$(date -I)"}" |
5 |
| -instance_string="${INSTANCE+" and instance='$INSTANCE'"}" |
6 |
| -worker_string="${WORKER+"assigned_worker_id in (select id from workers where (host='$WORKER'$instance_string)) and "}" |
7 |
| -result="${result:-"result='incomplete'"}" |
8 |
| -additional_filters="${additional_filters+" and $additional_filters"}" |
9 |
| -comment="${comment:-""}" |
10 |
| -dry_run="${dry_run:-"0"}" |
11 |
| - |
12 |
| -usage() { |
13 |
| - cat << EOF |
14 |
| -Usage: $0 [OPTIONS] |
| 1 | +#!/usr/bin/env python3 |
15 | 2 |
|
| 3 | +""" |
16 | 4 | Retrigger openQA jobs based on database queries.
|
17 | 5 |
|
18 |
| -By default retriggers openQA jobs with result '$result' since '$failed_since' |
19 |
| -on '$host'. |
20 |
| -
|
21 |
| -Can be restricted to jobs that ran on worker by setting the variable 'WORKER' |
22 |
| -and optionally 'INSTANCE'. |
23 |
| -
|
24 |
| -Needs SSH access to the target openQA host '$host'. |
25 |
| -
|
26 |
| -Options: |
27 |
| - -h, --help display this help |
28 |
| -EOF |
29 |
| - exit "$1" |
30 |
| -} |
31 |
| - |
32 |
| -main() { |
33 |
| - opts=$(getopt -o h -l help -n "$0" -- "$@") || usage 1 |
34 |
| - eval set -- "$opts" |
35 |
| - while true; do |
36 |
| - case "$1" in |
37 |
| - -h | --help) usage 0 ;; |
38 |
| - --) |
39 |
| - shift |
40 |
| - break |
41 |
| - ;; |
42 |
| - *) break ;; |
43 |
| - esac |
44 |
| - done |
45 |
| - |
46 |
| - [ "$dry_run" = "1" ] && client_prefix="echo" |
47 |
| - # shellcheck disable=SC2029 |
48 |
| - for i in $(ssh "$host" "sudo -u geekotest psql --no-align --tuples-only --command=\"select id from jobs where (${worker_string}${result} and clone_id is null and t_finished >= '$failed_since'$additional_filters);\" openqa"); do |
49 |
| - $client_prefix openqa-cli api --host "$host" -X POST jobs/"$i"/restart |
50 |
| - [ -n "$comment" ] && $client_prefix openqa-cli api --host "$host" -X POST jobs/"$i"/comments text="$comment" |
51 |
| - done |
52 |
| -} |
53 |
| - |
54 |
| -caller 0 > /dev/null || main "$@" |
| 6 | +Needs SSH access to the specified target openQA host. |
| 7 | +
|
| 8 | +Simple example call retriggering all recent incompletes on the default host: |
| 9 | + %(prog)s |
| 10 | +
|
| 11 | +Advanced example retriggering failed instead of incompletes, verbose output, with custom starting date, |
| 12 | +custom host and excluding jobs with \":investigate:\" in the name, executed as dry-run: |
| 13 | + %(prog)s -vvvv --host openqa.example.org --failed-since '2000-01-01T10:00' --result failed \ |
| 14 | +--additional-filters \"test not like '%%:investigate:%%'\" --dry-run |
| 15 | +""" |
| 16 | + |
| 17 | +import argparse |
| 18 | +import logging |
| 19 | +import subprocess |
| 20 | +import sys |
| 21 | +from datetime import datetime |
| 22 | + |
| 23 | +logging.basicConfig() |
| 24 | +log = logging.getLogger(sys.argv[0] if __name__ == "__main__" else __name__) |
| 25 | + |
| 26 | + |
| 27 | +class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): |
| 28 | + """Preserve multi-line __doc__ and provide default arguments in help strings.""" |
| 29 | + |
| 30 | + pass |
| 31 | + |
| 32 | + |
| 33 | +def parse_args(): |
| 34 | + parser = argparse.ArgumentParser(description=__doc__, formatter_class=CustomFormatter) |
| 35 | + parser.add_argument( |
| 36 | + "-v", |
| 37 | + "--verbose", |
| 38 | + help="Increase verbosity level, specify multiple times to increase verbosity", |
| 39 | + action="count", |
| 40 | + default=1, |
| 41 | + ) |
| 42 | + parser.add_argument("-H", "--host", default="openqa.opensuse.org", help="Target openQA host") |
| 43 | + parser.add_argument( |
| 44 | + "-s", |
| 45 | + "--failed-since", |
| 46 | + default=datetime.today().isoformat(), |
| 47 | + help="Filter jobs failed since this date", |
| 48 | + ) |
| 49 | + parser.add_argument("-w", "--worker", default=None, help="Filter jobs assigned to this worker") |
| 50 | + parser.add_argument("-i", "--instance", default=None, help="Instance of the worker") |
| 51 | + parser.add_argument("-r", "--result", default="incomplete", help="Filter jobs with this result") |
| 52 | + parser.add_argument( |
| 53 | + "-a", |
| 54 | + "--additional-filters", |
| 55 | + default=None, |
| 56 | + help="Additional filters for the SQL query", |
| 57 | + ) |
| 58 | + parser.add_argument("-c", "--comment", default=None, help="Comment to add to the retriggered jobs") |
| 59 | + parser.add_argument( |
| 60 | + "-d", |
| 61 | + "--dry-run", |
| 62 | + action="store_true", |
| 63 | + help="If set, only print the actions without executing", |
| 64 | + ) |
| 65 | + args = parser.parse_args() |
| 66 | + logging_level = (5 - min(args.verbose, 4)) * 10 |
| 67 | + log.setLevel(logging_level) |
| 68 | + return args |
| 69 | + |
| 70 | +def post(dry_run: bool, host: str, route: str, *args): |
| 71 | + cmd=('openqa-cli', 'api', '--host', host, '-X', 'POST', route, *args) |
| 72 | + if dry_run: |
| 73 | + return print(f"dry run: {cmd}") |
| 74 | + subprocess.run(cmd, check=True) |
| 75 | + |
| 76 | +def main(args): |
| 77 | + log.debug(args) |
| 78 | + instance_string = f" and instance='{args.instance}'" if args.instance else "" |
| 79 | + worker_string = f"assigned_worker_id in (select id from workers where (host='{args.worker}'{instance_string})) and " if args.worker else "" |
| 80 | + additional_filters = f" and {args.additional_filters}" if args.additional_filters else "" |
| 81 | + |
| 82 | + query = ( |
| 83 | + f"select id from jobs where ({worker_string}result='{args.result}' " |
| 84 | + f"and clone_id is null and t_finished >= '{args.failed_since}'{additional_filters});" |
| 85 | + ) |
| 86 | + |
| 87 | + log.debug(f"Using SQL query: '{query}' on {args.host}") |
| 88 | + ssh_command = ('ssh', args.host, f"sudo -u geekotest psql --no-align --tuples-only --command=\"{query}\" openqa") |
| 89 | + job_ids = subprocess.check_output(ssh_command).decode().splitlines() |
| 90 | + |
| 91 | + for job_id in job_ids: |
| 92 | + post(args.dry_run, args.host, f"jobs/{job_id}/restart") |
| 93 | + if args.comment: |
| 94 | + post(args.dry_run, args.host, f"jobs/{job_id}/comments", f'text={args.comment}') |
| 95 | + |
| 96 | + |
| 97 | +if __name__ == "__main__": |
| 98 | + main(parse_args()) |
0 commit comments