]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/buildservice/bugtracker.py
bugzilla: Cache repsonses
[people/jschlag/pbs.git] / src / buildservice / bugtracker.py
CommitLineData
f6e6ff79
MT
1#!/usr/bin/python
2
f6e6ff79
MT
3import xmlrpclib
4
2c909128 5from . import base
f6e6ff79 6
cc05687f
MT
7from .decorators import *
8
f6e6ff79
MT
9class BugzillaBug(base.Object):
10 def __init__(self, bugzilla, bug_id):
11 base.Object.__init__(self, bugzilla.pakfire)
12 self.bugzilla = bugzilla
13
14 self.bug_id = bug_id
15 self._data = None
16
17 def __cmp__(self, other):
18 return cmp(self.bug_id, other.bug_id)
19
20 def call(self, *args, **kwargs):
21 args = (("Bug",) + args)
22
23 return self.bugzilla.call(*args, **kwargs)
24
25 @property
26 def id(self):
27 return self.bug_id
28
cc05687f 29 @lazy_property
f6e6ff79 30 def data(self):
cc05687f
MT
31 # Fetch bug information from cache
32 data = self.backend.cache.get(self._cache_key)
33
34 # Hit
35 if data:
36 return data
37
38 # Fetch bug information from Bugzilla
39 for data in self.call("get", ids=[self.id,])["bugs"]:
40 # Put it into the cache
41 self.backend.cache.set(self._cache_key, data, self.backend.bugzilla.cache_lifetime)
f6e6ff79 42
cc05687f
MT
43 return data
44
45 @property
46 def _cache_key(self):
47 return "bug-%s" % self.bug_id
f6e6ff79
MT
48
49 @property
50 def url(self):
51 return self.bugzilla.bug_url(self.id)
52
53 @property
54 def summary(self):
55 return self.data.get("summary")
56
57 @property
58 def assignee(self):
59 return self.data.get("assigned_to")
60
61 @property
62 def status(self):
63 return self.data.get("status")
64
65 @property
66 def resolution(self):
67 return self.data.get("resolution")
68
69 @property
70 def is_closed(self):
71 return not self.data["is_open"]
72
73 def set_status(self, status, resolution=None, comment=None):
74 kwargs = { "status" : status }
75 if resolution:
76 kwargs["resolution"] = resolution
77 if comment:
78 kwargs["comment"] = { "body" : comment }
79
80 self.call("update", ids=[self.id,], **kwargs)
f6e6ff79 81
cc05687f
MT
82 # Invalidate cache
83 self.backend.cache.delete(self.cache_key)
84
f6e6ff79
MT
85
86class Bugzilla(base.Object):
87 def __init__(self, pakfire):
88 base.Object.__init__(self, pakfire)
89
90 # Open the connection to the server.
91 self.server = xmlrpclib.ServerProxy(self.url, use_datetime=True)
92
93 # Cache the credentials.
94 self.__credentials = {
95 "Bugzilla_login" : self.user,
96 "Bugzilla_password" : self.password,
97 }
98
99 def call(self, *args, **kwargs):
100 # Add authentication information.
101 kwargs.update(self.__credentials)
102
103 method = self.server
104 for arg in args:
105 method = getattr(method, arg)
106
107 return method(kwargs)
108
109 def bug_url(self, bugid):
110 url = self.settings.get("bugzilla_url", None)
111
112 try:
113 return url % { "bugid" : bugid }
114 except:
115 return "#"
116
117 def enter_url(self, component):
118 args = {
119 "product" : self.settings.get("bugzilla_product", ""),
120 "component" : component,
121 }
122
123 url = self.settings.get("bugzilla_url_new")
124
125 return url % args
126
127 def buglist_url(self, component):
128 args = {
129 "product" : self.settings.get("bugzilla_product", ""),
130 "component" : component,
131 }
132
133 url = self.settings.get("bugzilla_url_buglist")
134
135 return url % args
136
137 @property
138 def url(self):
139 return self.settings.get("bugzilla_url_xmlrpc", None)
140
141 @property
142 def user(self):
143 return self.settings.get("bugzilla_xmlrpc_user", "")
144
145 @property
146 def password(self):
147 return self.settings.get("bugzilla_xmlrpc_password")
148
cc05687f
MT
149 @lazy_property
150 def cache_lifetime(self):
151 return self.settings.get("bugzilla_cache_lifetime", 3600)
152
f6e6ff79
MT
153 def get_bug(self, bug_id):
154 try:
155 bug = BugzillaBug(self, bug_id)
f6e6ff79
MT
156
157 except xmlrpclib.Fault:
158 return None
159
160 return bug
161
162 def find_users(self, pattern):
966498de
MT
163 users = self.call("User", "get", match=[pattern,])
164 if users:
165 return users["users"]
f6e6ff79
MT
166
167 def find_user(self, pattern):
168 users = self.find_users(pattern)
169
170 if not users:
171 return
172
173 elif len(users) > 1:
174 raise Exception, "Got more than one result."
175
176 return users[0]
177
178 def get_bugs_from_component(self, component, closed=False):
179 kwargs = {
180 "product" : self.settings.get("bugzilla_product", ""),
181 "component" : component,
182 }
183
184 query = self.call("Bug", "search", include_fields=["id"], **kwargs)
185
186 bugs = []
187 for bug in query["bugs"]:
188 bug = self.get_bug(bug["id"])
189
190 if not bug.is_closed == closed:
191 continue
192
193 bugs.append(bug)
194
195 return bugs
76353712
MT
196
197 def send_all(self, limit=100):
198 # Get up to ten updates.
199 query = self.db.query("SELECT * FROM builds_bugs_updates \
200 WHERE error IS FALSE ORDER BY time LIMIT %s", limit)
201
202 # XXX CHECK IF BZ IS ACTUALLY REACHABLE AND WORKING
203
204 for update in query:
205 try:
206 bug = self.backend.bugzilla.get_bug(update.bug_id)
207 if not bug:
208 logging.error("Bug #%s does not exist." % update.bug_id)
209 continue
210
211 # Set the changes.
212 bug.set_status(update.status, update.resolution, update.comment)
213
214 except Exception, e:
215 # If there was an error, we save that and go on.
216 self.db.execute("UPDATE builds_bugs_updates SET error = 'Y', error_msg = %s \
217 WHERE id = %s", "%s" % e, update.id)
218
219 else:
220 # Remove the update when it has been done successfully.
221 self.db.execute("DELETE FROM builds_bugs_updates WHERE id = %s", update.id)