# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2014, Craig Tracey # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations import re import requests from pygerrit.rest import GerritRestAPI DEFAULT_GERRIT_URL = 'https://review.openstack.org' class GerritReview(object): def __init__(self, changeid, project, gerrit_url=DEFAULT_GERRIT_URL): self.changeid = changeid self.project = project self._gerrit_url = gerrit_url self._restclient = None def build_pip_dependencies(self, py26=False, py27=True, string=False): url = self._get_gate_build_log_url(py26, py27) response = requests.get(url) if response.status_code != 200: raise Exception("Unable to get console log at %s. Error: %d" % (url, response.status_code)) log = response.text.encode('utf-8') freeze_found = False dependencies = [] for line in log.split('\n'): line = re.sub('.*\|\s*', '', line) if not freeze_found: if line.endswith("pip freeze") or line.endswith("pbr freeze"): freeze_found = True continue elif re.match('[\w\-]+==.+', line) and not line.startswith('-e'): dependencies.append(line) short_name = self.project.split('/')[1] dependencies = filter(lambda x: not x.startswith(short_name + "=="), dependencies) if string: return (' ').join(dependencies) return dependencies def _get_rest_client(self): if not self._restclient: self._restclient = GerritRestAPI(url=self._gerrit_url) return self._restclient def _get_review_detail(self): """ get review details for a given change ID """ restclient = self._get_rest_client() url = "/changes/?q=%s" % self.changeid changes = restclient.get(url) change = None for c in changes: if c['project'] == self.project: change = c break if not change: raise Exception("could not find change with ID: %s" % self.changeid) detail = restclient.get("/changes/%s/detail" % change['id']) return detail def _get_reveiew_messages(self): details = self._get_review_detail() return details['messages'] def _get_gate_build_log_url(self, py26, py27): messages = self._get_reveiew_messages() messages.reverse() mergemsg = None for message in messages: msgtext = message['message'] if re.search('Patch Set \d+: Verified', msgtext): mergemsg = msgtext break gate_info = self._parse_merge_message(mergemsg) url = None for gate in gate_info: if py26 and re.match('gate\-.+\-python26', gate['name']): url = gate['url'] if py27 and re.match('gate\-.+\-python27', gate['name']): url = gate['url'] # check if it is console.html or console.html.gz resp = requests.get(url) if resp.status_code != 200: raise Exception("Unable to find the build's console log for %s" % url) build_log = None if 'console.html.gz' in resp.text: build_log = 'console.html.gz' elif 'console.html' in resp.text: build_log = 'console.html' else: raise Exception("Didn't find a build log. Does one exist?") if url: return "%s/%s" % (url, build_log) return url def _parse_merge_message(self, msg): """ a function that parses a successful gate gerrit message """ gate_info = [] for line in msg.split('\n'): parts = re.split('\s+', line) if parts[0] == '-': gate = {} gate['name'] = parts[1] gate['url'] = parts[2] gate['result'] = parts[4] gate_info.append(gate) return gate_info