aboutsummaryrefslogtreecommitdiff
path: root/linaro_gerrit.py
blob: 87a1c73c4a93db298a5aa859fd024d136acf12fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/usr/bin/python

import json
import logging

import requests

logging.basicConfig()
logging.getLogger("requests").setLevel(logging.WARNING)
log = logging.getLogger("linaro_gerrit")


class LinaroGerrit:

    def __init__(self, base, username, password, noverify=False,
                 loglevel="WARN", dryrun=False):
        self.base = base
        self.username = username
        self.password = password
        self.verify = not noverify
        self.dryrun = dryrun
        log.setLevel(getattr(logging, loglevel.upper()))
        try:
            from requests.auth import HTTPDigestAuth
            self.reqargs = {"auth": HTTPDigestAuth(username, password),
                            "verify": self.verify}
        except ImportError:
            log.info("Using old version of python-requests \
                     (--noverify not supported")
            self.reqargs = {"auth": ('digest', username, password)}

    def strip_gerrit_junk(self, string):
        # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
        return '\n'.join(string.split('\n')[1:])

    def list_keys(self, username):
        log.info("Listing keys for user: %s", username)
        url = "%s/a/accounts/%s/sshkeys/" % (self.base, username)
        r = requests.get(url, **self.reqargs)
        keydict = {}
        if r.status_code == 200:
            try:
                a = json.loads(self.strip_gerrit_junk(r.content))
                for data in a:
                    keydict[data["seq"]] = data["ssh_public_key"].strip()
                return keydict
            except ValueError as e:
                log.warn(e)
                return False
        else:
            log.info("user %s not found in gerrit", username)
            return False

    def add_key(self, pubkey, username):
        log.debug("Adding pubkey %s to user %s", pubkey, username)
        url = "%s/a/accounts/%s/sshkeys/" % (self.base, username)
        if not self.dryrun:
            r = requests.post(url, data=pubkey.encode("utf-8"), **self.reqargs)
            if r.status_code == 201:
                return True
            return False
        log.debug("Not actually doing it because --dryrun")
        return True

    def del_key(self, username, key_id):
        log.debug("Deleting key %s by id from user %s", key_id, username)
        url = "%s/a/accounts/%s/sshkeys/%i" % (self.base, username, key_id)
        if not self.dryrun:
            r = requests.delete(url, **self.reqargs)
            if r.status_code == 204:
                return True
            return False
        log.debug("Not actually doing it because --dryrun")
        return True

    def keysets_to_list(self, keysets):
        list = []
        for key in keysets:
            list.append(unicode(key[1]))
        return list

    def list_group_members(self, groupname):
        log.info("Listing member of group: %s", groupname)
        url = "%s/a/groups/%s/members/" % (self.base, groupname)
        r = requests.get(url, **self.reqargs)
        members = []
        if r.status_code == 200:
            try:
                a = json.loads(self.strip_gerrit_junk(r.content))
                for data in a:
                    try:
                        members.append(data["username"])
                    except KeyError as e:
                        log.error("ERROR: user %s has no username!",
                                  data["email"])
                return members
            except ValueError as e:
                log.warn(e)
                return False
        elif r.status_code == 404:
            log.info("Group %s not found in gerrit", groupname)
            return False
        else:
            log.error("list_group_members(%s): unexpected status code: %d", groupname, r.status_code)
            return False

    def add_group_member(self, groupname, username):
        log.debug("Adding %s to group %s", username, groupname)
        url = "%s/a/groups/%s/members/%s" % (self.base, groupname, username)
        if not self.dryrun:
            r = requests.put(url, **self.reqargs)
            if r.status_code == 201 or r.status_code == 200:
                return True
            else:
                log.info("Failed to add %s to group %s", username, groupname)
            return False
        log.debug("Not actually doing it because --dryrun")
        return True

    def delete_group_member(self, groupname, username):
        log.debug("Deleting %s from group %s", username, groupname)
        url = "%s/a/groups/%s/members/%s" % (self.base, groupname, username)
        if not self.dryrun:
            r = requests.delete(url, **self.reqargs)
            if r.status_code == 204:
                return True
            else:
                log.error("Failed to del %s from group %s", username,
                          groupname)
            return False
        log.debug("Not actually doing it because --dryrun")
        return True

    def list_included_groups(self, groupname):
        log.debug("Listing included groups for %s", groupname)
        url = "%s/a/groups/%s/groups/" % (self.base, groupname)
        r = requests.get(url, **self.reqargs)
        groups = []
        if r.status_code == 200:
            try:
                a = json.loads(self.strip_gerrit_junk(r.content))
                for data in a:
                    groups.append(data["name"])
                return groups
            except ValueError as e:
                log.warn(e)
                return False
        else:
            log.info("Group %s not found in gerrit", groupname)
            return False

    def add_included_group(self, groupname, include):
        log.debug("Including group %s in %s", include, groupname)
        include = requests.utils.quote(include, safe='')
        url = "%s/a/groups/%s/groups/%s" % (self.base, groupname, include)
        if not self.dryrun:
            r = requests.put(url, **self.reqargs)
            if r.status_code == 201 or r.status_code == 200:
                return True
            else:
                log.error("Failed to include %s to group %s", include,
                          groupname)
            return False
        log.debug("Not actually doing it because --dryrun")
        return True

    def list_projects(self, parents=False):
        log.debug("Listing projects")
        url = "%s/a/projects/" % self.base
        params = {}
        if parents:
            params["t"] = ""
        r = requests.get(url, params=params, **self.reqargs)
        if r.status_code == 200:
            return json.loads(self.strip_gerrit_junk(r.content))
        else:
            log.error("Failed to list projects")
            return {}

    def set_project_parent(self, project, parent, commit_msg=""):
        log.info("Setting project %s parent as %s", project, parent)
        project = requests.utils.quote(project, safe='')
        url = "%s/a/projects/%s/parent" % (self.base, project)
        body = {"parent": parent, "commit_message": commit_msg}
        headers = {"Content-Type": "application/json"}
        if not self.dryrun:
            r = requests.put(url, data=json.dumps(body), headers=headers, **self.reqargs)
            if r.status_code == 201 or r.status_code == 200:
                return True
            else:
                log.error("Failed to set project %s parent as %s", project,
                          parent)
            return False
        log.info("Not actually doing it because --dryrun")
        return True


