xref: /titanic_44/usr/src/tools/onbld/Checks/DbLookups.py (revision 84bb51db52515f628cb1781523b3574cbfe72c74)
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#
24*84bb51dbSMark J. Nelson# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
25cdf0c1d5Smjnelson#
26cdf0c1d5Smjnelson
27cdf0c1d5Smjnelson#
28cdf0c1d5Smjnelson# Various database lookup classes/methods, i.e.:
29cdf0c1d5Smjnelson#     * monaco
30cdf0c1d5Smjnelson#     * bugs.opensolaris.org (b.o.o.)
31*84bb51dbSMark J. Nelson#     * arc.opensolaris.org/cgi-bin/arc.cgi (for ARC off SWAN)
32*84bb51dbSMark J. Nelson#     * candi.sfbay.sun.com/cgi-bin/arc.cgi (for ARC on SWAN)
33cdf0c1d5Smjnelson#
34cdf0c1d5Smjnelson
3541a5f560SMark J. Nelsonimport csv
36cdf0c1d5Smjnelsonimport re
37cdf0c1d5Smjnelsonimport urllib
38c08a253cSJohn Sonnenscheinimport urllib2
39cdf0c1d5Smjnelsonimport htmllib
40cdf0c1d5Smjnelsonimport os
41cdf0c1d5Smjnelsonfrom socket import socket, AF_INET, SOCK_STREAM
42cdf0c1d5Smjnelson
43cdf0c1d5Smjnelsonfrom onbld.Checks import onSWAN
44cdf0c1d5Smjnelson
4541a5f560SMark J. Nelsonclass NonExistentBug(Exception):
46cdf0c1d5Smjnelson	def __str__(self):
4741a5f560SMark J. Nelson		return "Bug %s does not exist" % (Exception.__str__(self))
48cdf0c1d5Smjnelson
49c08a253cSJohn Sonnenscheinclass BugDBException(Exception):
50c08a253cSJohn Sonnenschein	def __str__(self):
5141a5f560SMark J. Nelson		return "Unknown bug database: %s" % (Exception.__str__(self))
52cdf0c1d5Smjnelson
53cdf0c1d5Smjnelsonclass BugDB(object):
54cdf0c1d5Smjnelson	"""Lookup change requests.
55cdf0c1d5Smjnelson
56cdf0c1d5Smjnelson	Object can be used on or off of SWAN, using either monaco or
57cdf0c1d5Smjnelson	bugs.opensolaris.org as a database.
58cdf0c1d5Smjnelson
59cdf0c1d5Smjnelson	Usage:
60cdf0c1d5Smjnelson	bdb = BugDB()
61cdf0c1d5Smjnelson	r = bdb.lookup("6455550")
62cdf0c1d5Smjnelson	print r["6455550"]["synopsis"]
63cdf0c1d5Smjnelson	r = bdb.lookup(["6455550", "6505625"])
64cdf0c1d5Smjnelson	print r["6505625"]["synopsis"]
65cdf0c1d5Smjnelson	"""
66cdf0c1d5Smjnelson
67c08a253cSJohn Sonnenschein	def __init__(self, priority = ("bugster",), forceBoo=False):
68cdf0c1d5Smjnelson		"""Create a BugDB object.
69cdf0c1d5Smjnelson
70cdf0c1d5Smjnelson		Keyword argument:
71cdf0c1d5Smjnelson		forceBoo: use b.o.o even from SWAN (default=False)
72c08a253cSJohn Sonnenschein		priority: use bug databases in this order
73cdf0c1d5Smjnelson		"""
74c08a253cSJohn Sonnenschein		self.__validBugDB = ["bugster"]
75c08a253cSJohn Sonnenschein		self.__onSWAN = not forceBoo and onSWAN()
76c08a253cSJohn Sonnenschein		for database in priority:
77c08a253cSJohn Sonnenschein			if database not in self.__validBugDB:
78c08a253cSJohn Sonnenschein				raise BugDBException, database
79c08a253cSJohn Sonnenschein		self.__priority = priority
80c08a253cSJohn Sonnenschein
81c08a253cSJohn Sonnenschein
82c08a253cSJohn Sonnenschein	def __boobug(self, cr):
83c08a253cSJohn Sonnenschein		cr = str(cr)
84c08a253cSJohn Sonnenschein		url = "http://bugs.opensolaris.org/view_bug.do"
85c08a253cSJohn Sonnenschein   		req = urllib2.Request(url, urllib.urlencode({"bug_id": cr}))
86c08a253cSJohn Sonnenschein		results = {}
87c08a253cSJohn Sonnenschein		try:
88c08a253cSJohn Sonnenschein			data = urllib2.urlopen(req).readlines()
89c08a253cSJohn Sonnenschein		except urllib2.HTTPError, e:
90c08a253cSJohn Sonnenschein			if e.code != 404:
91c08a253cSJohn Sonnenschein				print "ERROR: HTTP error at " + \
92c08a253cSJohn Sonnenschein					req.get_full_url() + \
93c08a253cSJohn Sonnenschein					" got error: " + str(e.code)
94c08a253cSJohn Sonnenschein				raise e
95cdf0c1d5Smjnelson			else:
96c08a253cSJohn Sonnenschein				raise NonExistentBug
97c08a253cSJohn Sonnenschein		except urllib2.URLError, e:
98c08a253cSJohn Sonnenschein			print "ERROR: could not connect to " + \
99c08a253cSJohn Sonnenschein				req.get_full_url() + \
100c08a253cSJohn Sonnenschein				' got error: "' + e.reason[1] + '"'
101c08a253cSJohn Sonnenschein			raise e
102c08a253cSJohn Sonnenschein		htmlParser = htmllib.HTMLParser(None)
103c08a253cSJohn Sonnenschein		metaHtmlRe = re.compile(r'^<meta name="([^"]+)" content="([^"]*)">$')
104c08a253cSJohn Sonnenschein		for line in data:
105c08a253cSJohn Sonnenschein			m = metaHtmlRe.search(line)
106c08a253cSJohn Sonnenschein			if not m:
107c08a253cSJohn Sonnenschein				continue
108c08a253cSJohn Sonnenschein			val = urllib.unquote(m.group(2))
109c08a253cSJohn Sonnenschein			htmlParser.save_bgn()
110c08a253cSJohn Sonnenschein			htmlParser.feed(val)
111c08a253cSJohn Sonnenschein			results[m.group(1)] = htmlParser.save_end()
112c08a253cSJohn Sonnenschein		htmlParser.close()
113c08a253cSJohn Sonnenschein
114c08a253cSJohn Sonnenschein		if "synopsis" not in results:
115c08a253cSJohn Sonnenschein			raise NonExistentBug(cr)
116c08a253cSJohn Sonnenschein
117c08a253cSJohn Sonnenschein		results["cr_number"] = cr
118c08a253cSJohn Sonnenschein		results["sub_category"] = results.pop("subcategory")
119c08a253cSJohn Sonnenschein		results["status"] = results.pop("state")
120c08a253cSJohn Sonnenschein		results["date_submitted"] = results.pop("submit_date")
121c08a253cSJohn Sonnenschein
122c08a253cSJohn Sonnenschein		return results
123c08a253cSJohn Sonnenschein
124c08a253cSJohn Sonnenschein
125c08a253cSJohn Sonnenschein	def __monaco(self, crs):
126c08a253cSJohn Sonnenschein		"""Return all info for requested change reports.
127c08a253cSJohn Sonnenschein
128c08a253cSJohn Sonnenschein		Argument:
129c08a253cSJohn Sonnenschein		crs: list of change request ids
130c08a253cSJohn Sonnenschein
131c08a253cSJohn Sonnenschein		Returns:
132c08a253cSJohn Sonnenschein		Dictionary, mapping CR=>dictionary, where the nested dictionary
133c08a253cSJohn Sonnenschein		is a mapping of field=>value
134c08a253cSJohn Sonnenschein		"""
135c08a253cSJohn Sonnenschein
136c08a253cSJohn Sonnenschein		#
13752ddba99SOndrej Kubecka		# See if 'maxcrs' for maximal batch query size is defined
13852ddba99SOndrej Kubecka		# if not, default to 200.
13952ddba99SOndrej Kubecka		# This clears the 2499 chars query limit
14052ddba99SOndrej Kubecka		#
14152ddba99SOndrej Kubecka		try:
14252ddba99SOndrej Kubecka			maxcrs
14352ddba99SOndrej Kubecka		except NameError:
14452ddba99SOndrej Kubecka			maxcrs = 200
14552ddba99SOndrej Kubecka
14652ddba99SOndrej Kubecka		i = 0
14752ddba99SOndrej Kubecka		results = {}
14852ddba99SOndrej Kubecka		data = []
14952ddba99SOndrej Kubecka
15052ddba99SOndrej Kubecka		while i < len(crs):
15152ddba99SOndrej Kubecka			if len(crs) < ( i + maxcrs ):
15252ddba99SOndrej Kubecka				j = len(crs)
15352ddba99SOndrej Kubecka			else:
15452ddba99SOndrej Kubecka				j = i + maxcrs
15552ddba99SOndrej Kubecka
15652ddba99SOndrej Kubecka			crstmp=crs[i:j]
15752ddba99SOndrej Kubecka
15852ddba99SOndrej Kubecka			#
159c08a253cSJohn Sonnenschein			# We request synopsis last, and split on only
160c08a253cSJohn Sonnenschein			# the number of separators that we expect to
161c08a253cSJohn Sonnenschein			# see such that a | in the synopsis doesn't
162c08a253cSJohn Sonnenschein			# throw us out of whack.
163c08a253cSJohn Sonnenschein			#
164c08a253cSJohn Sonnenschein			monacoFields = [ "cr_number", "category", "sub_category",
165c08a253cSJohn Sonnenschein				"area", "release", "build", "responsible_manager",
166c08a253cSJohn Sonnenschein				"responsible_engineer", "priority", "status", "sub_status",
167c08a253cSJohn Sonnenschein				"submitted_by", "date_submitted", "synopsis" ]
168c08a253cSJohn Sonnenschein			cmd = []
169c08a253cSJohn Sonnenschein			cmd.append("set What = cr." + ', cr.'.join(monacoFields))
170c08a253cSJohn Sonnenschein			cmd.append("")
17152ddba99SOndrej Kubecka			cmd.append("set Which = cr.cr_number in (" + ','.join(crstmp) +")")
172c08a253cSJohn Sonnenschein			cmd.append("")
173c08a253cSJohn Sonnenschein			cmd.append("set FinalClauses = order by cr.cr_number")
174c08a253cSJohn Sonnenschein			cmd.append("")
175c08a253cSJohn Sonnenschein			cmd.append("doMeta genQuery cr")
176c08a253cSJohn Sonnenschein			url = "http://hestia.sfbay.sun.com/cgi-bin/expert?format="
177c08a253cSJohn Sonnenschein			url += "Pipe-delimited+text;Go=2;no_header=on;cmds="
178c08a253cSJohn Sonnenschein			url += urllib.quote_plus("\n".join(cmd))
179c08a253cSJohn Sonnenschein			try:
18052ddba99SOndrej Kubecka				data += urllib2.urlopen(url).readlines()
181c08a253cSJohn Sonnenschein			except urllib2.HTTPError, e:
182c08a253cSJohn Sonnenschein				print "ERROR: HTTP error at " + url + \
183c08a253cSJohn Sonnenschein					" got error: " + str(e.code)
184c08a253cSJohn Sonnenschein				raise e
185c08a253cSJohn Sonnenschein
186c08a253cSJohn Sonnenschein			except urllib2.URLError, e:
187c08a253cSJohn Sonnenschein				print "ERROR: could not connect to " + url + \
188c08a253cSJohn Sonnenschein					' got error: "' + e.reason[1] + '"'
189c08a253cSJohn Sonnenschein				raise e
19052ddba99SOndrej Kubecka
19152ddba99SOndrej Kubecka			i += maxcrs
19252ddba99SOndrej Kubecka
193c08a253cSJohn Sonnenschein		for line in data:
194c08a253cSJohn Sonnenschein			line = line.rstrip('\n')
195c08a253cSJohn Sonnenschein			values = line.split('|', len(monacoFields) - 1)
196c08a253cSJohn Sonnenschein			v = 0
197c08a253cSJohn Sonnenschein			cr = values[0]
198c08a253cSJohn Sonnenschein			results[cr] = {}
199c08a253cSJohn Sonnenschein			for field in monacoFields:
200c08a253cSJohn Sonnenschein				results[cr][field] = values[v]
201c08a253cSJohn Sonnenschein				v += 1
20252ddba99SOndrej Kubecka
20352ddba99SOndrej Kubecka
204c08a253cSJohn Sonnenschein		return results
205cdf0c1d5Smjnelson
206cdf0c1d5Smjnelson	def lookup(self, crs):
207cdf0c1d5Smjnelson		"""Return all info for requested change reports.
208cdf0c1d5Smjnelson
209cdf0c1d5Smjnelson		Argument:
210cdf0c1d5Smjnelson		crs: one change request id (may be integer, string, or list),
211cdf0c1d5Smjnelson	             or multiple change request ids (must be a list)
212cdf0c1d5Smjnelson
213cdf0c1d5Smjnelson		Returns:
214cdf0c1d5Smjnelson		Dictionary, mapping CR=>dictionary, where the nested dictionary
215cdf0c1d5Smjnelson		is a mapping of field=>value
216cdf0c1d5Smjnelson		"""
217c08a253cSJohn Sonnenschein		results = {}
218cdf0c1d5Smjnelson		if not isinstance(crs, list):
219cdf0c1d5Smjnelson			crs = [str(crs)]
220c08a253cSJohn Sonnenschein		for database in self.__priority:
221c08a253cSJohn Sonnenschein			if database == "bugster":
222cdf0c1d5Smjnelson				if self.__onSWAN:
223c08a253cSJohn Sonnenschein					results.update(self.__monaco(crs))
224cdf0c1d5Smjnelson				# else we're off-swan and querying via boo, which we can
225cdf0c1d5Smjnelson				# only do one bug at a time
226c08a253cSJohn Sonnenschein				else:
227cdf0c1d5Smjnelson					for cr in crs:
228cdf0c1d5Smjnelson						cr = str(cr)
229cdf0c1d5Smjnelson						try:
230c08a253cSJohn Sonnenschein							results[cr] = self.__boobug(cr)
231cdf0c1d5Smjnelson						except NonExistentBug:
232cdf0c1d5Smjnelson							continue
233cdf0c1d5Smjnelson
234c08a253cSJohn Sonnenschein			# the CR has already been found by one bug database
235c08a253cSJohn Sonnenschein			# so don't bother looking it up in the others
236c08a253cSJohn Sonnenschein			for cr in crs:
237c08a253cSJohn Sonnenschein				if cr in results:
238c08a253cSJohn Sonnenschein					crs.remove(cr)
239cdf0c1d5Smjnelson
240cdf0c1d5Smjnelson		return results
241cdf0c1d5Smjnelson####################################################################
24241a5f560SMark J. Nelsonclass ARCException(Exception):
24341a5f560SMark J. Nelson	"""This covers arc.cgi script failure."""
24441a5f560SMark J. Nelson	def __str__(self):
24541a5f560SMark J. Nelson		return "Error retrieving ARC data: %s" % (Exception.__str__(self))
24641a5f560SMark J. Nelson
24741a5f560SMark J. Nelsondef ARC(arclist, arcPath=None):
24841a5f560SMark J. Nelson	if not arcPath:
249*84bb51dbSMark J. Nelson		if onSWAN():
250*84bb51dbSMark J. Nelson			arcPath = "http://candi.sfbay.sun.com/cgi-bin/arc.cgi"
251*84bb51dbSMark J. Nelson		else:
25241a5f560SMark J. Nelson			arcPath = "http://arc.opensolaris.org/cgi-bin/arc.cgi"
25341a5f560SMark J. Nelson	fields = ["present", "arc", "year", "case", "status", "title"]
25441a5f560SMark J. Nelson	opts = [("case", "%s/%s" % (a, c)) for a, c in arclist]
25541a5f560SMark J. Nelson	req = urllib2.Request(arcPath, urllib.urlencode(opts))
256c08a253cSJohn Sonnenschein	try:
257c08a253cSJohn Sonnenschein		data = urllib2.urlopen(req).readlines()
258c08a253cSJohn Sonnenschein	except urllib2.HTTPError, e:
2597199625bSJohn Sonnenschein		print "ERROR: HTTP error at " + req.get_full_url() + \
260c08a253cSJohn Sonnenschein			" got error: " + str(e.code)
261c08a253cSJohn Sonnenschein		raise e
262cdf0c1d5Smjnelson
263c08a253cSJohn Sonnenschein	except urllib2.URLError, e:
2647199625bSJohn Sonnenschein		print "ERROR: could not connect to " + req.get_full_url() + \
265c08a253cSJohn Sonnenschein			' got error: "' + e.reason[1] + '"'
266c08a253cSJohn Sonnenschein		raise e
267c08a253cSJohn Sonnenschein	ret = {}
26841a5f560SMark J. Nelson	for line in csv.DictReader(data, fields):
26941a5f560SMark J. Nelson		if line["present"] == "exists":
27041a5f560SMark J. Nelson			yc = "%s/%s" % (line["year"], line["case"])
27141a5f560SMark J. Nelson			ret[(line["arc"], yc)] = line["title"]
27241a5f560SMark J. Nelson		elif line["present"] == "fatal":
27341a5f560SMark J. Nelson			raise ARCException(line["arc"])
27441a5f560SMark J. Nelson
275c08a253cSJohn Sonnenschein	return ret
276cdf0c1d5Smjnelson
277cdf0c1d5Smjnelson####################################################################
278cdf0c1d5Smjnelson
279cdf0c1d5Smjnelson# Pointers to the webrti server hostname & port to use
280cdf0c1d5Smjnelson# Using it directly is probably not *officially* supported, so we'll
281cdf0c1d5Smjnelson# have a pointer to the official `webrticli` command line interface
282cdf0c1d5Smjnelson# if using a direct socket connection fails for some reason, so we
283cdf0c1d5Smjnelson# have a fallback
284cdf0c1d5SmjnelsonWEBRTI_HOST = 'webrti.sfbay.sun.com'
285cdf0c1d5SmjnelsonWEBRTI_PORT = 9188
28681540946SJohn SonnenscheinWEBRTICLI = '/net/onnv.sfbay.sun.com/export/onnv-gate/public/bin/webrticli'
287cdf0c1d5Smjnelson
288cdf0c1d5Smjnelson
289cdf0c1d5Smjnelsonclass RtiException(Exception):
29041a5f560SMark J. Nelson	pass
291cdf0c1d5Smjnelson
292cdf0c1d5Smjnelsonclass RtiCallFailed(RtiException):
293cdf0c1d5Smjnelson	def __str__(self):
29441a5f560SMark J. Nelson		return "Unable to call webrti: %s" % (RtiException.__str__(self))
295cdf0c1d5Smjnelson
296cdf0c1d5Smjnelsonclass RtiSystemProblem(RtiException):
297cdf0c1d5Smjnelson	def __str__(self):
29841a5f560SMark J. Nelson		return "RTI status cannot be determined for CR: %s" % (RtiException.__str__(self))
299cdf0c1d5Smjnelson
300cdf0c1d5Smjnelsonclass RtiIncorrectCR(RtiException):
301cdf0c1d5Smjnelson	def __str__(self):
30241a5f560SMark J. Nelson		return "Incorrect CR number specified: %s" % (RtiException.__str__(self))
303cdf0c1d5Smjnelson
304cdf0c1d5Smjnelsonclass RtiNotFound(RtiException):
305cdf0c1d5Smjnelson	def __str__(self):
30641a5f560SMark J. Nelson		return "RTI not found for CR: %s" % (RtiException.__str__(self))
307cdf0c1d5Smjnelson
308cdf0c1d5Smjnelsonclass RtiNeedConsolidation(RtiException):
309cdf0c1d5Smjnelson	def __str__(self):
31041a5f560SMark J. Nelson		return "More than one consolidation has this CR: %s" % (RtiException.__str__(self))
311cdf0c1d5Smjnelson
312cdf0c1d5Smjnelsonclass RtiBadGate(RtiException):
313cdf0c1d5Smjnelson	def __str__(self):
31441a5f560SMark J. Nelson		return "Incorrect gate name specified: %s" % (RtiException.__str__(self))
31541a5f560SMark J. Nelson
31641a5f560SMark J. Nelsonclass RtiUnknownException(Exception):
31741a5f560SMark J. Nelson	def __str__(self):
31841a5f560SMark J. Nelson		return "Unknown webrti return code: %s" % (RtiException.__str__(self))
319cdf0c1d5Smjnelson
320cdf0c1d5Smjnelsonclass RtiOffSwan(RtiException):
321cdf0c1d5Smjnelson	def __str__(self):
32241a5f560SMark J. Nelson		return "RTI status checks need SWAN access: %s" % (RtiException.__str__(self))
323cdf0c1d5Smjnelson
324cdf0c1d5SmjnelsonWEBRTI_ERRORS = {
325cdf0c1d5Smjnelson	'1': RtiSystemProblem,
326cdf0c1d5Smjnelson	'2': RtiIncorrectCR,
327cdf0c1d5Smjnelson	'3': RtiNotFound,
328cdf0c1d5Smjnelson	'4': RtiNeedConsolidation,
329cdf0c1d5Smjnelson	'5': RtiBadGate,
330cdf0c1d5Smjnelson}
331cdf0c1d5Smjnelson
332cdf0c1d5Smjnelson# Our Rti object which we'll use to represent an Rti query
333cdf0c1d5Smjnelson# It's really just a wrapper around the Rti connection, and attempts
334cdf0c1d5Smjnelson# to establish a direct socket connection and query the webrti server
335cdf0c1d5Smjnelson# directly (thus avoiding a system/fork/exec call).  If it fails, it
336cdf0c1d5Smjnelson# falls back to the webrticli command line client.
337cdf0c1d5Smjnelson
338cdf0c1d5SmjnelsonreturnCodeRe = re.compile(r'.*RETURN_CODE=(\d+)')
339cdf0c1d5Smjnelsonclass Rti:
340cdf0c1d5Smjnelson	"""Lookup an RTI.
341cdf0c1d5Smjnelson
342cdf0c1d5Smjnelson	Usage:
343cdf0c1d5Smjnelson	r = Rti("6640538")
344cdf0c1d5Smjnelson	print r.rtiNumber();
345cdf0c1d5Smjnelson	"""
346cdf0c1d5Smjnelson
347cdf0c1d5Smjnelson	def __init__(self, cr, gate=None, consolidation=None):
348cdf0c1d5Smjnelson		"""Create an Rti object for the specified change request.
349cdf0c1d5Smjnelson
350cdf0c1d5Smjnelson		Argument:
351cdf0c1d5Smjnelson		cr: change request id
352cdf0c1d5Smjnelson
353cdf0c1d5Smjnelson		Keyword arguments, to limit scope of RTI search:
354cdf0c1d5Smjnelson		gate: path to gate workspace (default=None)
355cdf0c1d5Smjnelson		consolidation: consolidation name (default=None)
356cdf0c1d5Smjnelson		"""
357cdf0c1d5Smjnelson
358cdf0c1d5Smjnelson		bufSz = 1024
359cdf0c1d5Smjnelson		addr = (WEBRTI_HOST, WEBRTI_PORT)
360cdf0c1d5Smjnelson		# If the passed 'cr' was given as an int, then wrap it
361cdf0c1d5Smjnelson		# into a string to make our life easier
362cdf0c1d5Smjnelson		if isinstance(cr, int):
363cdf0c1d5Smjnelson			cr = str(cr)
364cdf0c1d5Smjnelson		self.__queryCr = cr
365cdf0c1d5Smjnelson		self.__queryGate = gate
366cdf0c1d5Smjnelson		self.__queryConsolidation = consolidation
367cdf0c1d5Smjnelson
36881540946SJohn Sonnenschein		self.__webRtiOutput = []
36981540946SJohn Sonnenschein		self.__mainCR = []
37081540946SJohn Sonnenschein		self.__rtiNumber = []
37181540946SJohn Sonnenschein		self.__consolidation = []
37281540946SJohn Sonnenschein		self.__project = []
37381540946SJohn Sonnenschein		self.__status = []
37481540946SJohn Sonnenschein		self.__rtiType = []
375cdf0c1d5Smjnelson		try:
376cdf0c1d5Smjnelson			# try to use a direct connection to the
377cdf0c1d5Smjnelson			# webrti server first
378cdf0c1d5Smjnelson			sock = socket(AF_INET, SOCK_STREAM)
379cdf0c1d5Smjnelson			sock.connect(addr)
380cdf0c1d5Smjnelson			command = "WEBRTICLI/1.0\nRTIstatus\n%s\n" % cr
381cdf0c1d5Smjnelson			if consolidation:
382cdf0c1d5Smjnelson				command += "-c\n%s\n" % consolidation
383cdf0c1d5Smjnelson			if gate:
384cdf0c1d5Smjnelson				command += "-g\n%s\n" % gate
385cdf0c1d5Smjnelson			command += "\n"
386cdf0c1d5Smjnelson			sock.send(command)
387cdf0c1d5Smjnelson			dataList = []
388cdf0c1d5Smjnelson			# keep receiving data from the socket until the
389cdf0c1d5Smjnelson			# server closes the connection
390cdf0c1d5Smjnelson			stillReceiving = True
391cdf0c1d5Smjnelson			while stillReceiving:
392cdf0c1d5Smjnelson				dataPiece = sock.recv(bufSz)
393cdf0c1d5Smjnelson				if dataPiece:
394cdf0c1d5Smjnelson					dataList.append(dataPiece)
395cdf0c1d5Smjnelson				else:
396cdf0c1d5Smjnelson					stillReceiving = False
397cdf0c1d5Smjnelson			# create the lines, skipping the first
398cdf0c1d5Smjnelson			# ("WEBRTCLI/1.0\n")
399cdf0c1d5Smjnelson			data = '\n'.join(''.join(dataList).split('\n')[1:])
400cdf0c1d5Smjnelson		except:
401cdf0c1d5Smjnelson			if not onSWAN():
402cdf0c1d5Smjnelson				raise RtiOffSwan(cr)
403cdf0c1d5Smjnelson
404cdf0c1d5Smjnelson			if not os.path.exists(WEBRTICLI):
405cdf0c1d5Smjnelson				raise RtiCallFailed('not found')
406cdf0c1d5Smjnelson
407cdf0c1d5Smjnelson			# fallback to the "supported" webrticli interface
408cdf0c1d5Smjnelson			command = WEBRTICLI
409cdf0c1d5Smjnelson			if consolidation:
410cdf0c1d5Smjnelson				command += " -c " + consolidation
411cdf0c1d5Smjnelson			if gate:
412cdf0c1d5Smjnelson				command += " -g " + gate
413cdf0c1d5Smjnelson			command += " RTIstatus " + cr
414cdf0c1d5Smjnelson
415cdf0c1d5Smjnelson			try:
416cdf0c1d5Smjnelson				cliPipe = os.popen(command)
417cdf0c1d5Smjnelson			except:
418cdf0c1d5Smjnelson				# we couldn't call the webrticli for some
419cdf0c1d5Smjnelson				# reason, so return a failure
420cdf0c1d5Smjnelson				raise RtiCallFailed('unknown')
421cdf0c1d5Smjnelson
422cdf0c1d5Smjnelson			data = cliPipe.readline()
423cdf0c1d5Smjnelson
424cdf0c1d5Smjnelson		# parse the data to see if we got a return code
425cdf0c1d5Smjnelson		# if we did, then that's bad.  if we didn't,
42641a5f560SMark J. Nelson		# then our call was successful
427cdf0c1d5Smjnelson		m = returnCodeRe.search(data)
428cdf0c1d5Smjnelson		if m:
42941a5f560SMark J. Nelson			rc = m.group(1)
430cdf0c1d5Smjnelson			# we got a return code, set it in our
431cdf0c1d5Smjnelson			# object, set the webRtiOutput for debugging
432cdf0c1d5Smjnelson			# or logging, and return a failure
43341a5f560SMark J. Nelson			if rc in WEBRTI_ERRORS:
43441a5f560SMark J. Nelson				exc = WEBRTI_ERRORS[rc]
43541a5f560SMark J. Nelson				if exc == RtiBadGate:
43641a5f560SMark J. Nelson					edata = gate
437cdf0c1d5Smjnelson				else:
43841a5f560SMark J. Nelson					edata = cr
43941a5f560SMark J. Nelson			else:
44041a5f560SMark J. Nelson				exc = RtiUnknownException
44141a5f560SMark J. Nelson				edata = rc
44241a5f560SMark J. Nelson			raise exc(edata)
443cdf0c1d5Smjnelson
44481540946SJohn Sonnenschein		data = data.splitlines()
445cdf0c1d5Smjnelson		# At this point, we should have valid data
44681540946SJohn Sonnenschein		for line in data:
44781540946SJohn Sonnenschein			line = line.rstrip('\r\n')
44881540946SJohn Sonnenschein			self.__webRtiOutput.append(line)
44981540946SJohn Sonnenschein			fields = line.split(':')
45081540946SJohn Sonnenschein			self.__mainCR.append(fields[0])
45181540946SJohn Sonnenschein			self.__rtiNumber.append(fields[1])
45281540946SJohn Sonnenschein			self.__consolidation.append(fields[2])
45381540946SJohn Sonnenschein			self.__project.append(fields[3])
45481540946SJohn Sonnenschein			self.__status.append(fields[4])
45581540946SJohn Sonnenschein			self.__rtiType.append(fields[5])
456cdf0c1d5Smjnelson
457cdf0c1d5Smjnelson	# accessors in case callers need the raw data
458cdf0c1d5Smjnelson	def mainCR(self):
459cdf0c1d5Smjnelson		return self.__mainCR
460cdf0c1d5Smjnelson	def rtiNumber(self):
461cdf0c1d5Smjnelson		return self.__rtiNumber
462cdf0c1d5Smjnelson	def consolidation(self):
463cdf0c1d5Smjnelson		return self.__consolidation
464cdf0c1d5Smjnelson	def project(self):
465cdf0c1d5Smjnelson		return self.__project
466cdf0c1d5Smjnelson	def status(self):
467cdf0c1d5Smjnelson		return self.__status
468cdf0c1d5Smjnelson	def rtiType(self):
469cdf0c1d5Smjnelson		return self.__rtiType
470cdf0c1d5Smjnelson	def queryCr(self):
471cdf0c1d5Smjnelson		return self.__queryCr
472cdf0c1d5Smjnelson	def queryGate(self):
473cdf0c1d5Smjnelson		return self.__queryGate
474cdf0c1d5Smjnelson	def queryConsolidation(self):
475cdf0c1d5Smjnelson		return self.__queryConsolidation
476cdf0c1d5Smjnelson
477cdf0c1d5Smjnelson	# in practice, most callers only care about the following
478cdf0c1d5Smjnelson	def accepted(self):
47981540946SJohn Sonnenschein		for status in self.__status:
48081540946SJohn Sonnenschein			if status != "S_ACCEPTED":
48181540946SJohn Sonnenschein				return False
48281540946SJohn Sonnenschein		return True
483cdf0c1d5Smjnelson
484cdf0c1d5Smjnelson	# for logging/debugging in case the caller wants the raw webrti output
485cdf0c1d5Smjnelson	def webRtiOutput(self):
486cdf0c1d5Smjnelson		return self.__webRtiOutput
487cdf0c1d5Smjnelson
488