Back to index

nordugrid-arc-nox  1.1.0~rc6
client.py
Go to the documentation of this file.
00001 import arc
00002 import thread, threading
00003 from arcom.xmltree import XMLTree
00004 import random
00005 
00006 from arcom.logger import get_logger
00007 log = get_logger('arcom.client')
00008 
00009 
00010 class Client:
00011     """ Base Client class for sending SOAP messages to services """
00012 
00013     NS_class = arc.NS
00014 
00015     def __init__(self, url, ns, print_xml = False, xmlnode_class = arc.XMLNode, ssl_config = {}):
00016         """ The constructor of the Client class.
00017         
00018         Client(url, ns, print_xml = false)
00019         
00020         url is the URL of the service, it could be a list of multiple URLs
00021         ns contains the namespaces we want to use with each message
00022         print_xml is for debugging, prints all the SOAP messages to the screen
00023         """
00024         self.ns = ns
00025         if type(url) == list:
00026             urls = url
00027         else:
00028             urls = [url]
00029         self.urls = [arc.URL(url) for url in urls]
00030         self.https = 'https' in [url.Protocol() for url in self.urls]
00031         self.print_xml = print_xml
00032         self.xmlnode_class = xmlnode_class
00033         self.ssl_config = {}
00034         self.cfg = arc.MCCConfig()
00035         self.get_trusted_dns_method = ssl_config.get('get_trusted_dns_method', None)
00036         self.connection_cache = {}
00037         # semaphores to limit number of concurent writes
00038         self.semapool = threading.BoundedSemaphore(128)
00039         if self.https:
00040             self.ssl_config = ssl_config
00041             if ssl_config.has_key('proxy_file'):
00042                 self.cfg.AddProxy(self.ssl_config['proxy_file'])
00043             else:
00044                 try:
00045                     self.cfg.AddCertificate(self.ssl_config['cert_file'])
00046                     self.cfg.AddPrivateKey(self.ssl_config['key_file'])
00047                 except:
00048                     raise Exception, 'no key file or cert file found!'
00049             if ssl_config.has_key('ca_file'):
00050                 self.cfg.AddCAFile(self.ssl_config['ca_file'])
00051             elif ssl_config.has_key('ca_dir'):
00052                 self.cfg.AddCADir(self.ssl_config['ca_dir'])
00053             else:
00054                 raise Exception, 'no CA file or CA dir found!'
00055                 
00056     def reset(self):
00057         tid = thread.get_ident()
00058         self.connection_cache[tid] = None
00059 
00060     def call(self, tree, return_tree_only = False):
00061         """ Create a SOAP message from an XMLTree and send it to the service.
00062         
00063         call(tree, return_tree_only = False)
00064         
00065         tree is an XMLTree object containing the content of the request
00066         return_tree_only indicates that we only need to put the response into an XMLTree
00067         """
00068         # create a new PayloadSOAP object with the given namespace
00069         out = arc.PayloadSOAP(self.ns)
00070         # add the content of the XMLTree to the XMLNode of the SOAP object
00071         tree.add_to_node(out)
00072         if self.print_xml:
00073             msg = out.GetXML()
00074             print 'Request:'
00075             print XMLTree(out).pretty_xml(indent = '    ', prefix = '        #   ')
00076             print
00077         # call the service and get back the response, and the HTTP status
00078         resp = self.call_raw(out)
00079         if self.print_xml:
00080             print 'Response:'
00081             try:
00082                 print XMLTree(from_string = resp).pretty_xml(indent = '    ', prefix = '        #   ')
00083             except:
00084                 print resp
00085             print
00086         if return_tree_only:
00087             # wrap the response into an XMLTree and return only the tree
00088             return XMLTree(from_string = resp, forget_namespace = True).get_trees('///')[0]
00089         else:
00090             return resp
00091 
00092     def _fawlty(self, message, status):
00093         if not status.isOk():
00094             return 'ERROR: %s' % status
00095         if message.IsFault():
00096             return 'ERROR: %s' % message.Fault().Reason()
00097         return False
00098 
00099     def call_raw(self, outpayload):
00100         """ Send a POST request with the SOAP XML message.
00101         
00102         call_raw(outpayload)
00103         
00104         outpayload is an XMLNode with the SOAP message
00105         """
00106         tid = thread.get_ident()
00107         s = self.connection_cache.get(tid, None)
00108         if s:
00109             try:
00110                 resp, status = s.process(outpayload)
00111                 if not self._fawlty(resp, status):
00112                     return resp.GetXML()
00113             except:
00114                 pass
00115             self.connection_cache[tid] = None
00116         if len(self.urls) == 0:
00117             log.msg(arc.WARNING, 'No URLs to connect to (in %s)' % str(self.__class__.__name__))
00118             raise Exception, 'No URLs to connect'
00119         random.shuffle(self.urls)
00120         #print "available URLs", [url.fullstr() for url in self.urls]
00121         for url in self.urls:
00122             #print "trying URL", url.fullstr()
00123             try:
00124                 s = arc.ClientSOAP(self.cfg, url)
00125                 if self.get_trusted_dns_method:
00126                     dnlist = self.get_trusted_dns_method()
00127                     if dnlist:
00128                         dnlistconf = arc.DNListHandlerConfig(dnlist, 'outgoing')
00129                         # _s points to the superclass, but not the object, so it needs the object as first argument
00130                         s._s._s.AddSecHandler(s, dnlistconf, arc.TLSSec)
00131                 resp, status = s.process(outpayload)
00132                 fawlty = self._fawlty(resp, status)
00133                 if fawlty:
00134                     raise Exception, fawlty
00135                 resp = resp.GetXML()
00136                 self.connection_cache[tid] = s
00137                 return resp
00138             except:
00139                 log.msg(arc.WARNING, "ERROR connecting to", url.fullstr())
00140                 pass
00141         log.msg(arc.ERROR, "ERROR connecting to all of these:", ', '.join([url.fullstr() for url in self.urls]))
00142         raise