def add_gerrit_args(parser, def_loglevel="WARNING"):
    parser.add_argument('--username', help="Gerrit HTTP API Username")
    parser.add_argument('--password', help="Gerrit HTTP API Password")
    parser.add_argument('--base', required=True, help="Gerrit BASE URL (e.g. "
                                       "https://review.linaro.org)")
    parser.add_argument('--noverify', action="store_true",
                        help="Disable SSL certificate verficiation")
    parser.add_argument('--dryrun', action="store_true",
                        help="Do not perform any actions, just report")
    parser.add_argument('--loglevel', default=def_loglevel,
                        help="Setting logging level, default: %(default)s")


def apply_gerrit_conf(args):
    """Try to load linaro_gerrit.conf file of simple arg=val format and
    use options there for None-valued arguments in args. The usecase is:
    Let specify password in a config file, so it didn't show in the process
    list, cron emails, etc. If password is specified on command line however,
    it should be used instead of one in config file. Different/mroe advanced
    usecases may not be handled by this function."""
    conf_path = __file__.rsplit(".", 1)[0] + ".conf"
    with open(conf_path) as f:
        for l in f:
            l = l.strip()
            if not l or l[0] == "#":
                continue
            k, v = [x.strip() for x in l.split("=", 1)]
            if hasattr(args, k) and getattr(args, k) is None:
                setattr(args, k, v)