wbredmine.py 6.59 KB
Newer Older
Loic Dachary's avatar
Loic Dachary committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# -*- mode: python; coding: utf-8 -*-
#
# Copyright (C) 2015 <contact@redhat.com>
#
# Author: Loic Dachary <loic@dachary.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see `<http://www.gnu.org/licenses/>`.
#
Loic Dachary's avatar
Loic Dachary committed
20 21
import argparse
from ceph_workbench import util
Loic Dachary's avatar
Loic Dachary committed
22 23
import logging
import re
24 25
# http://python-redmine.readthedocs.org/
import redmine
Loic Dachary's avatar
Loic Dachary committed
26

27 28
log = logging.getLogger(__name__)

Loic Dachary's avatar
Loic Dachary committed
29

30 31 32 33
class ExceptionUserOrKey(Exception):
    pass


Loïc Dachary's avatar
Loïc Dachary committed
34
class WBRedmine(object):
Loic Dachary's avatar
Loic Dachary committed
35

36 37
    pending_backport = 'Pending Backport'

38
    def __init__(self, args):
Loic Dachary's avatar
Loic Dachary committed
39 40
        self.args = args
        self.issues = {}
Loïc Dachary's avatar
Loïc Dachary committed
41 42 43
        self.status2status_id = {}
        self.version2version_id = {}
        self.tracker2tracker_id = {}
44
        self.project_id2project = {}
Loic Dachary's avatar
Loic Dachary committed
45

46
    def open(self):
47
        self.args_sanity_check()
48
        self.r = redmine.Redmine(self.args.redmine_url,
49
                                 key=self.get_key())
50
        self.set_custom_fields()
51 52
        return self

53
    def index(self):
Loïc Dachary's avatar
Loïc Dachary committed
54
        self.map_names_to_ids()
55 56 57
        self.load_open_issues()
        return self

58 59 60 61 62 63 64 65 66 67
    def get_key(self):
        if self.args.redmine_user:
            r = redmine.Redmine(self.args.redmine_url,
                                username=self.args.redmine_user,
                                password=self.args.redmine_password)
            self.key = r.user.get('current').api_key
        else:
            self.key = self.args.redmine_key
        return self.key

Loïc Dachary's avatar
Loïc Dachary committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81
    def map_names_to_ids(self):
        self.status2status_id = {}
        for status in self.r.issue_status.all():
            self.status2status_id[status.name] = status.id

        versions = self.r.version.filter(project_id='ceph')
        self.version2version_id = {}
        for version in versions:
            self.version2version_id[version.name] = version.id

        self.tracker2tracker_id = {}
        for tracker in self.r.tracker.all():
            self.tracker2tracker_id[tracker.name] = tracker.id

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
    def set_custom_fields(self):
        #
        # Hack because
        # http://www.redmine.org/projects/redmine/wiki/Rest_CustomFields
        # requires administrative permissions. It can be removed when
        # https://www.redmine.org/issues/18875
        # is resolved.
        #
        if 'tracker.ceph.com' in self.args.redmine_url:
            self.backport_id = 2
            self.release_id = 16
            return 'hack'
        else:
            for field in self.r.custom_field.all():
                if field.name == 'Backport':
                    self.backport_id = field.id
                elif field.name == 'Release':
                    self.release_id = field.id

101 102 103 104 105 106 107 108 109 110 111 112
    def has_tracker(self, project_id, tracker_name):
        for tracker in self.get_project(project_id).trackers:
            if tracker['name'] == tracker_name:
                return True
        return False

    def get_project(self, project_id):
        if project_id not in self.project_id2project:
            project = self.r.project.get(project_id, include='trackers')
            self.project_id2project[project_id] = project
        return self.project_id2project[project_id]

Loic Dachary's avatar
Loic Dachary committed
113 114 115 116 117 118 119
    @staticmethod
    def get_parser():
        parser = argparse.ArgumentParser(
            parents=[util.get_parser()],
            add_help=False,
        )
        parser.add_argument('--redmine-url',
120
                            help='base URL for API calls',
121
                            default='http://tracker.ceph.com')
122 123
        auth = parser.add_mutually_exclusive_group()
        auth.add_argument('--redmine-user',
124 125 126
                          help=('if set, use --redmine-password to '
                                'authenticate (cannot be used with '
                                '--redmine-key)'))
Loic Dachary's avatar
Loic Dachary committed
127
        parser.add_argument('--redmine-password',
128
                            help=('must be set if --redmine-user is set'))
129
        auth.add_argument('--redmine-key',
130 131 132
                          help=('authenticate using the value found at '
                                'http://tracker.ceph.com/my/api_key instead '
                                'of --redmine-user and --redmine-password'))
Loic Dachary's avatar
Loic Dachary committed
133 134
        return parser

135
    def args_sanity_check(self):
136 137 138
        if (not self.args.redmine_user and
                not self.args.redmine_key):
            raise ExceptionUserOrKey("did not find --redmine-user "
139 140 141 142
                                     " or --redmine-key")

    @staticmethod
    def factory(argv):
143
        return WBRedmine(WBRedmine.get_parser().parse_args(argv))
Loic Dachary's avatar
Loic Dachary committed
144

145 146 147
    def url(self, issue):
        return self.args.redmine_url + "/issues/" + str(issue['id'])

Loic Dachary's avatar
Loic Dachary committed
148 149 150 151 152
    def load_issue(self, issue_id):
        if issue_id not in self.issues or 'id' not in self.issues[issue_id]:
            self.update_issues(issue_id)
        return self.issues[issue_id]

153 154 155 156
    def set_backport(self, issue):
        for field in issue['custom_fields']:
            if field['name'] == 'Backport' and field['value'] != 0:
                issue['backports'] = set(re.findall('\w+', field['value']))
157 158 159
                log.debug("backports for " + str(issue['id']) +
                          " is " + str(field['value']) + " " +
                          str(issue['backports']))
160 161 162 163 164 165
                return True
        return False

    def get_release(self, issue):
        for field in issue.custom_fields:
            if field['name'] == 'Release':
166
                return ",".join(field['value'])
167

Loic Dachary's avatar
Loic Dachary committed
168
    def update_issues(self, issue_id):
169 170 171
        self.issues.setdefault(issue_id, {}).update(
            self.r.issue.get(issue_id, include='journals')
        )
Loic Dachary's avatar
Loic Dachary committed
172
        issue = self.issues[issue_id]
173
        self.set_backport(issue)
Loic Dachary's avatar
Loic Dachary committed
174 175 176

    def load_open_issues(self):
        for release in self.args.releases:
177 178 179 180 181 182
            kwargs = {
                'project_id': 'ceph',
                'status_id': 'open',
                'limit': 100,
                'cf_' + str(self.backport_id): '' + release,
            }
183
            log.debug('load_open_issues ' + str(kwargs))
184
            for issue in self.r.issue.filter(**kwargs):
Loic Dachary's avatar
Loic Dachary committed
185 186
                if issue['project']['name'] in ('Ceph', 'rbd', 'rgw', 'fs'):
                    self.update_issues(issue['id'])