|
Michal Koutný |
ccf7f1 |
# This work is licensed under the GNU GPLv2 or later.
|
|
Michal Koutný |
ccf7f1 |
# See the COPYING file in the top-level directory.
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
from logging import getLogger
|
|
Michal Koutný |
ccf7f1 |
import sys
|
|
Michal Koutný |
ccf7f1 |
from xmlrpc.client import (Binary, Fault, ProtocolError,
|
|
Michal Koutný |
ccf7f1 |
ServerProxy, Transport)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
from requests import RequestException
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
from ._backendbase import _BackendBase
|
|
Michal Koutný |
ccf7f1 |
from .exceptions import BugzillaError
|
|
Michal Koutný |
ccf7f1 |
from ._util import listify
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
log = getLogger(__name__)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
class _BugzillaXMLRPCTransport(Transport):
|
|
Michal Koutný |
ccf7f1 |
def __init__(self, bugzillasession):
|
|
Michal Koutný |
ccf7f1 |
if hasattr(Transport, "__init__"):
|
|
Michal Koutný |
ccf7f1 |
Transport.__init__(self, use_datetime=False)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
self.__bugzillasession = bugzillasession
|
|
Michal Koutný |
ccf7f1 |
self.__bugzillasession.set_xmlrpc_defaults()
|
|
Michal Koutný |
ccf7f1 |
self.__seen_valid_xml = False
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
# Override Transport.user_agent
|
|
Michal Koutný |
ccf7f1 |
self.user_agent = self.__bugzillasession.get_user_agent()
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
############################
|
|
Michal Koutný |
ccf7f1 |
# Bugzilla private helpers #
|
|
Michal Koutný |
ccf7f1 |
############################
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def __request_helper(self, url, request_body):
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
A helper method to assist in making a request and parsing the response.
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
response = None
|
|
Michal Koutný |
ccf7f1 |
# pylint: disable=try-except-raise
|
|
Michal Koutný |
ccf7f1 |
# pylint: disable=raise-missing-from
|
|
Michal Koutný |
ccf7f1 |
try:
|
|
Michal Koutný |
ccf7f1 |
response = self.__bugzillasession.request(
|
|
Michal Koutný |
ccf7f1 |
"POST", url, data=request_body)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
return self.parse_response(response)
|
|
Michal Koutný |
ccf7f1 |
except RequestException as e:
|
|
Michal Koutný |
ccf7f1 |
if not response:
|
|
Michal Koutný |
ccf7f1 |
raise
|
|
Michal Koutný |
ccf7f1 |
raise ProtocolError( # pragma: no cover
|
|
Michal Koutný |
ccf7f1 |
url, response.status_code, str(e), response.headers)
|
|
Michal Koutný |
ccf7f1 |
except Fault:
|
|
Michal Koutný |
ccf7f1 |
raise
|
|
Michal Koutný |
ccf7f1 |
except Exception:
|
|
Michal Koutný |
ccf7f1 |
msg = str(sys.exc_info()[1])
|
|
Michal Koutný |
ccf7f1 |
if not self.__seen_valid_xml:
|
|
Michal Koutný |
ccf7f1 |
msg += "\nThe URL may not be an XMLRPC URL: %s" % url
|
|
Michal Koutný |
ccf7f1 |
e = BugzillaError(msg)
|
|
Michal Koutný |
ccf7f1 |
# pylint: disable=attribute-defined-outside-init
|
|
Michal Koutný |
ccf7f1 |
e.__traceback__ = sys.exc_info()[2]
|
|
Michal Koutný |
ccf7f1 |
# pylint: enable=attribute-defined-outside-init
|
|
Michal Koutný |
ccf7f1 |
raise e
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
######################
|
|
Michal Koutný |
ccf7f1 |
# Tranport overrides #
|
|
Michal Koutný |
ccf7f1 |
######################
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def parse_response(self, response):
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
Override Transport.parse_response
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
parser, unmarshaller = self.getparser()
|
|
Michal Koutný |
ccf7f1 |
msg = response.text.encode('utf-8')
|
|
Michal Koutný |
ccf7f1 |
try:
|
|
Michal Koutný |
ccf7f1 |
parser.feed(msg)
|
|
Michal Koutný |
ccf7f1 |
except Exception: # pragma: no cover
|
|
Michal Koutný |
ccf7f1 |
log.debug("Failed to parse this XMLRPC response:\n%s", msg)
|
|
Michal Koutný |
ccf7f1 |
raise
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
self.__seen_valid_xml = True
|
|
Michal Koutný |
ccf7f1 |
parser.close()
|
|
Michal Koutný |
ccf7f1 |
return unmarshaller.close()
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def request(self, host, handler, request_body, verbose=0):
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
Override Transport.request
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
# Setting self.verbose here matches overrided request() behavior
|
|
Michal Koutný |
ccf7f1 |
# pylint: disable=attribute-defined-outside-init
|
|
Michal Koutný |
ccf7f1 |
self.verbose = verbose
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
url = "%s://%s%s" % (self.__bugzillasession.get_scheme(),
|
|
Michal Koutný |
ccf7f1 |
host, handler)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
# xmlrpclib fails to escape \r
|
|
Michal Koutný |
ccf7f1 |
request_body = request_body.replace(b'\r', b'
')
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
return self.__request_helper(url, request_body)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
class _BugzillaXMLRPCProxy(ServerProxy, object):
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
Override of xmlrpc ServerProxy, to insert bugzilla API auth
|
|
Michal Koutný |
ccf7f1 |
into the XMLRPC request data
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
def __init__(self, uri, bugzillasession, *args, **kwargs):
|
|
Michal Koutný |
ccf7f1 |
self.__bugzillasession = bugzillasession
|
|
Michal Koutný |
ccf7f1 |
transport = _BugzillaXMLRPCTransport(self.__bugzillasession)
|
|
Michal Koutný |
ccf7f1 |
ServerProxy.__init__(self, uri, transport, *args, **kwargs)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def _ServerProxy__request(self, methodname, params):
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
Overrides ServerProxy _request method
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
# params is a singleton tuple, enforced by xmlrpc.client.dumps
|
|
Michal Koutný |
ccf7f1 |
newparams = params and params[0].copy() or {}
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
log.debug("XMLRPC call: %s(%s)", methodname, newparams)
|
|
Michal Koutný |
ccf7f1 |
authparams = self.__bugzillasession.get_auth_params()
|
|
Michal Koutný |
ccf7f1 |
authparams.update(newparams)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
# pylint: disable=no-member
|
|
Michal Koutný |
ccf7f1 |
ret = ServerProxy._ServerProxy__request(
|
|
Michal Koutný |
ccf7f1 |
self, methodname, (authparams,))
|
|
Michal Koutný |
ccf7f1 |
# pylint: enable=no-member
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
return ret
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
class _BackendXMLRPC(_BackendBase):
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
Internal interface for direct calls to bugzilla's XMLRPC API
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
def __init__(self, url, bugzillasession):
|
|
Michal Koutný |
ccf7f1 |
_BackendBase.__init__(self, url, bugzillasession)
|
|
Michal Koutný |
ccf7f1 |
self._xmlrpc_proxy = _BugzillaXMLRPCProxy(url, self._bugzillasession)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def get_xmlrpc_proxy(self):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy
|
|
Michal Koutný |
ccf7f1 |
def is_xmlrpc(self):
|
|
Michal Koutný |
ccf7f1 |
return True
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def bugzilla_version(self):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bugzilla.version()
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def bug_attachment_get(self, attachment_ids, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["attachment_ids"] = listify(attachment_ids)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.attachments(data)
|
|
Michal Koutný |
ccf7f1 |
def bug_attachment_get_all(self, bug_ids, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["ids"] = listify(bug_ids)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.attachments(data)
|
|
Michal Koutný |
ccf7f1 |
def bug_attachment_create(self, bug_ids, data, paramdict):
|
|
Michal Koutný |
ccf7f1 |
pdata = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
pdata["ids"] = listify(bug_ids)
|
|
Michal Koutný |
ccf7f1 |
if data is not None and "data" not in paramdict:
|
|
Michal Koutný |
ccf7f1 |
pdata["data"] = Binary(data)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.add_attachment(pdata)
|
|
Michal Koutný |
ccf7f1 |
def bug_attachment_update(self, attachment_ids, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["ids"] = listify(attachment_ids)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.update_attachment(data)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def bug_comments(self, bug_ids, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["ids"] = listify(bug_ids)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.comments(data)
|
|
Michal Koutný |
ccf7f1 |
def bug_create(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.create(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def bug_fields(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.fields(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def bug_get(self, bug_ids, aliases, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["ids"] = listify(bug_ids) or []
|
|
Michal Koutný |
ccf7f1 |
data["ids"] += listify(aliases) or []
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.get(data)
|
|
Michal Koutný |
ccf7f1 |
def bug_history(self, bug_ids, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["ids"] = listify(bug_ids)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.history(data)
|
|
Michal Koutný |
ccf7f1 |
def bug_search(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.search(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def bug_update(self, bug_ids, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["ids"] = listify(bug_ids)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.update(data)
|
|
Michal Koutný |
ccf7f1 |
def bug_update_tags(self, bug_ids, paramdict):
|
|
Michal Koutný |
ccf7f1 |
data = paramdict.copy()
|
|
Michal Koutný |
ccf7f1 |
data["ids"] = listify(bug_ids)
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Bug.update_tags(data)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def component_create(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Component.create(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def component_update(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Component.update(paramdict)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def externalbugs_add(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.ExternalBugs.add_external_bug(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def externalbugs_update(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.ExternalBugs.update_external_bug(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def externalbugs_remove(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.ExternalBugs.remove_external_bug(paramdict)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def group_get(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Group.get(paramdict)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def product_get(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Product.get(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def product_get_accessible(self):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Product.get_accessible_products()
|
|
Michal Koutný |
ccf7f1 |
def product_get_enterable(self):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Product.get_enterable_products()
|
|
Michal Koutný |
ccf7f1 |
def product_get_selectable(self):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.Product.get_selectable_products()
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def user_create(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.User.create(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def user_get(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.User.get(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def user_login(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.User.login(paramdict)
|
|
Michal Koutný |
ccf7f1 |
def user_logout(self):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.User.logout()
|
|
Michal Koutný |
ccf7f1 |
def user_update(self, paramdict):
|
|
Michal Koutný |
ccf7f1 |
return self._xmlrpc_proxy.User.update(paramdict)
|