Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # This tool is useful to query gerrit for negative or missing votes left by |
| 3 | # a user. It may require tweaking for different failure messages etc. |
| 4 | |
| 5 | import json |
| 6 | import requests |
| 7 | import traceback |
| 8 | |
| 9 | # Set the user to watch |
| 10 | user = 'turbo-hipster' |
Joshua Hesketh | 0d2c8b8 | 2014-08-14 12:51:46 +1000 | [diff] [blame] | 11 | author_name = 'DB Datasets CI' |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 12 | upstream_user = 'jenkins' |
| 13 | upstream_author_name = "Jenkins" |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 14 | |
| 15 | # Grab a list of missing or negative reviews for a user: |
| 16 | url = ("https://review.openstack.org/changes/?q=status:open " |
| 17 | "project:openstack/nova NOT label:Verified>=0,%s " |
| 18 | "branch:master&o=CURRENT_REVISION&o=MESSAGES" % user) |
| 19 | |
| 20 | print "Grabbing reviews from %s" % url |
| 21 | r = requests.get(url) |
| 22 | |
| 23 | no_votes = [] |
| 24 | negative_votes = [] |
| 25 | merge_failures = [] |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 26 | upstream_merge_failures = [] |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 27 | unknown = [] |
| 28 | |
| 29 | for change in json.loads(r.text[5:]): |
| 30 | try: |
| 31 | patchset = change['revisions'][change['current_revision']]['_number'] |
| 32 | change_id = str(change['_number']) + ',' + str(patchset) |
| 33 | last_message = None |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 34 | last_upstream_message = None |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 35 | for message in sorted(change['messages'], |
| 36 | key=lambda k: (k['_revision_number'], |
| 37 | k['date']), reverse=True): |
| 38 | if message['_revision_number'] < patchset: |
| 39 | # Finished looking at all the messages on this patchset |
| 40 | break |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 41 | if not last_message and message['author']['name'] == author_name: |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 42 | last_message = message['message'] |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 43 | if (not last_upstream_message and |
| 44 | message['author']['name'] == upstream_author_name): |
| 45 | last_upstream_message = message['message'] |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 46 | |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 47 | if (last_upstream_message and |
| 48 | 'Merge Failed.' in last_upstream_message.split('\n')[2]): |
| 49 | upstream_merge_failures.append({ |
| 50 | 'change_id': change_id, |
| 51 | 'updated': change['updated'], |
| 52 | 'change': change, |
| 53 | 'last_upstream_message': last_upstream_message, |
| 54 | }) |
| 55 | elif not last_message: |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 56 | # turbo-hister hasn't commented on this patchset |
| 57 | no_votes.append({ |
| 58 | 'change_id': change_id, |
| 59 | 'updated': change['updated'], |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 60 | 'change': change, |
| 61 | 'last_upstream_message': last_upstream_message, |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 62 | }) |
| 63 | elif ('This change was unable to be automatically merged with the ' |
| 64 | 'current state of the repository.' in last_message): |
| 65 | merge_failures.append({ |
| 66 | 'change_id': change_id, |
| 67 | 'updated': change['updated'], |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 68 | 'change': change, |
| 69 | 'last_upstream_message': last_upstream_message, |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 70 | }) |
| 71 | elif 'Database migration testing failed' in last_message: |
| 72 | negative_votes.append({ |
| 73 | 'change_id': change_id, |
| 74 | 'updated': change['updated'], |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 75 | 'change': change, |
| 76 | 'last_upstream_message': last_upstream_message, |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 77 | }) |
| 78 | else: |
| 79 | unknown.append({ |
| 80 | 'change_id': change_id, |
| 81 | 'updated': change['updated'], |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 82 | 'change': change, |
| 83 | 'last_upstream_message': last_upstream_message, |
Joshua Hesketh | da8ac65 | 2014-07-09 20:13:46 +1000 | [diff] [blame] | 84 | }) |
| 85 | |
| 86 | except Exception: |
| 87 | print "Something failed.. Here is the change..." |
| 88 | print change |
| 89 | traceback.print_exc() |
| 90 | |
| 91 | |
| 92 | def print_enqueues(changes): |
| 93 | for change in sorted(changes, key=lambda k: k['updated'], reverse=True): |
| 94 | print ("zuul enqueue --trigger gerrit --pipeline check " |
| 95 | "--project openstack/nova --change %s" % (change['change_id'])) |
| 96 | |
| 97 | print "=" * 20 + (" Changes with no votes (%d) " % len(no_votes)) + "=" * 20 |
| 98 | print_enqueues(no_votes) |
| 99 | print ("=" * 20 + (" Changes with negative votes (%d) " % len(negative_votes)) |
| 100 | + "=" * 20) |
| 101 | print_enqueues(negative_votes) |
| 102 | print ("=" * 20 + (" Changes with merge failure (%d) " % len(merge_failures)) + |
| 103 | "=" * 20) |
| 104 | print_enqueues(merge_failures) |
| 105 | print "=" * 20 + (" Others in this query (%d) " % len(unknown)) + "=" * 20 |
| 106 | print_enqueues(unknown) |
Joshua Hesketh | 6730ed4 | 2015-06-12 21:33:57 +1000 | [diff] [blame] | 107 | print "=" * 20 + (" Changes with merge failures upstream (%d) " |
| 108 | % len(upstream_merge_failures)) + "=" * 20 |
| 109 | print_enqueues(upstream_merge_failures) |