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