1cdf0c1d5Smjnelson#! /usr/bin/python 2cdf0c1d5Smjnelson# 3cdf0c1d5Smjnelson# CDDL HEADER START 4cdf0c1d5Smjnelson# 5cdf0c1d5Smjnelson# The contents of this file are subject to the terms of the 6cdf0c1d5Smjnelson# Common Development and Distribution License (the "License"). 7cdf0c1d5Smjnelson# You may not use this file except in compliance with the License. 8cdf0c1d5Smjnelson# 9cdf0c1d5Smjnelson# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10cdf0c1d5Smjnelson# or http://www.opensolaris.org/os/licensing. 11cdf0c1d5Smjnelson# See the License for the specific language governing permissions 12cdf0c1d5Smjnelson# and limitations under the License. 13cdf0c1d5Smjnelson# 14cdf0c1d5Smjnelson# When distributing Covered Code, include this CDDL HEADER in each 15cdf0c1d5Smjnelson# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16cdf0c1d5Smjnelson# If applicable, add the following below this CDDL HEADER, with the 17cdf0c1d5Smjnelson# fields enclosed by brackets "[]" replaced with your own identifying 18cdf0c1d5Smjnelson# information: Portions Copyright [yyyy] [name of copyright owner] 19cdf0c1d5Smjnelson# 20cdf0c1d5Smjnelson# CDDL HEADER END 21cdf0c1d5Smjnelson# 22cdf0c1d5Smjnelson 23cdf0c1d5Smjnelson# 2484bb51dbSMark J. Nelson# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 25cdf0c1d5Smjnelson# 26cdf0c1d5Smjnelson 27*2f54b716SRichard Lowe# Copyright 2010, Richard Lowe 28*2f54b716SRichard Lowe 29cdf0c1d5Smjnelson# 30cdf0c1d5Smjnelson# Various database lookup classes/methods, i.e.: 31cdf0c1d5Smjnelson# * monaco 32cdf0c1d5Smjnelson# * bugs.opensolaris.org (b.o.o.) 33*2f54b716SRichard Lowe# * redmine (illumos.org) 34cdf0c1d5Smjnelson# 35cdf0c1d5Smjnelson 36*2f54b716SRichard Loweimport htmllib 37cdf0c1d5Smjnelsonimport re 38cdf0c1d5Smjnelsonimport urllib 39c08a253cSJohn Sonnenscheinimport urllib2 40cdf0c1d5Smjnelson 41*2f54b716SRichard Lowetry: # Python >= 2.5 42*2f54b716SRichard Lowe from xml.etree import ElementTree 43*2f54b716SRichard Loweexcept ImportError: 44*2f54b716SRichard Lowe from elementtree import ElementTree 45cdf0c1d5Smjnelson 4641a5f560SMark J. Nelsonclass NonExistentBug(Exception): 47cdf0c1d5Smjnelson def __str__(self): 4841a5f560SMark J. Nelson return "Bug %s does not exist" % (Exception.__str__(self)) 49cdf0c1d5Smjnelson 50c08a253cSJohn Sonnenscheinclass BugDBException(Exception): 51c08a253cSJohn Sonnenschein def __str__(self): 5241a5f560SMark J. Nelson return "Unknown bug database: %s" % (Exception.__str__(self)) 53cdf0c1d5Smjnelson 54cdf0c1d5Smjnelsonclass BugDB(object): 55cdf0c1d5Smjnelson """Lookup change requests. 56cdf0c1d5Smjnelson 57cdf0c1d5Smjnelson Usage: 58cdf0c1d5Smjnelson bdb = BugDB() 59cdf0c1d5Smjnelson r = bdb.lookup("6455550") 60cdf0c1d5Smjnelson print r["6455550"]["synopsis"] 61cdf0c1d5Smjnelson r = bdb.lookup(["6455550", "6505625"]) 62cdf0c1d5Smjnelson print r["6505625"]["synopsis"] 63cdf0c1d5Smjnelson """ 64cdf0c1d5Smjnelson 65*2f54b716SRichard Lowe VALID_DBS = ["bugster", "illumos"] 66*2f54b716SRichard Lowe 67*2f54b716SRichard Lowe def __init__(self, priority = ("illumos", "bugster")): 68cdf0c1d5Smjnelson """Create a BugDB object. 69cdf0c1d5Smjnelson 70cdf0c1d5Smjnelson Keyword argument: 71c08a253cSJohn Sonnenschein priority: use bug databases in this order 72cdf0c1d5Smjnelson """ 73c08a253cSJohn Sonnenschein for database in priority: 74*2f54b716SRichard Lowe if database not in self.VALID_DBS: 75c08a253cSJohn Sonnenschein raise BugDBException, database 76c08a253cSJohn Sonnenschein self.__priority = priority 77c08a253cSJohn Sonnenschein 78c08a253cSJohn Sonnenschein 79*2f54b716SRichard Lowe def __illbug(self, cr): 80*2f54b716SRichard Lowe url = "http://illumos.org/issues/%s.xml" % cr 81*2f54b716SRichard Lowe req = urllib2.Request(url) 82*2f54b716SRichard Lowe 83*2f54b716SRichard Lowe try: 84*2f54b716SRichard Lowe data = urllib2.urlopen(req) 85*2f54b716SRichard Lowe except urllib2.HTTPError, e: 86*2f54b716SRichard Lowe if e.code == 404: 87*2f54b716SRichard Lowe raise NonExistentBug(cr) 88*2f54b716SRichard Lowe else: 89*2f54b716SRichard Lowe raise 90*2f54b716SRichard Lowe 91*2f54b716SRichard Lowe bug = ElementTree.parse(data) 92*2f54b716SRichard Lowe 93*2f54b716SRichard Lowe return {'cr_number': bug.find('id').text, 94*2f54b716SRichard Lowe 'synopsis': bug.find('subject').text, 95*2f54b716SRichard Lowe 'status': bug.find('status').attrib['name'] 96*2f54b716SRichard Lowe } 97*2f54b716SRichard Lowe 98*2f54b716SRichard Lowe 99c08a253cSJohn Sonnenschein def __boobug(self, cr): 100c08a253cSJohn Sonnenschein cr = str(cr) 101c08a253cSJohn Sonnenschein url = "http://bugs.opensolaris.org/view_bug.do" 102c08a253cSJohn Sonnenschein req = urllib2.Request(url, urllib.urlencode({"bug_id": cr})) 103c08a253cSJohn Sonnenschein results = {} 104c08a253cSJohn Sonnenschein try: 105c08a253cSJohn Sonnenschein data = urllib2.urlopen(req).readlines() 106c08a253cSJohn Sonnenschein except urllib2.HTTPError, e: 107c08a253cSJohn Sonnenschein if e.code != 404: 108c08a253cSJohn Sonnenschein print "ERROR: HTTP error at " + \ 109c08a253cSJohn Sonnenschein req.get_full_url() + \ 110c08a253cSJohn Sonnenschein " got error: " + str(e.code) 111c08a253cSJohn Sonnenschein raise e 112cdf0c1d5Smjnelson else: 113*2f54b716SRichard Lowe raise NonExistentBug(cr) 114c08a253cSJohn Sonnenschein except urllib2.URLError, e: 115c08a253cSJohn Sonnenschein print "ERROR: could not connect to " + \ 116c08a253cSJohn Sonnenschein req.get_full_url() + \ 117c08a253cSJohn Sonnenschein ' got error: "' + e.reason[1] + '"' 118c08a253cSJohn Sonnenschein raise e 119c08a253cSJohn Sonnenschein htmlParser = htmllib.HTMLParser(None) 120c08a253cSJohn Sonnenschein metaHtmlRe = re.compile(r'^<meta name="([^"]+)" content="([^"]*)">$') 121c08a253cSJohn Sonnenschein for line in data: 122c08a253cSJohn Sonnenschein m = metaHtmlRe.search(line) 123c08a253cSJohn Sonnenschein if not m: 124c08a253cSJohn Sonnenschein continue 125c08a253cSJohn Sonnenschein val = urllib.unquote(m.group(2)) 126c08a253cSJohn Sonnenschein htmlParser.save_bgn() 127c08a253cSJohn Sonnenschein htmlParser.feed(val) 128c08a253cSJohn Sonnenschein results[m.group(1)] = htmlParser.save_end() 129c08a253cSJohn Sonnenschein htmlParser.close() 130c08a253cSJohn Sonnenschein 131c08a253cSJohn Sonnenschein if "synopsis" not in results: 132c08a253cSJohn Sonnenschein raise NonExistentBug(cr) 133c08a253cSJohn Sonnenschein 134c08a253cSJohn Sonnenschein results["cr_number"] = cr 135c08a253cSJohn Sonnenschein results["sub_category"] = results.pop("subcategory") 136c08a253cSJohn Sonnenschein results["status"] = results.pop("state") 137c08a253cSJohn Sonnenschein results["date_submitted"] = results.pop("submit_date") 138c08a253cSJohn Sonnenschein 139c08a253cSJohn Sonnenschein return results 140c08a253cSJohn Sonnenschein 141cdf0c1d5Smjnelson def lookup(self, crs): 142cdf0c1d5Smjnelson """Return all info for requested change reports. 143cdf0c1d5Smjnelson 144cdf0c1d5Smjnelson Argument: 145cdf0c1d5Smjnelson crs: one change request id (may be integer, string, or list), 146cdf0c1d5Smjnelson or multiple change request ids (must be a list) 147cdf0c1d5Smjnelson 148cdf0c1d5Smjnelson Returns: 149cdf0c1d5Smjnelson Dictionary, mapping CR=>dictionary, where the nested dictionary 150cdf0c1d5Smjnelson is a mapping of field=>value 151cdf0c1d5Smjnelson """ 152c08a253cSJohn Sonnenschein results = {} 153cdf0c1d5Smjnelson if not isinstance(crs, list): 154cdf0c1d5Smjnelson crs = [str(crs)] 155c08a253cSJohn Sonnenschein for database in self.__priority: 156c08a253cSJohn Sonnenschein if database == "bugster": 157cdf0c1d5Smjnelson for cr in crs: 158cdf0c1d5Smjnelson cr = str(cr) 159cdf0c1d5Smjnelson try: 160c08a253cSJohn Sonnenschein results[cr] = self.__boobug(cr) 161cdf0c1d5Smjnelson except NonExistentBug: 162cdf0c1d5Smjnelson continue 163*2f54b716SRichard Lowe elif database == "illumos": 164*2f54b716SRichard Lowe for cr in crs: 165*2f54b716SRichard Lowe try: 166*2f54b716SRichard Lowe results[str(cr)] = self.__illbug(cr) 167*2f54b716SRichard Lowe except NonExistentBug: 168*2f54b716SRichard Lowe continue 169cdf0c1d5Smjnelson 170c08a253cSJohn Sonnenschein # the CR has already been found by one bug database 171c08a253cSJohn Sonnenschein # so don't bother looking it up in the others 172c08a253cSJohn Sonnenschein for cr in crs: 173c08a253cSJohn Sonnenschein if cr in results: 174c08a253cSJohn Sonnenschein crs.remove(cr) 175cdf0c1d5Smjnelson 176cdf0c1d5Smjnelson return results 177