3 # This file is part of REPA: Release Engineering Process Assistant.
5 # Copyright (C) 2013 Intel Corporation
7 # REPA is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # version 2 as published by the Free Software Foundation.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 REPA: Release Engineering Process Assistant.
24 Copyright (C) Intel Corporation 2013
25 Licence: GPL version 2
26 Author: Ed Bartosh <eduard.bartosh@intel.com>
29 Interface module to access Open Build System.
31 This module is a intermediate step for new APIs
32 to oscapi. As soon as APIs are mature enough they
33 should be contributed to oscapi.
41 from base64 import b64encode
42 from xml.etree import cElementTree as ET
43 from StringIO import StringIO
44 from urllib2 import HTTPError
48 from gitbuildsys.oscapi import OSC, OSCError
49 from gitbuildsys.utils import Temp
51 from repa.common import RepaException, retry
54 OSCRC_TEMPLATE = """[general]
58 http_debug = %(http_debug)s
66 # pylint: disable=too-many-public-methods
68 """Interface to OBS API."""
70 def __init__(self, apiurl, apiuser, apipasswd):
71 oscrc = OSCRC_TEMPLATE % {
76 "passwdx": b64encode(apipasswd.encode('bz2'))}
79 tmpf = Temp(prefix='.oscrc', content=oscrc)
80 self.oscrcpath = tmpf.path
81 OSC.__init__(self, apiurl, self.oscrcpath)
84 @retry((OSCError, HTTPError))
85 def get_descr(self, project):
86 """Wrapper around get_description to be able to use @retry."""
87 return self.get_description(project)
89 def get_project_list(self, regexp=''):
90 """Get list of projects matching regexp."""
92 projects = core.meta_get_project_list(self.apiurl)
93 except OSCError as err:
94 raise RepaException("cat't get list of projects from %s: %s" %
98 if regexp and re.match(regexp, proj):
102 def get_projects(self, regexp='', processes=0):
103 """List projects with attributes."""
104 projects = list(self.get_project_list(regexp))
107 from multiprocessing.pool import ThreadPool
108 pool = ThreadPool(processes=processes)
110 for project in projects:
111 processes[project] = (
112 pool.apply_async(self.get_descr, [project]),
113 pool.apply_async(self.get_build_results, [project]))
115 for project in projects:
118 processes[project][0].get(),
119 processes[project][1].get())
122 self.get_descr(project),
123 self.get_build_results(project))
126 @retry((OSCError, HTTPError))
127 def get_build_results(self, prj):
128 """Get project build results."""
129 meta = core.show_prj_results_meta(self.apiurl, prj)
130 root = ET.fromstring(''.join(meta))
132 if not root.find('result'):
134 for results in root.findall('result'):
135 key = (results.get('repository'), results.get('arch'))
136 buildres[key] = dict([(field, results.get(field)) \
137 for field in ('code', 'state')])
140 code = node.get('code')
141 if node.get('code') != 'excluded':
142 pkgresults.append((node.get('package'), code))
143 buildres[key]['packages'] = pkgresults
147 @retry((OSCError, HTTPError))
148 def get_build_time(self, prj, repo, arch):
149 """Get build time for the project/repo/arch."""
150 url = core.makeurl(self.apiurl,
151 ['build', prj, repo, arch, '_jobhistory'])
152 history = self.core_http(core.http_GET, url)
153 history_root = ET.parse(history).getroot()
155 for node in history_root.findall('jobhist'):
156 seconds += int(node.get('endtime')) - int(node.get('starttime'))
159 def get_source_packages(self, prj):
160 """Get list of binary packages in the project."""
161 return iter(core.meta_get_packagelist(self.apiurl, prj))
164 def get_binary_packages(self, prj):
165 """Get list of binary packages in the project."""
166 for repo in core.get_repos_of_project(self.apiurl, prj):
167 yield (repo.name, repo.arch), core.get_binarylist(self.apiurl,
173 @retry((OSCError, HTTPError))
174 def aggregate_package(src_project, src_package, dst_project,
176 """Aggregate package. Wraps core.aggregate_pack."""
178 sys.stdout = StringIO()
180 core.aggregate_pac(src_project, src_package,
181 dst_project, dst_package)
185 return src_project, src_package, dst_project, dst_package
187 def create_sr(self, src_project, packages, tgt_project, message=''):
188 """Create submit request for the project."""
190 content = '<request><description>%s</description>' % \
191 cgi.escape(message.encode('utf-8'))
192 for package in packages:
193 content += '<action type="submit">'
194 content += '<source project="%s" package="%s"/>' % \
195 (src_project, package)
196 content += '<target project="%s" package="%s" />' % \
197 (tgt_project, package)
198 content += '</action>'
199 content += '</request>\n'
200 url = core.makeurl(self.apiurl, ['request'], query='cmd=create')
201 reply = self.core_http(core.http_POST, url, data=content)
202 return ET.parse(reply).getroot().get('id')
204 def set_sr_state(self, reqid, state, message='', force=False):
206 return core.change_request_state(self.apiurl, reqid, state,
207 message=message, force=force)
209 def get_srs(self, prj, states=None, pkg=None):
211 Get SRs for the project (and package).
214 prj (str): OBS project name
215 states (str): comma-separated list of states, e.g. 'new,accepted'
216 pkg (str): package name
219 id, state, description of found SRs
221 query = 'view=collection&types=submit&project=%s' % prj
223 query += '&states=%s' % states
225 query += '&package=%s' % pkg
226 url = core.makeurl(self.apiurl, ['request'], query)
228 root = ET.parse(self.core_http(core.http_GET, url))
229 for req in root.findall('request'):
230 yield req.get('id'), req.find('state').get('name'), \
231 req.find('description').text
233 def set_global_flag(self, flag, value, prj, pkg=None):
235 Set global flag in meta
236 Supported flag: publish,build,useforbuild,debuginfo
237 Supported values: enable,disable
239 supported_flags = ('publish', 'build', 'useforbuild', 'debuginfo')
240 if flag not in supported_flags:
241 raise RepaException("flag %s is not supported. "
242 "supported flags: %s" % \
243 (flag, ', '.join(supported_flags)))
244 supported_vals = ('enable', 'disable')
245 if value not in supported_vals:
246 raise RepaException("value %s is not supported. "
247 "supported values: %s" % \
248 (value, ', '.join(supported_vals)))
249 meta = self.get_meta(prj, pkg)
250 root = ET.fromstring(meta)
251 elem = root.find(flag)
253 elem = ET.SubElement(root, flag)
255 ET.SubElement(elem, value)
256 self.set_meta(ET.tostring(root), prj, pkg)
258 def get_file_list(self, prj, pkg):
259 """Get file list from OBS."""
260 url = core.makeurl(self.apiurl,
261 ['source', prj, pkg]);
264 xml = self.core_http(core.http_GET, url).read()
265 root = ET.fromstring(''.join(xml))
266 for entry in root.findall("entry"):
267 files.append(entry.get('name'))
269 except OSCError as error:
270 if error.message == "HTTP Error 404: Not Found":
274 def get_file(self, prj, pkg, fname, rev=None):
275 """Get file content from OBS."""
276 query = {'expand': 1}
279 encoded_fname = core.pathname2url(
280 fname.encode(locale.getpreferredencoding(), 'replace'))
281 url = core.makeurl(self.apiurl,
282 ['source', prj, pkg, encoded_fname],
285 return self.core_http(core.http_GET, url).read()
286 except OSCError as error:
287 if error.message == "HTTP Error 404: Not Found":
291 def get_package_list(self, prj, deleted=None):
292 """Get package list of the project"""
299 url = core.makeurl(self.apiurl, ['source', prj], query)
300 _file = core.http_GET(url)
301 root = ET.parse(_file).getroot()
302 return [node.get('name') for node in root.findall('entry')]