Changeset f8b118e


Ignore:
Timestamp:
Nov 21, 2008 10:20:31 AM (15 years ago)
Author:
Ted Faber <faber@…>
Branches:
axis_example, compt_changes, info-ops, master, version-1.30, version-2.00, version-3.01, version-3.02
Children:
9460b1e
Parents:
cfabc40
Message:

clean up service classes a bit

Location:
fedd
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • fedd/fedd_client.py

    rcfabc40 rf8b118e  
    2020import xmlrpclib
    2121
    22 from fedd_util import fedid, fedd_ssl_context, pack_soap, unpack_soap, \
    23         pack_id, unpack_id, encapsulate_binaries, decapsulate_binaries, \
     22from fedd_util import fedid, fedd_ssl_context, pack_id, unpack_id, \
    2423        service_caller
    2524from service_error import *
  • fedd/fedd_util.py

    rcfabc40 rf8b118e  
    174174    creating dictionaries to be converted to messages.
    175175    """
    176     if isinstance(id, type(fedid())): return { 'fedid': id }
     176    if isinstance(id, fedid): return { 'fedid': id }
    177177    elif id.startswith("http:") or id.startswith("https:"): return { 'uri': id }
    178178    else: return { 'localname': id}
     
    180180def unpack_id(id):
    181181    """return id as a type determined by the key"""
    182     if id.has_key("fedid"): return fedid(id["fedid"])
    183     else:
    184         for k in ("localname", "uri", "kerberosUsername"):
    185             if id.has_key(k): return id[k]
     182    for k in ("localname", "fedid", "uri", "kerberosUsername"):
     183        if id.has_key(k): return id[k]
    186184    return None
    187185
    188 def pack_soap(container, name, contents):
    189     """
    190     Convert the dictionary in contents into a tree of ZSI classes.
    191 
    192     The holder classes are constructed from factories in container and assigned
    193     to either the element or attribute name.  This is used to recursively
    194     create the SOAP message.
    195     """
    196     if getattr(contents, "__iter__", None) != None:
    197         attr =getattr(container, "new_%s" % name, None)
    198         if attr: obj = attr()
    199         else:
    200             raise TypeError("%s does not have a new_%s attribute" % \
    201                     (container, name))
    202         for e, v in contents.iteritems():
    203             assign = getattr(obj, "set_element_%s" % e, None) or \
    204                     getattr(obj, "set_attribute_%s" % e, None)
    205             if isinstance(v, type(dict())):
    206                 assign(pack_soap(obj, e, v))
    207             elif getattr(v, "__iter__", None) != None:
    208                 assign([ pack_soap(obj, e, val ) for val in v])
    209             elif getattr(v, "pack_soap", None) != None:
    210                 assign(v.pack_soap())
    211             else:
    212                 assign(v)
    213         return obj
    214     else: return contents
    215 
    216 def unpack_soap(element):
    217     """
    218     Convert a tree of ZSI SOAP classes intro a hash.  The inverse of pack_soap
    219 
    220     Elements or elements that are empty are ignored.
    221     """
    222     methods = [ m for m in dir(element) \
    223             if m.startswith("get_element") or m.startswith("get_attribute")]
    224     if len(methods) > 0:
    225         rv = { }
    226         for m in methods:
    227             if m.startswith("get_element_"): n = m.replace("get_element_","",1)
    228             else: n = m.replace("get_attribute_", "", 1)
    229             sub = getattr(element, m)()
    230             if sub != None:
    231                 if isinstance(sub, basestring):
    232                     rv[n] = sub
    233                 elif getattr(sub, "__iter__", None) != None:
    234                     if len(sub) > 0: rv[n] = [unpack_soap(e) for e in sub]
    235                 else:
    236                     rv[n] = unpack_soap(sub)
    237         return rv
    238     else:
    239         return element
    240 def apply_to_tags(e, map):
    241     """
    242     Map is an iterable of ordered pairs (tuples) that map a key to a function.
    243     This function walks the given message and replaces any object with a key in
    244     the map with the result of applying that function to the object.
    245     """
    246     dict_type = type(dict())
    247     list_type = type(list())
    248     str_type = type(str())
    249 
    250     if isinstance(e, dict_type):
    251         for k in e.keys():
    252             for tag, fcn in map:
    253                 if k == tag:
    254                     if isinstance(e[k], list_type):
    255                         e[k] = [ fcn(b) for b in e[k]]
    256                     else:
    257                         e[k] = fcn(e[k])
    258                 elif isinstance(e[k], dict_type):
    259                     apply_to_tags(e[k], map)
    260                 elif isinstance(e[k], list_type):
    261                     for ee in e[k]:
    262                         apply_to_tags(ee, map)
    263     # Other types end the recursion - they should be leaves
    264     return e
    265 
    266 # These are all just specializations of apply_to_tags
    267 def fedids_to_obj(e, tags=('fedid',)):
    268     """
    269     Turn the fedids in a message that are encoded as bitstrings into fedid
    270     objects.
    271     """
    272     map = [ (t, lambda x: fedid(bits=x)) for t in tags]
    273     return apply_to_tags(e, map)
    274 
    275 def encapsulate_binaries(e, tags):
    276     """Walk through a message and encapsulate any dictionary entries in
    277     tags into a binary object."""
    278 
    279     def to_binary(o):
    280         pack = getattr(o, 'pack_xmlrpc', None)
    281         if callable(pack): return Binary(pack())
    282         else: return Binary(o)
    283 
    284     map = [ (t, to_binary) for t in tags]
    285     return apply_to_tags(e, map)
    286 
    287 def decapsulate_binaries(e, tags):
    288     """Walk through a message and encapsulate any dictionary entries in
    289     tags into a binary object."""
    290 
    291     map = [ (t, lambda x: x.data) for t in tags]
    292     return apply_to_tags(e, map)
    293 #end of tag specializations
    294 
    295 def strip_unicode(obj):
    296     """Walk through a message and convert all strings to non-unicode strings"""
    297     if isinstance(obj, dict):
    298         for k in obj.keys():
    299             obj[k] = strip_unicode(obj[k])
    300         return obj
    301     elif isinstance(obj, basestring):
    302         return str(obj)
    303     elif getattr(obj, "__iter__", None):
    304         return [ strip_unicode(x) for x in obj]
    305     else:
    306         return obj
    307 
    308 def make_unicode(obj):
    309     """Walk through a message and convert all strings to unicode"""
    310     if isinstance(obj, dict):
    311         for k in obj.keys():
    312             obj[k] = make_unicode(obj[k])
    313         return obj
    314     elif isinstance(obj, basestring):
    315         return unicode(obj)
    316     elif getattr(obj, "__iter__", None):
    317         return [ make_unicode(x) for x in obj]
    318     else:
    319         return obj
    320 
    321 
    322 def generate_fedid(subj, bits=2048, log=None, dir=None, trace=sys.stderr):
     186def generate_fedid(subj, bits=2048, log=None, dir=None, trace=sys.stderr,
     187        ssl_prog="/usr/bin/openssl"):
    323188    """
    324189    Create a new certificate and derive a fedid from it.
     
    336201                    suffix=".pem")
    337202
    338             cmd = ["/usr/bin/openssl", "req", "-text", "-newkey",
     203            cmd = [ssl_prog, "req", "-text", "-newkey",
    339204                    "rsa:%d" % bits, "-keyout", keypath,  "-nodes",
    340205                    "-subj", "/CN=%s" % subj, "-x509", "-days", "30",
     
    367232        if certpath: os.remove(certpath)
    368233
    369 
    370 class soap_handler:
     234# Used by the remote_service_base class.
     235def to_binary(o):
     236    """
     237    A function that converts an object into an xmlrpclib.Binary using
     238    either its internal packing method, or the standard Binary constructor.
     239    """
     240    pack = getattr(o, 'pack_xmlrpc', None)
     241    if callable(pack): return Binary(pack())
     242    else: return Binary(o)
     243
     244# Classes that encapsulate the process of making and dealing with requests to
     245# WSDL-generated and XMLRPC remote accesses. 
     246
     247class remote_service_base:
     248    """
     249    This invisible base class encapsulates the functions used to massage the
     250    dictionaries used to pass parameters into and out of the RPC formats.  It's
     251    mostly a container for the static methods to do that work, but defines some
     252    maps sued by sub classes on apply_to_tags
     253    """
     254    # A map used to convert fedid fields to fedid objects (when the field is
     255    # already a string)
     256    fedid_to_object = ( ('fedid', lambda x: fedid(bits=x)),)
     257    # A map used by apply_to_tags to convert fedids from xmlrpclib.Binary
     258    # objects to fedid objects in one sweep.
     259    decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
     260    # A map used to encapsulate fedids into xmlrpclib.Binary objects
     261    encap_fedids = (('fedid', to_binary),)
     262
     263    @staticmethod
     264    def pack_soap(container, name, contents):
     265        """
     266        Convert the dictionary in contents into a tree of ZSI classes.
     267
     268        The holder classes are constructed from factories in container and
     269        assigned to either the element or attribute name.  This is used to
     270        recursively create the SOAP message.
     271        """
     272        if getattr(contents, "__iter__", None) != None:
     273            attr =getattr(container, "new_%s" % name, None)
     274            if attr: obj = attr()
     275            else:
     276                raise TypeError("%s does not have a new_%s attribute" % \
     277                        (container, name))
     278            for e, v in contents.iteritems():
     279                assign = getattr(obj, "set_element_%s" % e, None) or \
     280                        getattr(obj, "set_attribute_%s" % e, None)
     281                if isinstance(v, type(dict())):
     282                    assign(remote_service_base.pack_soap(obj, e, v))
     283                elif getattr(v, "__iter__", None) != None:
     284                    assign([ remote_service_base.pack_soap(obj, e, val ) \
     285                            for val in v])
     286                elif getattr(v, "pack_soap", None) != None:
     287                    assign(v.pack_soap())
     288                else:
     289                    assign(v)
     290            return obj
     291        else: return contents
     292
     293    @staticmethod
     294    def unpack_soap(element):
     295        """
     296        Convert a tree of ZSI SOAP classes intro a hash.  The inverse of
     297        pack_soap
     298
     299        Elements or elements that are empty are ignored.
     300        """
     301        methods = [ m for m in dir(element) \
     302                if m.startswith("get_element") or m.startswith("get_attribute")]
     303        if len(methods) > 0:
     304            rv = { }
     305            for m in methods:
     306                if m.startswith("get_element_"):
     307                    n = m.replace("get_element_","",1)
     308                else:
     309                    n = m.replace("get_attribute_", "", 1)
     310                sub = getattr(element, m)()
     311                if sub != None:
     312                    if isinstance(sub, basestring):
     313                        rv[n] = sub
     314                    elif getattr(sub, "__iter__", None) != None:
     315                        if len(sub) > 0: rv[n] = \
     316                                [remote_service_base.unpack_soap(e) \
     317                                    for e in sub]
     318                    else:
     319                        rv[n] = remote_service_base.unpack_soap(sub)
     320            return rv
     321        else:
     322            return element
     323
     324    @staticmethod
     325    def apply_to_tags(e, map):
     326        """
     327        Map is an iterable of ordered pairs (tuples) that map a key to a
     328        function.
     329        This function walks the given message and replaces any object with a
     330        key in the map with the result of applying that function to the object.
     331        """
     332        if isinstance(e, dict):
     333            for k in e.keys():
     334                for tag, fcn in map:
     335                    if k == tag:
     336                        if isinstance(e[k], list):
     337                            e[k] = [ fcn(b) for b in e[k]]
     338                        else:
     339                            e[k] = fcn(e[k])
     340                    elif isinstance(e[k], dict):
     341                        remote_service_base.apply_to_tags(e[k], map)
     342                    elif isinstance(e[k], list):
     343                        for ee in e[k]:
     344                            remote_service_base.apply_to_tags(ee, map)
     345        # Other types end the recursion - they should be leaves
     346        return e
     347
     348    @staticmethod
     349    def strip_unicode(obj):
     350        """Walk through a message and convert all strings to non-unicode
     351        strings"""
     352        if isinstance(obj, dict):
     353            for k in obj.keys():
     354                obj[k] = remote_service_base.strip_unicode(obj[k])
     355            return obj
     356        elif isinstance(obj, basestring) and not isinstance(obj, str):
     357            return str(obj)
     358        elif getattr(obj, "__iter__", None):
     359            return [ remote_service_base.strip_unicode(x) for x in obj]
     360        else:
     361            return obj
     362
     363    @staticmethod
     364    def make_unicode(obj):
     365        """Walk through a message and convert all strings to unicode"""
     366        if isinstance(obj, dict):
     367            for k in obj.keys():
     368                obj[k] = remote_service_base.make_unicode(obj[k])
     369            return obj
     370        elif isinstance(obj, basestring) and not isinstance(obj, unicode):
     371            return unicode(obj)
     372        elif getattr(obj, "__iter__", None):
     373            return [ remote_service_base.make_unicode(x) for x in obj]
     374        else:
     375            return obj
     376
     377
     378
     379class soap_handler(remote_service_base):
    371380    """
    372381    Encapsulate the handler code to unpack and pack SOAP requests and responses
     
    391400    def __call__(self, ps, fid):
    392401        req = ps.Parse(self.typecode)
    393 
    394         msg = self.method(fedids_to_obj(unpack_soap(req)), fid)
     402        # Convert the message to a dict with the fedid strings converted to
     403        # fedid objects
     404        req = self.apply_to_tags(self.unpack_soap(req), self.fedid_to_object)
     405
     406        msg = self.method(req, fid)
    395407
    396408        resp = self.constructor()
     
    398410        if set_element and callable(set_element):
    399411            try:
    400                 set_element(pack_soap(resp, self.body_name, msg))
     412                set_element(self.pack_soap(resp, self.body_name, msg))
    401413                return resp
    402414            except (NameError, TypeError):
     
    405417            return None
    406418
    407 class xmlrpc_handler:
     419class xmlrpc_handler(remote_service_base):
    408420    """
    409421    Generate the handler code to unpack and pack XMLRPC requests and responses
     
    422434        self.method = method
    423435        self.body_name = body_name
    424         # A map used by apply_to_tags to convert fedids from xmlrpclib.Binary
    425         # objects to fedid objects in one sweep.
    426         self.decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
    427436
    428437    def __call__(self, params, fid):
    429438        msg = None
    430439
    431         p = apply_to_tags(params[0], self.decap_fedids)
     440        p = self.apply_to_tags(params[0], self.decap_fedids)
    432441        try:
    433442            msg = self.method(p, fid)
     
    435444            raise Fault(e.code_string(), e.desc)
    436445        if msg != None:
    437             return make_unicode(encapsulate_binaries(\
    438                     { self.body_name: msg }, ('fedid',)))
     446            return self.make_unicode(self.apply_to_tags(\
     447                    { self.body_name: msg }, self.encap_fedids))
    439448        else:
    440449            return None
    441450
    442 class service_caller:
     451class service_caller(remote_service_base):
    443452    def __init__(self, service_name, port_name, locator, request_message,
    444453            request_body_name, tracefile=None):
     
    454463            trusted_certs=None, context=None, tracefile=None):
    455464        """Send an XMLRPC request.  """
    456         decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
    457465
    458466
     
    473481        # the unicode objects converted.  We also convert the url to a
    474482        # basic string if it isn't one already.
    475         r = strip_unicode(copy.deepcopy(req))
     483        r = self.strip_unicode(copy.deepcopy(req))
    476484        url = str(url)
    477485       
     
    481489        try:
    482490            remote_method = getattr(port, self.service_name, None)
    483             resp = remote_method(encapsulate_binaries(\
    484                     { self.request_body_name: r}, ('fedid',)))
     491            resp = remote_method(self.apply_to_tags(\
     492                    { self.request_body_name: r}, self.encap_fedids))
    485493        except Fault, f:
    486494            raise service_error(None, f.faultString, f.faultCode)
     
    489497                    "Remote XMLRPC Fault: %s" % e)
    490498
    491         return apply_to_tags(resp, decap_fedids)
     499        return self.apply_to_tags(resp, self.decap_fedids)
    492500
    493501    def call_soap_service(self, url, req, cert_file=None, cert_pwd=None,
     
    535543                    "Cannot get element setting method for %s" % \
    536544                            self.request_body_name)
    537         set_element(pack_soap(msg, self.request_body_name, req))
     545        set_element(self.pack_soap(msg, self.request_body_name, req))
    538546        try:
    539547            resp = remote_method(msg)
     
    542550                    "Bad format message (XMLRPC??): %s" % e)
    543551        except ZSI.FaultException, e:
    544             ee = unpack_soap(e.fault.detail[0]).get('FeddFaultBody', { })
     552            ee = self.unpack_soap(e.fault.detail[0]).get('FeddFaultBody', { })
    545553            if ee:
    546554                raise service_error(ee['code'], ee['desc'])
     
    548556                raise service_error(service_error.internal,
    549557                        "Unexpected fault body")
    550         r = make_unicode(fedids_to_obj(unpack_soap(resp)))
     558        # Unpack and convert fedids to objects
     559        r = self.apply_to_tags(self.unpack_soap(resp), self.fedid_to_object)
     560        #  Make sure all strings are unicode
     561        r = self.make_unicode(r)
    551562        return r
    552563
Note: See TracChangeset for help on using the changeset viewer.