xref: /illumos-gate/usr/src/tools/onbld/Checks/DbLookups.py (revision 815409466dd2a69a595e7520cc86142882da957c)
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#
24cdf0c1d5Smjnelson# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
25cdf0c1d5Smjnelson# Use is subject to license terms.
26cdf0c1d5Smjnelson#
27cdf0c1d5Smjnelson
28cdf0c1d5Smjnelson#
29cdf0c1d5Smjnelson# Various database lookup classes/methods, i.e.:
30cdf0c1d5Smjnelson#     * monaco
31cdf0c1d5Smjnelson#     * bugs.opensolaris.org (b.o.o.)
32cdf0c1d5Smjnelson#     * opensolaris.org/cgi/arc.py (for ARC)
33cdf0c1d5Smjnelson#
34cdf0c1d5Smjnelson
35cdf0c1d5Smjnelsonimport re
36cdf0c1d5Smjnelsonimport urllib
37cdf0c1d5Smjnelsonimport htmllib
38cdf0c1d5Smjnelsonimport os
39cdf0c1d5Smjnelsonfrom socket import socket, AF_INET, SOCK_STREAM
40cdf0c1d5Smjnelson
41cdf0c1d5Smjnelsonfrom onbld.Checks import onSWAN
42cdf0c1d5Smjnelson
43cdf0c1d5Smjnelsonclass BugException(Exception):
44cdf0c1d5Smjnelson	def __init__(self, data=''):
45cdf0c1d5Smjnelson		self.data = data
46cdf0c1d5Smjnelson		Exception.__init__(self, data)
47cdf0c1d5Smjnelson
48cdf0c1d5Smjnelson	def __str__(self):
49cdf0c1d5Smjnelson		return "Unknown error: %s" % self.data
50cdf0c1d5Smjnelson
51cdf0c1d5Smjnelsonclass NonExistentBug(BugException):
52cdf0c1d5Smjnelson	def __str__(self):
53cdf0c1d5Smjnelson		return "Bug %s does not exist" % self.data
54cdf0c1d5Smjnelson
55cdf0c1d5Smjnelsonclass Monaco(object):
56cdf0c1d5Smjnelson	"""
57cdf0c1d5Smjnelson	Query bug database.
58cdf0c1d5Smjnelson
59cdf0c1d5Smjnelson	Methods:
60cdf0c1d5Smjnelson	queryBugs()
61cdf0c1d5Smjnelson	expertQuery()
62cdf0c1d5Smjnelson	"""
63cdf0c1d5Smjnelson
64cdf0c1d5Smjnelson	def __init__(self):
65cdf0c1d5Smjnelson		self.__baseURL = "http://hestia.sfbay.sun.com/cgi-bin/expert?"
66cdf0c1d5Smjnelson
67cdf0c1d5Smjnelson	def expertQuery(self, cmd, format="Normal+text", header=False):
68cdf0c1d5Smjnelson		"""Return results of user-supplied bug query.
69cdf0c1d5Smjnelson
70cdf0c1d5Smjnelson		Argument:
71cdf0c1d5Smjnelson		cmd: query to run
72cdf0c1d5Smjnelson
73cdf0c1d5Smjnelson		Keyword arguments:
74cdf0c1d5Smjnelson		format: desired output format (default="Normal+text")
75cdf0c1d5Smjnelson		header: include headers in output? (default=False)
76cdf0c1d5Smjnelson
77cdf0c1d5Smjnelson		Returns:
78cdf0c1d5Smjnelson		List of lines representing the output from Monaco
79cdf0c1d5Smjnelson		"""
80cdf0c1d5Smjnelson
81cdf0c1d5Smjnelson		url = self.__baseURL + "format=" + format + ";Go=2;"
82cdf0c1d5Smjnelson		if not header: url += "no_header=on;"
83cdf0c1d5Smjnelson		url += "cmds=" + urllib.quote_plus("\n".join(cmd))
84cdf0c1d5Smjnelson		myMonaco = urllib.urlopen(url)
85cdf0c1d5Smjnelson		return myMonaco.readlines()
86cdf0c1d5Smjnelson
87cdf0c1d5Smjnelson	def queryBugs(self, crs):
88cdf0c1d5Smjnelson		"""Return all info for requested change reports.
89cdf0c1d5Smjnelson
90cdf0c1d5Smjnelson		Argument:
91cdf0c1d5Smjnelson		crs: list of change request ids
92cdf0c1d5Smjnelson
93cdf0c1d5Smjnelson		Returns:
94cdf0c1d5Smjnelson		Dictionary, mapping CR=>dictionary, where the nested dictionary
95cdf0c1d5Smjnelson		is a mapping of field=>value
96cdf0c1d5Smjnelson		"""
97ef62fef3SRichard Lowe		monacoFields = [ "cr_number", "category", "sub_category",
98cdf0c1d5Smjnelson			"area", "release", "build", "responsible_manager",
99cdf0c1d5Smjnelson			"responsible_engineer", "priority", "status", "sub_status",
100ef62fef3SRichard Lowe			"submitted_by", "date_submitted", "synopsis" ]
101cdf0c1d5Smjnelson		cmd = []
102cdf0c1d5Smjnelson		cmd.append("set What = cr." + ', cr.'.join(monacoFields))
103cdf0c1d5Smjnelson		cmd.append("")
104cdf0c1d5Smjnelson		cmd.append("set Which = cr.cr_number in (" + ','.join(crs) +")")
105cdf0c1d5Smjnelson		cmd.append("")
106cdf0c1d5Smjnelson		cmd.append("set FinalClauses = order by cr.cr_number")
107cdf0c1d5Smjnelson		cmd.append("")
108cdf0c1d5Smjnelson		cmd.append("doMeta genQuery cr")
109cdf0c1d5Smjnelson		output = self.expertQuery(cmd, "Pipe-delimited+text")
110cdf0c1d5Smjnelson		results = {}
111cdf0c1d5Smjnelson		for line in output:
112ef62fef3SRichard Lowe			line = line.rstrip('\n')
113ef62fef3SRichard Lowe
114ef62fef3SRichard Lowe			#
115ef62fef3SRichard Lowe			# We request synopsis last, and split on only
116ef62fef3SRichard Lowe			# the number of separators that we expect to
117ef62fef3SRichard Lowe			# see such that a | in the synopsis doesn't
118ef62fef3SRichard Lowe			# throw us out of whack.
119ef62fef3SRichard Lowe			#
120ef62fef3SRichard Lowe			values = line.split('|', len(monacoFields) - 1)
121cdf0c1d5Smjnelson			v = 0
122cdf0c1d5Smjnelson			cr = values[0]
123cdf0c1d5Smjnelson			results[cr] = {}
124cdf0c1d5Smjnelson			for field in monacoFields:
125cdf0c1d5Smjnelson				results[cr][field] = values[v]
126cdf0c1d5Smjnelson				v += 1
127cdf0c1d5Smjnelson		return results
128cdf0c1d5Smjnelson
129cdf0c1d5Smjnelsonclass BooBug(object):
130cdf0c1d5Smjnelson	"""Look up a single bug on bugs.opensolaris.org."""
131cdf0c1d5Smjnelson	def __init__(self, cr):
132cdf0c1d5Smjnelson		cr = str(cr)
133cdf0c1d5Smjnelson		url = "http://bugs.opensolaris.org/view_bug.do?bug_id="+cr
134cdf0c1d5Smjnelson		data = urllib.urlopen(url).readlines()
135cdf0c1d5Smjnelson		self.__fields = {}
136cdf0c1d5Smjnelson		self.__fields["cr_number"] = cr
137cdf0c1d5Smjnelson		htmlParser = htmllib.HTMLParser(None)
138cdf0c1d5Smjnelson		metaHtmlRe = re.compile(r'^<meta name="([^"]+)" content="([^"]*)">$')
139cdf0c1d5Smjnelson		for line in data:
140cdf0c1d5Smjnelson			m = metaHtmlRe.search(line)
141cdf0c1d5Smjnelson			if not m:
142cdf0c1d5Smjnelson				continue
143cdf0c1d5Smjnelson			val = urllib.unquote(m.group(2))
144cdf0c1d5Smjnelson			htmlParser.save_bgn()
145cdf0c1d5Smjnelson			htmlParser.feed(val)
146cdf0c1d5Smjnelson			self.__fields[m.group(1)] = htmlParser.save_end()
147cdf0c1d5Smjnelson		htmlParser.close()
148cdf0c1d5Smjnelson		if "synopsis" not in self.__fields:
149cdf0c1d5Smjnelson			raise NonExistentBug(cr)
150cdf0c1d5Smjnelson
151cdf0c1d5Smjnelson	def synopsis(self):
152cdf0c1d5Smjnelson		return self.__fields["synopsis"]
153cdf0c1d5Smjnelson	def product(self):
154cdf0c1d5Smjnelson		return self.__fields["product"]
155cdf0c1d5Smjnelson	def cat(self):
156cdf0c1d5Smjnelson		return self.__fields["category"]
157cdf0c1d5Smjnelson	def subcat(self):
158cdf0c1d5Smjnelson		return self.__fields["subcategory"]
159cdf0c1d5Smjnelson	def keywords(self):
160cdf0c1d5Smjnelson		return self.__fields["keywords"]
161cdf0c1d5Smjnelson	def state(self):
162cdf0c1d5Smjnelson		return self.__fields["state"]
163cdf0c1d5Smjnelson	def submit_date(self):
164cdf0c1d5Smjnelson		return self.__fields["submit_date"]
165cdf0c1d5Smjnelson	def type(self):
166cdf0c1d5Smjnelson		return self.__fields["type"]
167cdf0c1d5Smjnelson	def date(self):
168cdf0c1d5Smjnelson		return self.__fields["date"]
169cdf0c1d5Smjnelson	def number(self):
170cdf0c1d5Smjnelson		return self.__fields["cr_number"]
171cdf0c1d5Smjnelson
172cdf0c1d5Smjnelsonclass BugDB(object):
173cdf0c1d5Smjnelson	"""Lookup change requests.
174cdf0c1d5Smjnelson
175cdf0c1d5Smjnelson	Object can be used on or off of SWAN, using either monaco or
176cdf0c1d5Smjnelson	bugs.opensolaris.org as a database.
177cdf0c1d5Smjnelson
178cdf0c1d5Smjnelson	Usage:
179cdf0c1d5Smjnelson	bdb = BugDB()
180cdf0c1d5Smjnelson	r = bdb.lookup("6455550")
181cdf0c1d5Smjnelson	print r["6455550"]["synopsis"]
182cdf0c1d5Smjnelson	r = bdb.lookup(["6455550", "6505625"])
183cdf0c1d5Smjnelson	print r["6505625"]["synopsis"]
184cdf0c1d5Smjnelson	"""
185cdf0c1d5Smjnelson
186cdf0c1d5Smjnelson	def __init__(self, forceBoo = False):
187cdf0c1d5Smjnelson		"""Create a BugDB object.
188cdf0c1d5Smjnelson
189cdf0c1d5Smjnelson		Keyword argument:
190cdf0c1d5Smjnelson		forceBoo: use b.o.o even from SWAN (default=False)
191cdf0c1d5Smjnelson		"""
192cdf0c1d5Smjnelson		if forceBoo:
193cdf0c1d5Smjnelson			self.__onSWAN = False
194cdf0c1d5Smjnelson		else:
195cdf0c1d5Smjnelson			self.__onSWAN = onSWAN()
196cdf0c1d5Smjnelson			if self.__onSWAN:
197cdf0c1d5Smjnelson				self.__m = Monaco()
198cdf0c1d5Smjnelson
199cdf0c1d5Smjnelson	def lookup(self, crs):
200cdf0c1d5Smjnelson		"""Return all info for requested change reports.
201cdf0c1d5Smjnelson
202cdf0c1d5Smjnelson		Argument:
203cdf0c1d5Smjnelson		crs: one change request id (may be integer, string, or list),
204cdf0c1d5Smjnelson	             or multiple change request ids (must be a list)
205cdf0c1d5Smjnelson
206cdf0c1d5Smjnelson		Returns:
207cdf0c1d5Smjnelson		Dictionary, mapping CR=>dictionary, where the nested dictionary
208cdf0c1d5Smjnelson		is a mapping of field=>value
209cdf0c1d5Smjnelson		"""
210cdf0c1d5Smjnelson		if not isinstance(crs, list):
211cdf0c1d5Smjnelson			crs = [str(crs)]
212cdf0c1d5Smjnelson		if self.__onSWAN:
213cdf0c1d5Smjnelson			results = self.__m.queryBugs(crs)
214cdf0c1d5Smjnelson			return self.__m.queryBugs(crs)
215cdf0c1d5Smjnelson		# else we're off-swan and querying via boo, which we can
216cdf0c1d5Smjnelson		# only do one bug at a time
217cdf0c1d5Smjnelson		results = {}
218cdf0c1d5Smjnelson		for cr in crs:
219cdf0c1d5Smjnelson			cr = str(cr)
220cdf0c1d5Smjnelson			try:
221cdf0c1d5Smjnelson				b = BooBug(cr)
222cdf0c1d5Smjnelson			except NonExistentBug:
223cdf0c1d5Smjnelson				continue
224cdf0c1d5Smjnelson
225cdf0c1d5Smjnelson			results[cr] = {}
226cdf0c1d5Smjnelson			results[cr]["cr_number"] = cr
227cdf0c1d5Smjnelson			results[cr]["product"] = b.product()
228cdf0c1d5Smjnelson			results[cr]["synopsis"] = b.synopsis()
229cdf0c1d5Smjnelson			results[cr]["category"] = b.cat()
230cdf0c1d5Smjnelson			results[cr]["sub_category"] = b.subcat()
231cdf0c1d5Smjnelson			results[cr]["keywords"] = b.keywords()
232cdf0c1d5Smjnelson			results[cr]["status"] = b.state()
233cdf0c1d5Smjnelson			results[cr]["date_submitted"] = b.submit_date()
234cdf0c1d5Smjnelson			results[cr]["type"] = b.type()
235cdf0c1d5Smjnelson			results[cr]["date"] = b.date()
236cdf0c1d5Smjnelson
237cdf0c1d5Smjnelson		return results
238cdf0c1d5Smjnelson
239cdf0c1d5Smjnelson####################################################################
240cdf0c1d5Smjnelson
241cdf0c1d5Smjnelsonclass ARC(object):
242cdf0c1d5Smjnelson	"""Lookup an ARC case on opensolaris.org.
243cdf0c1d5Smjnelson
244cdf0c1d5Smjnelson	Usage:
245cdf0c1d5Smjnelson	a = ARC("PSARC", "2008/002")
246cdf0c1d5Smjnelson	if a.valid():
247cdf0c1d5Smjnelson		print a.name()
248cdf0c1d5Smjnelson	"""
249cdf0c1d5Smjnelson	def __init__(self, arc, case):
250cdf0c1d5Smjnelson		self.__valid = False
251cdf0c1d5Smjnelson		q = "http://opensolaris.org/cgi/arc.py?n=1"
252cdf0c1d5Smjnelson		q += "&arc0=" + arc
253cdf0c1d5Smjnelson		q += "&case0=" + case
254cdf0c1d5Smjnelson		data = urllib.urlopen(q).readlines()
255cdf0c1d5Smjnelson		self.__fields = {}
256cdf0c1d5Smjnelson		for line in data:
257cdf0c1d5Smjnelson			line = line.rstrip('\n')
258cdf0c1d5Smjnelson			fields = line.split('|')
259cdf0c1d5Smjnelson			validity = fields[0]
260cdf0c1d5Smjnelson
261cdf0c1d5Smjnelson			if validity != "0":
262cdf0c1d5Smjnelson				return
263cdf0c1d5Smjnelson			else:
264cdf0c1d5Smjnelson				self.__fields["Name"] = fields[2]
265cdf0c1d5Smjnelson
266cdf0c1d5Smjnelson		self.__valid = True
267cdf0c1d5Smjnelson
268cdf0c1d5Smjnelson	def valid(self):
269cdf0c1d5Smjnelson		return self.__valid
270cdf0c1d5Smjnelson	def name(self):
271cdf0c1d5Smjnelson		return self.__fields["Name"]
272cdf0c1d5Smjnelson	def status(self):
273cdf0c1d5Smjnelson		return self.__fields["Status"]
274cdf0c1d5Smjnelson	def type(self):
275cdf0c1d5Smjnelson		return self.__fields["Type"]
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
286*81540946SJohn SonnenscheinWEBRTICLI = '/net/onnv.sfbay.sun.com/export/onnv-gate/public/bin/webrticli'
287cdf0c1d5Smjnelson
288cdf0c1d5Smjnelson
289cdf0c1d5Smjnelsonclass RtiException(Exception):
290cdf0c1d5Smjnelson	def __init__(self, data=''):
291cdf0c1d5Smjnelson		self.data = data
292cdf0c1d5Smjnelson		Exception.__init__(self, data)
293cdf0c1d5Smjnelson
294cdf0c1d5Smjnelson	def __str__(self):
295cdf0c1d5Smjnelson		return "Unknown error: %s" % self.data
296cdf0c1d5Smjnelson
297cdf0c1d5Smjnelsonclass RtiCallFailed(RtiException):
298cdf0c1d5Smjnelson	def __str__(self):
299cdf0c1d5Smjnelson		return "Unable to call webrti: %s" % self.data
300cdf0c1d5Smjnelson
301cdf0c1d5Smjnelsonclass RtiSystemProblem(RtiException):
302cdf0c1d5Smjnelson	def __str__(self):
303cdf0c1d5Smjnelson		return "RTI status cannot be determined: %s" % self.data
304cdf0c1d5Smjnelson
305cdf0c1d5Smjnelsonclass RtiIncorrectCR(RtiException):
306cdf0c1d5Smjnelson	def __str__(self):
307cdf0c1d5Smjnelson		return "Incorrect CR number specified: %s" % self.data
308cdf0c1d5Smjnelson
309cdf0c1d5Smjnelsonclass RtiNotFound(RtiException):
310cdf0c1d5Smjnelson	def __str__(self):
311cdf0c1d5Smjnelson		return "RTI not found: %s" % self.data
312cdf0c1d5Smjnelson
313cdf0c1d5Smjnelsonclass RtiNeedConsolidation(RtiException):
314cdf0c1d5Smjnelson	def __str__(self):
315cdf0c1d5Smjnelson		return "More than one consolidation has this CR: %s" % self.data
316cdf0c1d5Smjnelson
317cdf0c1d5Smjnelsonclass RtiBadGate(RtiException):
318cdf0c1d5Smjnelson	def __str__(self):
319cdf0c1d5Smjnelson		return "Incorrect gate name specified: %s" % self.data
320cdf0c1d5Smjnelson
321cdf0c1d5Smjnelsonclass RtiOffSwan(RtiException):
322cdf0c1d5Smjnelson	def __str__(self):
323cdf0c1d5Smjnelson		return "RTI status checks need SWAN access: %s" % self.data
324cdf0c1d5Smjnelson
325cdf0c1d5SmjnelsonWEBRTI_ERRORS = {
326cdf0c1d5Smjnelson	'1': RtiSystemProblem,
327cdf0c1d5Smjnelson	'2': RtiIncorrectCR,
328cdf0c1d5Smjnelson	'3': RtiNotFound,
329cdf0c1d5Smjnelson	'4': RtiNeedConsolidation,
330cdf0c1d5Smjnelson	'5': RtiBadGate,
331cdf0c1d5Smjnelson}
332cdf0c1d5Smjnelson
333cdf0c1d5Smjnelson# Our Rti object which we'll use to represent an Rti query
334cdf0c1d5Smjnelson# It's really just a wrapper around the Rti connection, and attempts
335cdf0c1d5Smjnelson# to establish a direct socket connection and query the webrti server
336cdf0c1d5Smjnelson# directly (thus avoiding a system/fork/exec call).  If it fails, it
337cdf0c1d5Smjnelson# falls back to the webrticli command line client.
338cdf0c1d5Smjnelson
339cdf0c1d5SmjnelsonreturnCodeRe = re.compile(r'.*RETURN_CODE=(\d+)')
340cdf0c1d5Smjnelsonclass Rti:
341cdf0c1d5Smjnelson	"""Lookup an RTI.
342cdf0c1d5Smjnelson
343cdf0c1d5Smjnelson	Usage:
344cdf0c1d5Smjnelson	r = Rti("6640538")
345cdf0c1d5Smjnelson	print r.rtiNumber();
346cdf0c1d5Smjnelson	"""
347cdf0c1d5Smjnelson
348cdf0c1d5Smjnelson	def __init__(self, cr, gate=None, consolidation=None):
349cdf0c1d5Smjnelson		"""Create an Rti object for the specified change request.
350cdf0c1d5Smjnelson
351cdf0c1d5Smjnelson		Argument:
352cdf0c1d5Smjnelson		cr: change request id
353cdf0c1d5Smjnelson
354cdf0c1d5Smjnelson		Keyword arguments, to limit scope of RTI search:
355cdf0c1d5Smjnelson		gate: path to gate workspace (default=None)
356cdf0c1d5Smjnelson		consolidation: consolidation name (default=None)
357cdf0c1d5Smjnelson		"""
358cdf0c1d5Smjnelson
359cdf0c1d5Smjnelson		bufSz = 1024
360cdf0c1d5Smjnelson		addr = (WEBRTI_HOST, WEBRTI_PORT)
361cdf0c1d5Smjnelson		# If the passed 'cr' was given as an int, then wrap it
362cdf0c1d5Smjnelson		# into a string to make our life easier
363cdf0c1d5Smjnelson		if isinstance(cr, int):
364cdf0c1d5Smjnelson			cr = str(cr)
365cdf0c1d5Smjnelson		self.__queryCr = cr
366cdf0c1d5Smjnelson		self.__queryGate = gate
367cdf0c1d5Smjnelson		self.__queryConsolidation = consolidation
368cdf0c1d5Smjnelson
369*81540946SJohn Sonnenschein		self.__webRtiOutput = []
370*81540946SJohn Sonnenschein		self.__mainCR = []
371*81540946SJohn Sonnenschein		self.__rtiNumber = []
372*81540946SJohn Sonnenschein		self.__consolidation = []
373*81540946SJohn Sonnenschein		self.__project = []
374*81540946SJohn Sonnenschein		self.__status = []
375*81540946SJohn Sonnenschein		self.__rtiType = []
376cdf0c1d5Smjnelson		try:
377cdf0c1d5Smjnelson			# try to use a direct connection to the
378cdf0c1d5Smjnelson			# webrti server first
379cdf0c1d5Smjnelson			sock = socket(AF_INET, SOCK_STREAM)
380cdf0c1d5Smjnelson			sock.connect(addr)
381cdf0c1d5Smjnelson			command = "WEBRTICLI/1.0\nRTIstatus\n%s\n" % cr
382cdf0c1d5Smjnelson			if consolidation:
383cdf0c1d5Smjnelson				command += "-c\n%s\n" % consolidation
384cdf0c1d5Smjnelson			if gate:
385cdf0c1d5Smjnelson				command += "-g\n%s\n" % gate
386cdf0c1d5Smjnelson			command += "\n"
387cdf0c1d5Smjnelson			sock.send(command)
388cdf0c1d5Smjnelson			dataList = []
389cdf0c1d5Smjnelson			# keep receiving data from the socket until the
390cdf0c1d5Smjnelson			# server closes the connection
391cdf0c1d5Smjnelson			stillReceiving = True
392cdf0c1d5Smjnelson			while stillReceiving:
393cdf0c1d5Smjnelson				dataPiece = sock.recv(bufSz)
394cdf0c1d5Smjnelson				if dataPiece:
395cdf0c1d5Smjnelson					dataList.append(dataPiece)
396cdf0c1d5Smjnelson				else:
397cdf0c1d5Smjnelson					stillReceiving = False
398cdf0c1d5Smjnelson			# create the lines, skipping the first
399cdf0c1d5Smjnelson			# ("WEBRTCLI/1.0\n")
400cdf0c1d5Smjnelson			data = '\n'.join(''.join(dataList).split('\n')[1:])
401cdf0c1d5Smjnelson		except:
402cdf0c1d5Smjnelson			if not onSWAN():
403cdf0c1d5Smjnelson				raise RtiOffSwan(cr)
404cdf0c1d5Smjnelson
405cdf0c1d5Smjnelson			if not os.path.exists(WEBRTICLI):
406cdf0c1d5Smjnelson				raise RtiCallFailed('not found')
407cdf0c1d5Smjnelson
408cdf0c1d5Smjnelson			# fallback to the "supported" webrticli interface
409cdf0c1d5Smjnelson			command = WEBRTICLI
410cdf0c1d5Smjnelson			if consolidation:
411cdf0c1d5Smjnelson				command += " -c " + consolidation
412cdf0c1d5Smjnelson			if gate:
413cdf0c1d5Smjnelson				command += " -g " + gate
414cdf0c1d5Smjnelson			command += " RTIstatus " + cr
415cdf0c1d5Smjnelson
416cdf0c1d5Smjnelson			try:
417cdf0c1d5Smjnelson				cliPipe = os.popen(command)
418cdf0c1d5Smjnelson			except:
419cdf0c1d5Smjnelson				# we couldn't call the webrticli for some
420cdf0c1d5Smjnelson				# reason, so return a failure
421cdf0c1d5Smjnelson				raise RtiCallFailed('unknown')
422cdf0c1d5Smjnelson
423cdf0c1d5Smjnelson			data = cliPipe.readline()
424cdf0c1d5Smjnelson
425cdf0c1d5Smjnelson		# parse the data to see if we got a return code
426cdf0c1d5Smjnelson		# if we did, then that's bad.  if we didn't,
427cdf0c1d5Smjnelson		# then our call was successfully
428cdf0c1d5Smjnelson		m = returnCodeRe.search(data)
429cdf0c1d5Smjnelson		if m:
430cdf0c1d5Smjnelson			# we got a return code, set it in our
431cdf0c1d5Smjnelson			# object, set the webRtiOutput for debugging
432cdf0c1d5Smjnelson			# or logging, and return a failure
433cdf0c1d5Smjnelson			if m.group(1) in WEBRTI_ERRORS:
434cdf0c1d5Smjnelson				exc = WEBRTI_ERRORS[m.group(1)]
435cdf0c1d5Smjnelson			else:
436cdf0c1d5Smjnelson				exc = RtiException
437cdf0c1d5Smjnelson			raise exc(data)
438cdf0c1d5Smjnelson
439*81540946SJohn Sonnenschein		data = data.splitlines()
440cdf0c1d5Smjnelson		# At this point, we should have valid data
441*81540946SJohn Sonnenschein		for line in data:
442*81540946SJohn Sonnenschein			line = line.rstrip('\r\n')
443*81540946SJohn Sonnenschein			self.__webRtiOutput.append(line)
444*81540946SJohn Sonnenschein			fields = line.split(':')
445*81540946SJohn Sonnenschein			self.__mainCR.append(fields[0])
446*81540946SJohn Sonnenschein			self.__rtiNumber.append(fields[1])
447*81540946SJohn Sonnenschein			self.__consolidation.append(fields[2])
448*81540946SJohn Sonnenschein			self.__project.append(fields[3])
449*81540946SJohn Sonnenschein			self.__status.append(fields[4])
450*81540946SJohn Sonnenschein			self.__rtiType.append(fields[5])
451cdf0c1d5Smjnelson
452cdf0c1d5Smjnelson	# accessors in case callers need the raw data
453cdf0c1d5Smjnelson	def mainCR(self):
454cdf0c1d5Smjnelson		return self.__mainCR
455cdf0c1d5Smjnelson	def rtiNumber(self):
456cdf0c1d5Smjnelson		return self.__rtiNumber
457cdf0c1d5Smjnelson	def consolidation(self):
458cdf0c1d5Smjnelson		return self.__consolidation
459cdf0c1d5Smjnelson	def project(self):
460cdf0c1d5Smjnelson		return self.__project
461cdf0c1d5Smjnelson	def status(self):
462cdf0c1d5Smjnelson		return self.__status
463cdf0c1d5Smjnelson	def rtiType(self):
464cdf0c1d5Smjnelson		return self.__rtiType
465cdf0c1d5Smjnelson	def queryCr(self):
466cdf0c1d5Smjnelson		return self.__queryCr
467cdf0c1d5Smjnelson	def queryGate(self):
468cdf0c1d5Smjnelson		return self.__queryGate
469cdf0c1d5Smjnelson	def queryConsolidation(self):
470cdf0c1d5Smjnelson		return self.__queryConsolidation
471cdf0c1d5Smjnelson
472cdf0c1d5Smjnelson	# in practice, most callers only care about the following
473cdf0c1d5Smjnelson	def accepted(self):
474*81540946SJohn Sonnenschein		for status in self.__status:
475*81540946SJohn Sonnenschein			if status != "S_ACCEPTED":
476*81540946SJohn Sonnenschein				return False
477*81540946SJohn Sonnenschein		return True
478cdf0c1d5Smjnelson
479cdf0c1d5Smjnelson	# for logging/debugging in case the caller wants the raw webrti output
480cdf0c1d5Smjnelson	def webRtiOutput(self):
481cdf0c1d5Smjnelson		return self.__webRtiOutput
482cdf0c1d5Smjnelson
483