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