Skip to content

Commit 363ffbe

Browse files
Michal Novotnywgwoods
authored andcommitted
Add bugzilla4 XML-RPC handling
On May 21st 2012 at 2200 UTC RH Bugzilla will change to Bugzilla 4.2 with some other bits backported from Bugzilla 4.4. This version no longer has r['internals'] in the XML-RPC output and there are some changes in the API itself so this adds the basic support for Bugzilla 4.0+ and introduces the RHBugzilla4 class. I've tried this against https://partner-bugzilla.redhat.com to issue: * modify -F "Test" -p -l "Adding Fixed-In-Version" $BUGID * modify --cc [email protected] -p -l "Adding CC" $BUGID * modify -s ASSIGNED -p -l "Switching to ASSIGNED" $BUGID And everything was working fine. Michal Signed-off-by: Michal Novotny <[email protected]>
1 parent 5bc7c65 commit 363ffbe

5 files changed

Lines changed: 400 additions & 7 deletions

File tree

bin/bugzilla

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,11 @@ def main():
353353
if global_opt.bztype == 'auto':
354354
log.info('Autodetecting Bugzilla type')
355355
# Cheat a little, for the sake of speed
356-
if 'bugzilla.redhat.com' in global_opt.bugzilla:
356+
# 2012-05-14 minovotn - adding slash was necessary for testing of
357+
# partner-bugzilla to make it run the autodetection, partner-bugzilla
358+
# seems to be RH Bugzilla test server, should be changed to RHBugzilla4
359+
# once RHBZ upgrades to 4.2 (i.e. on May 21 2200 UTC).
360+
if '/bugzilla.redhat.com' in global_opt.bugzilla:
357361
log.info('Using RHBugzilla3 for URL containing bugzilla.redhat.com')
358362
bzclass = bugzilla.RHBugzilla3
359363
else:

bugzilla.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ show this help message and exit
2929
.IP "--bugzilla=BUGZILLA"
3030
bugzilla XMLRPC URI. default: https://bugzilla.redhat.com/xmlrpc.cgi
3131
.IP "--bztype=BZTYPE"
32-
Bugzilla type. Autodetected if not set. Available types: Bugzilla3 Bugzilla32 Bugzilla34 Bugzilla36 RHBugzilla3 NovellBugzilla
32+
Bugzilla type. Autodetected if not set. Available types: Bugzilla3 Bugzilla32 Bugzilla34 Bugzilla36 Bugzilla4 RHBugzilla3 RHBugzilla4 NovellBugzilla
3333
.IP "--user=USER"
3434
username
3535
.IP "--password=PASSWORD"

bugzilla/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@
99
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
1010
# the full text of the license.
1111

12-
from bugzilla3 import Bugzilla3, Bugzilla32, Bugzilla34, Bugzilla36
13-
from rhbugzilla import RHBugzilla, RHBugzilla3
12+
from bugzilla3 import Bugzilla3, Bugzilla32, Bugzilla34, Bugzilla3
13+
from bugzilla4 import Bugzilla4
14+
from rhbugzilla import RHBugzilla, RHBugzilla3, RHBugzilla4
1415
from nvlbugzilla import NovellBugzilla
1516
from base import version
1617
import xmlrpclib
1718
import logging
1819
log = logging.getLogger("bugzilla")
1920

2021
# advertised class list
21-
classlist = ['Bugzilla3', 'Bugzilla32', 'Bugzilla34', 'Bugzilla36',
22-
'RHBugzilla3', 'NovellBugzilla']
22+
classlist = ['Bugzilla3', 'Bugzilla32', 'Bugzilla34', 'Bugzilla36', 'Bugzilla4',
23+
'RHBugzilla3', 'RHBugzilla4', 'NovellBugzilla']
2324

2425
def getBugzillaClassForURL(url):
2526
log.debug("Choosing subclass for %s" % url)
@@ -51,6 +52,8 @@ def getBugzillaClassForURL(url):
5152
if rhbz:
5253
if bzversion.startswith('3.'):
5354
c = RHBugzilla3
55+
elif bzversion.startswith('4.'):
56+
c = RHBugzilla4
5457
else:
5558
c = RHBugzilla
5659
elif bzversion.startswith('3.'):

