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
272f54b716SRichard Lowe# Copyright 2010, Richard Lowe
28ca13eaa5SAndy Fiddaman# Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
292f54b716SRichard Lowe
30cdf0c1d5Smjnelson#
31cdf0c1d5Smjnelson# Various database lookup classes/methods, i.e.:
32cdf0c1d5Smjnelson#     * monaco
33cdf0c1d5Smjnelson#     * bugs.opensolaris.org (b.o.o.)
342f54b716SRichard Lowe#     * redmine (illumos.org)
35cdf0c1d5Smjnelson#
36cdf0c1d5Smjnelson
37cdf0c1d5Smjnelsonimport re
38ca13eaa5SAndy Fiddamantry:
39ca13eaa5SAndy Fiddaman	from urllib.request import urlopen, Request
40ca13eaa5SAndy Fiddaman	from urllib.error import HTTPError
41ca13eaa5SAndy Fiddamanexcept ImportError:
42ca13eaa5SAndy Fiddaman	# Python 2
43ca13eaa5SAndy Fiddaman	from urllib2 import Request, urlopen, HTTPError
44cdf0c1d5Smjnelson
452f54b716SRichard Lowetry:				# Python >= 2.5
462f54b716SRichard Lowe	from xml.etree import ElementTree
472f54b716SRichard Loweexcept ImportError:
482f54b716SRichard Lowe	from elementtree import ElementTree
49cdf0c1d5Smjnelson
5041a5f560SMark J. Nelsonclass NonExistentBug(Exception):
51cdf0c1d5Smjnelson	def __str__(self):
5241a5f560SMark J. Nelson		return "Bug %s does not exist" % (Exception.__str__(self))
53cdf0c1d5Smjnelson
54c08a253cSJohn Sonnenscheinclass BugDBException(Exception):
55c08a253cSJohn Sonnenschein	def __str__(self):
5641a5f560SMark J. Nelson		return "Unknown bug database: %s" % (Exception.__str__(self))
57cdf0c1d5Smjnelson
58c08a253cSJohn Sonnenscheinclass BugDB(object):
59c08a253cSJohn Sonnenschein	"""Lookup change requests.
60cdf0c1d5Smjnelson
61c08a253cSJohn Sonnenschein	Usage:
62c08a253cSJohn Sonnenschein	bdb = BugDB()
63c08a253cSJohn Sonnenschein	r = bdb.lookup("6455550")
64c08a253cSJohn Sonnenschein	print r["6455550"]["synopsis"]
65c08a253cSJohn Sonnenschein	r = bdb.lookup(["6455550", "6505625"])
66c08a253cSJohn Sonnenschein	print r["6505625"]["synopsis"]
67c08a253cSJohn Sonnenschein	"""
68cdf0c1d5Smjnelson
6937cc68d4SJoshua M. Clulow	VALID_DBS = ["illumos"]
702f54b716SRichard Lowe
7137cc68d4SJoshua M. Clulow	def __init__(self, priority = ["illumos"]):
72c08a253cSJohn Sonnenschein		"""Create a BugDB object.
73c08a253cSJohn Sonnenschein
74c08a253cSJohn Sonnenschein		Keyword argument:
75c08a253cSJohn Sonnenschein		priority: use bug databases in this order
76cdf0c1d5Smjnelson		"""
77c08a253cSJohn Sonnenschein		for database in priority:
782f54b716SRichard Lowe			if database not in self.VALID_DBS:
79ca13eaa5SAndy Fiddaman				raise BugDBException(database)
80c08a253cSJohn Sonnenschein		self.__priority = priority
81cdf0c1d5Smjnelson
82cdf0c1d5Smjnelson
832f54b716SRichard Lowe	def __illbug(self, cr):
842f54b716SRichard Lowe		url = "http://illumos.org/issues/%s.xml" % cr
85ca13eaa5SAndy Fiddaman		req = Request(url)
862f54b716SRichard Lowe
872f54b716SRichard Lowe		try:
88ca13eaa5SAndy Fiddaman			data = urlopen(req)
89ca13eaa5SAndy Fiddaman		except HTTPError as e:
90*ff02f816SToomas Soome			if e.code == 401 or e.code == 404:
912f54b716SRichard Lowe				raise NonExistentBug(cr)
922f54b716SRichard Lowe			else:
932f54b716SRichard Lowe				raise
942f54b716SRichard Lowe
952f54b716SRichard Lowe		bug = ElementTree.parse(data)
962f54b716SRichard Lowe
972f54b716SRichard Lowe		return {'cr_number': bug.find('id').text,
982f54b716SRichard Lowe			'synopsis': bug.find('subject').text,
992f54b716SRichard Lowe			'status': bug.find('status').attrib['name']
1002f54b716SRichard Lowe		}
1012f54b716SRichard Lowe
1022f54b716SRichard Lowe
103cdf0c1d5Smjnelson	def lookup(self, crs):
104cdf0c1d5Smjnelson		"""Return all info for requested change reports.
105cdf0c1d5Smjnelson
106cdf0c1d5Smjnelson		Argument:
107cdf0c1d5Smjnelson		crs: one change request id (may be integer, string, or list),
1082f54b716SRichard Lowe		     or multiple change request ids (must be a list)
109cdf0c1d5Smjnelson
110cdf0c1d5Smjnelson		Returns:
111cdf0c1d5Smjnelson		Dictionary, mapping CR=>dictionary, where the nested dictionary
112cdf0c1d5Smjnelson		is a mapping of field=>value
113cdf0c1d5Smjnelson		"""
114c08a253cSJohn Sonnenschein		results = {}
115cdf0c1d5Smjnelson		if not isinstance(crs, list):
116cdf0c1d5Smjnelson			crs = [str(crs)]
117c08a253cSJohn Sonnenschein		for database in self.__priority:
11837cc68d4SJoshua M. Clulow			if database == "illumos":
1192f54b716SRichard Lowe				for cr in crs:
1202f54b716SRichard Lowe					try:
1212f54b716SRichard Lowe						results[str(cr)] = self.__illbug(cr)
1222f54b716SRichard Lowe					except NonExistentBug:
1232f54b716SRichard Lowe						continue
124c08a253cSJohn Sonnenschein
125c08a253cSJohn Sonnenschein			# the CR has already been found by one bug database
126c08a253cSJohn Sonnenschein			# so don't bother looking it up in the others
127c08a253cSJohn Sonnenschein			for cr in crs:
128c08a253cSJohn Sonnenschein				if cr in results:
129c08a253cSJohn Sonnenschein					crs.remove(cr)
130cdf0c1d5Smjnelson
1312f54b716SRichard Lowe		return results
132