|
1 | | -#!/bin/bash -e |
2 | | -host="${host:-"openqa.opensuse.org"}" |
3 | | -failed_since="${failed_since:-"$(date -I)"}" |
4 | | -instance_string="${INSTANCE+" and instance='$INSTANCE'"}" |
5 | | -worker_string="${WORKER+"assigned_worker_id in (select id from workers where (host='$WORKER'$instance_string)) and "}" |
6 | | -result="${result:-"result='incomplete'"}" |
7 | | -additional_filters="${additional_filters+" and $additional_filters"}" |
8 | | -comment="${comment:-""}" |
9 | | -max_jobs_per_request=25 |
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 | | -opts=$(getopt -o h -l help -n "$0" -- "$@") || usage 1 |
33 | | -eval set -- "$opts" |
34 | | -while true; do |
35 | | - case "$1" in |
36 | | - -h | --help) usage 0 ;; |
37 | | - --) |
38 | | - shift |
39 | | - break |
40 | | - ;; |
41 | | - *) break ;; |
42 | | - esac |
43 | | -done |
44 | | - |
45 | | -[ "$dry_run" = "1" ] && client_prefix="echo" |
46 | | -sql_command="select id from jobs where (${worker_string}${result} and clone_id is null and t_finished >= '$failed_since'$additional_filters);" |
47 | | -# shellcheck disable=SC2029 |
48 | | -job_ids=${JOB_IDS:-$(ssh "$host" "sudo -u geekotest psql --no-align --tuples-only --command=\"$sql_command\" openqa")} |
49 | | - |
50 | | -usage() { |
51 | | - cat << EOF |
52 | | -Usage: $0 [OPTIONS] |
53 | | -
|
54 | | -Retrigger openQA jobs based on database queries. |
55 | | -
|
56 | | -By default retriggers openQA jobs with '$result' since '$failed_since' on |
57 | | -'$host'. |
58 | | -
|
59 | | -Needs SSH access to the target openQA host '$host' to query the database |
60 | | -unless 'JOB_IDS' is provided. |
61 | | -
|
62 | | -Options: |
63 | | - -h, --help display this help |
64 | | -
|
65 | | -Environment variables: |
66 | | - host target openQA host (default: $host) |
67 | | - failed_since retrigger jobs finished since this date (default: $failed_since) |
68 | | - result retrigger jobs with this result (default: $result) |
69 | | - additional_filters additional SQL filters for the job query |
70 | | - comment comment to add to retriggered jobs |
71 | | - dry_run set to 1 to only print what would be done |
72 | | - WORKER restrict to jobs that ran on this worker host |
73 | | - INSTANCE restrict to jobs that ran on this worker instance |
74 | | - sql_command custom SQL command to fetch job IDs |
75 | | - JOB_IDS comma or space separated list of job IDs to retrigger |
76 | | - cli_protocol protocol for openqa-cli (e.g. https) |
77 | | - cli_port port for openqa-cli |
78 | | -EOF |
79 | | - exit "$1" |
80 | | -} |
81 | | - |
82 | | -restart-jobs() { |
83 | | - [[ ${#query_params[@]} -lt 1 ]] && return |
84 | | - [[ $comment ]] && query_params+=("comment=$comment") |
85 | | - $client_prefix openqa-cli api --host "$host" -X POST jobs/restart "${query_params[@]}" |
86 | | - query_params=() |
87 | | -} |
88 | | - |
89 | | -main() { |
90 | | - opts=$(getopt -o h -l help -n "$0" -- "$@") || usage 1 |
91 | | - eval set -- "$opts" |
92 | | - while true; do |
93 | | - case "$1" in |
94 | | - -h | --help) usage 0 ;; |
95 | | - --) |
96 | | - shift |
97 | | - break |
98 | | - ;; |
99 | | - *) break ;; |
100 | | - esac |
101 | | - done |
102 | | - |
103 | | - [ "$dry_run" = "1" ] && client_prefix="echo" |
104 | | - sql_command=${sql_command:-"select id from jobs where (${worker_string}${result} and clone_id is null and t_finished >= '$failed_since'$additional_filters);"} |
105 | | - # shellcheck disable=SC2029 |
106 | | - job_ids=${JOB_IDS:-$(ssh "$host" "sudo -u geekotest psql --no-align --tuples-only --command=\"$sql_command\" openqa")} |
107 | | - |
108 | | - query_params=() |
109 | | - [[ $cli_protocol ]] && host=$cli_protocol://$host |
110 | | - [[ $cli_port ]] && host+=:$cli_port |
111 | | - |
112 | | - for job_id in $job_ids; do |
113 | | - query_params+=("jobs=$job_id") |
114 | | - [[ ${#query_params[@]} -ge "$max_jobs_per_request" ]] && restart-jobs |
115 | | - done |
116 | | - restart-jobs |
117 | | -} |
118 | | - |
119 | | -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