bugzilla/bugzilla4.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# bugzilla4.py - a really simple Python interface to Bugzilla 4.x using xmlrpclib.
2+
#
3+
# Copyright (C) 2008-2012 Red Hat Inc.
4+
# Author: Michal Novotny <[email protected]>
5+
#
6+
# This program is free software; you can redistribute it and/or modify it
7+
# under the terms of the GNU General Public License as published by the
8+
# Free Software Foundation; either version 2 of the License, or (at your
9+
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
10+
# the full text of the license.
11+
12+
import bugzilla.base
13+
14+
class Bugzilla4(bugzilla.base.BugzillaBase):
15+
'''Concrete implementation of the Bugzilla protocol. This one uses the
16+
methods provided by standard Bugzilla 4.0.x releases.'''
17+
18+
version = '0.1'
19+
user_agent = bugzilla.base.user_agent + ' Bugzilla4/%s' % version
20+
#createbug_required = ('product','component','summary','version')
21+
22+
createbug_required = ('product','component','summary','version',
23+
'op_sys','platform')
24+
25+
def __init__(self,**kwargs):
26+
bugzilla.base.BugzillaBase.__init__(self,**kwargs)
27+
self.user_agent = self.__class__.user_agent
28+
29+
def _login(self,user,password):
30+
'''Backend login method for Bugzilla4'''
31+
return self._proxy.User.login({'login':user,'password':password})
32+
33+
def _logout(self):
34+
'''Backend login method for Bugzilla4'''
35+
return self._proxy.User.logout()
36+
37+
#---- Methods and properties with basic bugzilla info
38+
39+
def _getuserforid(self,userid):
40+
'''Get the username for the given userid'''
41+
# STUB FIXME
42+
return str(userid)
43+
44+
def _getproducts(self):
45+
'''This throws away a bunch of data that RH's getProdInfo
46+
didn't return. Ah, abstraction.'''
47+
product_ids = self._proxy.Product.get_accessible_products()
48+
r = self._proxy.Product.get_products(product_ids)
49+
return r['products']
50+
def _getcomponents(self,product):
51+
if type(product) == str:
52+
product = self._product_name_to_id(product)
53+
r = self._proxy.Bug.legal_values({'product_id':product,'field':'component'})
54+
return r['values']
55+
56+
#---- Methods for reading bugs and bug info
57+
58+
def _getbugs(self,idlist):
59+
'''Return a list of dicts of full bug info for each given bug id.
60+
bug ids that couldn't be found will return None instead of a dict.'''
61+
idlist = map(lambda i: int(i), idlist)
62+
r = self._proxy.Bug.get_bugs({'ids':idlist, 'permissive': 1})
63+
bugdict = dict([(b['id'], b) for b in r['bugs']])
64+
return [bugdict.get(i) for i in idlist]
65+
def _getbug(self,id):
66+
'''Return a dict of full bug info for the given bug id'''
67+
return self._getbugs([id])[0]
68+
69+
# TODO: Bugzilla4 should support getbugsimple, needs to be implemented
70+
_getbugsimple = _getbug
71+
_getbugssimple = _getbugs
72+
73+
#---- createbug - call to create a new bug
74+
75+
def _createbug(self,**data):
76+
'''Raw xmlrpc call for createBug() Doesn't bother guessing defaults
77+
or checking argument validity. Use with care.
78+
Returns bug_id'''
79+
r = self._proxy.Bug.create(data)
80+
return r['id']
81+
82+
#---- Methods for interacting with users
83+
84+
def _createuser(self, email, name=None, password=None):
85+
'''Create a new bugzilla user directly.
86+
87+
:arg email: email address for the new user
88+
:kwarg name: full name for the user
89+
:kwarg password: a password to use with the account
90+
'''
91+
userid = self._proxy.User.create(email, name, password)
92+
return userid
93+
94+
def _addcomment(self,id,comment,private=False,
95+
timestamp='',worktime='',bz_gid=''):
96+
'''Add a comment to the bug with the given ID. Other optional
97+
arguments are as follows:
98+
private: if True, mark this comment as private.
99+
timestamp: Ignored by BZ32.
100+
worktime: amount of time spent on this comment, in hours
101+
bz_gid: Ignored by BZ32.
102+
'''
103+
return self._proxy.Bug.add_comment({'id':id,
104+
'comment':comment,
105+
'private':private,
106+
'work_time':worktime})
107+
108+
def _getusers(self, ids=None, names=None, match=None):
109+
'''Return a list of users that match criteria.
110+
111+
:kwarg ids: list of user ids to return data on
112+
:kwarg names: list of user names to return data on
113+
:kwarg match: list of patterns. Returns users whose real name or
114+
login name match the pattern.
115+
:raises xmlrpclib.Fault: Code 51: if a Bad Login Name was sent to the
116+
names array.
117+
Code 304: if the user was not authorized to see user they
118+
requested.
119+
Code 505: user is logged out and can't use the match or ids
120+
parameter.
121+
122+
Available in Bugzilla-3.4+
123+
'''
124+
params = {}
125+
if ids:
126+
params['ids'] = ids
127+
if names:
128+
params['names'] = names
129+
if match:
130+
params['match'] = match
131+
if not params:
132+
raise bugzilla.base.NeedParamError('_get() needs one of ids,'
133+
' names, or match kwarg.')
134+
135+
return self._proxy.User.get(params)
136+
137+
def _query(self,query):
138+
'''Query bugzilla and return a list of matching bugs.
139+
query must be a dict with fields like those in in querydata['fields'].
140+
You can also pass in keys called 'quicksearch' or 'savedsearch' -
141+
'quicksearch' will do a quick keyword search like the simple search
142+
on the Bugzilla home page.
143+
'savedsearch' should be the name of a previously-saved search to
144+
execute. You need to be logged in for this to work.
145+
Returns a dict like this: {'bugs':buglist,
146+
'sql':querystring}
147+
buglist is a list of dicts describing bugs, and 'sql' contains the SQL
148+
generated by executing the search.
149+
You can also pass 'limit:[int]' to limit the number of results.
150+
For more info, see:
151+
http://www.bugzilla.org/docs/4.0/en/html/api/Bugzilla/WebService/Bug.html
152+
'''
153+
return self._proxy.Bug.search(query)
154+
155+
# Hooray, a proper _getbugfields implementation
156+
def _getbugfields(self):
157+
'''Get the list of valid fields for Bug objects'''
158+
r = self._proxy.Bug.fields({'include_fields':['name']})
159+
return [f['name'] for f in r['fields']]
160+
# NOTE: the RHBZ version lists 'comments' and 'groups', and strips
161+
# the 'cf_' from the beginning of custom fields.

0 commit comments

Comments
 (0)