source: fedd/fedd_proj.py @ 7aec37d

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since 7aec37d was 7aec37d, checked in by Ted Faber <faber@…>, 16 years ago

split out internal interfaces

  • Property mode set to 100644
File size: 21.6 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4
5from BaseHTTPServer import BaseHTTPRequestHandler
6from ZSI import *
7from M2Crypto import SSL
8from M2Crypto.m2xmlrpclib import SSL_Transport
9from M2Crypto.SSL.SSLServer import SSLServer
10import M2Crypto.httpslib
11import xmlrpclib
12
13import re
14import string
15import subprocess
16import tempfile
17import copy
18
19from fedd_services import *
20from fedd_util import *
21from fedd_allocate_project import *
22import parse_detail
23from service_error import *
24
25class fedd_proj:
26    """
27    The implementation of access control based on mapping users to projects.
28
29    Users can be mapped to existing projects or have projects created
30    dynamically.  This implements both direct requests and proxies.
31    """
32    # Attributes that can be parsed from the configuration file
33    bool_attrs = ("dynamic_projects", "project_priority")
34    emulab_attrs = ("boss", "ops", "domain", "fileserver", "eventserver")
35    id_attrs = ("testbed", "cert_file", "cert_pwd", "trusted_certs", "proxy",
36            "proxy_cert_file", "proxy_cert_pwd", "proxy_trusted_certs",
37            "dynamic_projects_url", "dynamic_projects_cert_file", 
38            "dynamic_projects_cert_pwd", "dynamic_projects_trusted_certs")
39
40    # Used by the SOAP caller
41    soap_namespaces = ('http://www.isi.edu/faber/fedd.wsdl',
42            'http://www.isi.edu/faber/fedd_internal.wsdl')
43    soap_methods = { 'RequestAccess': 'soap_RequestAccess' }
44    xmlrpc_methods = { 'RequestAccess': 'xmlrpc_RequestAccess' }
45
46    class access_project:
47        """
48        A project description used to grant access to this testbed.
49
50        The description includes a name and a list of node types to which the
51        project will be granted access.
52        """
53        def __init__(self, name, nt):
54            self.name = name
55            self.node_types = list(nt)
56
57        def __repr__(self):
58            if len(self.node_types) > 0:
59                return "access_proj('%s', ['%s'])" % \
60                        (self.name, str("','").join(self.node_types))
61            else:
62                return "access_proj('%s', [])" % self.name
63
64    # Used to report errors parsing the configuration files, not in providing
65    # service
66    class parse_error(RuntimeError): pass
67
68
69    def __init__(self, config=None):
70        """
71        Initializer.  Parses a configuration if one is given.
72        """
73
74        # Create instance attributes from the static lists
75        for a in fedd_proj.bool_attrs:
76            setattr(self, a, False)
77
78        for a in fedd_proj.emulab_attrs + fedd_proj.id_attrs:
79            setattr(self, a, None)
80
81        # Other attributes
82        self.attrs = {}
83        self.access = {}
84        self.fedid_category = {}
85        self.fedid_default = "user"
86        self.restricted = []
87
88        # Delete these
89        self.wap = '/usr/testbed/sbin/wap'
90        self.newproj = '/usr/testbed/sbin/newproj'
91        self.mkproj = '/usr/testbed/sbin/mkproj'
92        self.grantnodetype = '/usr/testbed/sbin/grantnodetype'
93
94        # Read the configuration
95        if config != None: 
96            self.read_config(config)
97
98        # Certs are promoted from the generic to the specific, so without a
99        # specific proxy certificate, the main certificates are used for proxy
100        # interactions. If no dynamic project certificates, then proxy certs
101        # are used, and if none of those the main certs.
102
103        # init proxy certs
104        if self.proxy_cert_file == None:
105            self.proxy_cert_file = self.cert_file
106            self.proxy_cert_pwd = self.cert_pwd
107
108        if self.proxy_trusted_certs == None:
109            self.proxy_trusted_certs = self.trusted_certs
110
111        # init dynamic project certs
112        if self.dynamic_projects_cert_file == None:
113            self.dynamic_projects_cert_file = self.proxy_cert_file
114            self.dynamic_projects_cert_pwd = self.proxy_cert_pwd
115
116        if self.dynamic_projects_trusted_certs == None:
117            self.dynamic_projects_trusted_certs = self.proxy_trusted_certs
118
119        proj_certs = (self.dynamic_projects_cert_file, 
120                self.dynamic_projects_trusted_certs,
121                self.dynamic_projects_cert_pwd)
122
123        if self.dynamic_projects_url == None:
124            self.allocate_project = \
125                fedd_allocate_project_local(self.dynamic_projects, 
126                        self.dynamic_projects_url, proj_certs)
127            fedd_proj.soap_methods['AllocateProject'] = 'soap_AllocateProject'
128        else:
129            self.allocate_project = \
130                fedd_allocate_project_remote(self.dynamic_projects, 
131                        self.dynamic_projects_url, proj_certs)
132
133    def dump_state(self):
134        """
135        Dump the state read from a configuration file.  Mostly for debugging.
136        """
137        for a in fedd_proj.bool_attrs:
138            print "%s: %s" % (a, getattr(self, a ))
139        for a in fedd_proj.emulab_attrs + fedd_proj.id_attrs:
140            print "%s: %s" % (a, getattr(self, a))
141        for k, v in self.attrs.iteritems():
142            print "%s %s" % (k, v)
143        print "Access DB:"
144        for k, v in self.access.iteritems():
145            print "%s %s" % (k, v)
146        print "Trust DB:"
147        for k, v in self.fedid_category.iteritems():
148            print "%s %s" % (k, v)
149        print "Restricted: %s" % str(',').join(sorted(self.restricted))
150
151    def get_users(self, obj):
152        """
153        Return a list of the IDs of the users in dict
154        """
155        if obj.has_key('user'):
156            return [ unpack_id(u['userID']) \
157                    for u in obj['user'] if u.has_key('userID') ]
158        else:
159            return None
160
161    def strip_unicode(self, obj):
162        """Loosly de-unicode an object"""
163        if isinstance(obj, dict):
164            for k in obj.keys():
165                obj[k] = self.strip_unicode(obj[k])
166            return obj
167        elif isinstance(obj, basestring):
168            return str(obj)
169        elif getattr(obj, "__iter__", None):
170            return [ self.strip_unicode(x) for x in obj]
171        else:
172            return obj
173
174    def proxy_xmlrpc_request(self, dt, req):
175        """Send an XMLRPC proxy request.  Called if the SOAP RPC fails"""
176
177        # No retry loop here.  Proxy servers must correctly authenticate
178        # themselves without help
179        try:
180            ctx = fedd_ssl_context(self.proxy_cert_file, 
181                    self.proxy_trusted_certs, password=self.proxy_cert_pwd)
182        except SSL.SSLError:
183            raise service_error(service_error.server_config,
184                    "Server certificates misconfigured")
185
186        # Of all the dumbass things.  The XMLRPC library in use here won't
187        # properly encode unicode strings, so we make a copy of req with the
188        # unicode objects converted.  We also convert the destination testbed
189        # to a basic string if it isn't one already.
190        if isinstance(dt, str): url = dt
191        else: url = str(dt)
192
193        r = copy.deepcopy(req)
194        self.strip_unicode(r)
195       
196        transport = SSL_Transport(ctx)
197        port = xmlrpclib.ServerProxy(url, transport=transport)
198
199        # Reconstruct the full request message
200        try:
201            resp = port.RequestAccess(
202                    { "RequestAccessRequestBody": r})
203            resp, method = xmlrpclib.loads(resp)
204        except xmlrpclib.Fault, f:
205            se = service_error(None, f.faultString, f.faultCode)
206            raise se
207        except xmlrpclib.Error, e:
208            raise service_error(service_error.proxy, 
209                    "Remote XMLRPC Fault: %s" % e)
210       
211        if resp[0].has_key('RequestAccessResponseBody'):
212            return resp[0]['RequestAccessResponseBody']
213        else:
214            raise service_error(service_error.proxy, 
215                    "Bad proxy response")
216
217    def proxy_request(self, dt, req):
218        """
219        Send req on to the real destination in dt and return the response
220
221        Req is just the requestType object.  This function re-wraps it.  It
222        also rethrows any faults.
223        """
224        # No retry loop here.  Proxy servers must correctly authenticate
225        # themselves without help
226        try:
227            ctx = fedd_ssl_context(self.proxy_cert_file, 
228                    self.proxy_trusted_certs, password=self.proxy_cert_pwd)
229        except SSL.SSLError:
230            raise service_error(service_error.server_config, 
231                    "Server certificates misconfigured")
232
233        loc = feddServiceLocator();
234        port = loc.getfeddPortType(dt,
235                transport=M2Crypto.httpslib.HTTPSConnection, 
236                transdict={ 'ssl_context' : ctx })
237
238        # Reconstruct the full request message
239        msg = RequestAccessRequestMessage()
240        msg.set_element_RequestAccessRequestBody(
241                pack_soap(msg, "RequestAccessRequestBody", req))
242        try:
243            resp = port.RequestAccess(msg)
244        except ZSI.ParseException, e:
245            raise service_error(service_error.proxy,
246                    "Bad format message (XMLRPC??): %s" %
247                    str(e))
248        r = unpack_soap(resp)
249
250        if r.has_key('RequestAccessResponseBody'):
251            return r['RequestAccessResponseBody']
252        else:
253            raise service_error(service_error.proxy,
254                    "Bad proxy response")
255
256    def permute_wildcards(self, a, p):
257        """Return a copy of a with various fields wildcarded.
258
259        The bits of p control the wildcards.  A set bit is a wildcard
260        replacement with the lowest bit being user then project then testbed.
261        """
262        if p & 1: user = ["<any>"]
263        else: user = a[2]
264        if p & 2: proj = "<any>"
265        else: proj = a[1]
266        if p & 4: tb = "<any>"
267        else: tb = a[0]
268
269        return (tb, proj, user)
270
271    def find_access(self, search):
272        """
273        Search the access DB for a match on this tuple.  Return the matching
274        access tuple and the user that matched.
275       
276        NB, if the initial tuple fails to match we start inserting wildcards in
277        an order determined by self.project_priority.  Try the list of users in
278        order (when wildcarded, there's only one user in the list).
279        """
280        if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7)
281        else: perm = (0, 2, 1, 3, 4, 6, 5, 7)
282
283        for p in perm: 
284            s = self.permute_wildcards(search, p)
285            # s[2] is None on an anonymous, unwildcarded request
286            if s[2] != None:
287                for u in s[2]:
288                    if self.access.has_key((s[0], s[1], u)):
289                        return (self.access[(s[0], s[1], u)], u)
290            else:
291                if self.access.has_key(s):
292                    return (self.access[s], None)
293        return None
294
295    def lookup_access(self, req, fid):
296        """
297        Determine the allowed access for this request.  Return the access and
298        which fields are dynamic.
299
300        The fedid is needed to construct the request
301        """
302        tb = None
303        project = None
304        user = None
305
306        principal_type = self.fedid_category.get(fid, self.fedid_default)
307
308        if principal_type == "testbed": tb = fid
309
310        if req.has_key('project'):
311            p = req['project']
312            if p.has_key('name'):
313                project = unpack_id(p['name'])
314            user = self.get_users(p)
315        else:
316            user = self.get_users(req)
317
318        # Now filter by prinicpal type
319        if principal_type == "user":
320            if user != None:
321                fedids = [ u for u in user if isinstance(u, type(fid))]
322                if len(fedids) > 1:
323                    raise service_error(service_error.req,
324                            "User asserting multiple fedids")
325                elif len(fedids) == 1 and fedids[0] != fid:
326                    raise service_error(service_error.req,
327                            "User asserting different fedid")
328            project = None
329            tb = None
330        elif principal_type == "project":
331            if isinstance(project, type(fid)) and fid != project:
332                raise service_error(service_error.req,
333                        "Project asserting different fedid")
334            tb = None
335
336        # Ready to look up access
337        found, user_match = self.find_access((tb, project, user))
338       
339        if found == None:
340            raise service_error(service_error.access,
341                    "Access denied")
342
343        # resolve <dynamic> and <same> in found
344        dyn_proj = False
345        dyn_user = False
346
347        if found[0].name == "<same>":
348            if project != None:
349                found[0].name = project
350            else : 
351                raise service_error(\
352                        service_error.server_config,
353                        "Project matched <same> when no project given")
354        elif found[0].name == "<dynamic>":
355            found[0].name = None
356            dyn_proj = True
357
358        if found[1] == "<same>":
359            if user_match == "<any>":
360                if user != None: found = (found[0], user[0])
361                else: raise service_error(\
362                        service_error.server_config,
363                        "Matched <same> on anonymous request")
364            else:
365                found = (found[0], user_match)
366        elif found[1] == "<dynamic>":
367            found = (found[0], None)
368            dyn_user = True
369       
370        return found, (dyn_user, dyn_proj)
371
372    def build_response(self, alloc_id, ap):
373        """
374        Create the SOAP response.
375
376        Build the dictionary description of the response and use
377        fedd_utils.pack_soap to create the soap message.  NB that alloc_id is
378        a fedd_services_types.IDType_Holder pulled from the incoming message.
379        ap is the allocate project message returned from a remote project
380        allocation (even if that allocation was done locally).
381        """
382        # Because alloc_id is already a fedd_services_types.IDType_Holder,
383        # there's no need to repack it
384        msg = { 
385                'allocID': alloc_id,
386                'emulab': { 
387                    'domain': self.domain,
388                    'boss': self.boss,
389                    'ops': self.ops,
390                    'fileServer': self.fileserver,
391                    'eventServer': self.eventserver,
392                    'project': ap['project']
393                },
394            }
395        if len(self.attrs) > 0:
396            msg['emulab']['fedAttr'] = \
397                [ { 'attribute': x, 'value' : y } \
398                        for x,y in self.attrs.iteritems()]
399        return msg
400
401    def RequestAccess(self, req, fid):
402
403        if req.has_key('RequestAccessRequestBody'):
404            req = req['RequestAccessRequestBody']
405        else:
406            raise service_error(service_error.req, "No request!?")
407
408        if req.has_key('destinationTestbed'):
409            dt = unpack_id(req['destinationTestbed'])
410       
411        if dt == None or dt == self.testbed:
412            # Request for this fedd
413            found, dyn = self.lookup_access(req, fid)
414            restricted = None
415            ap = None
416
417            # Check for access to restricted nodes
418            if req.has_key('resources') and req['resources'].has_key('node'):
419                resources = req['resources']
420                restricted = [ t for n in resources['node'] \
421                                if n.has_key('hardware') \
422                                    for t in n['hardware'] \
423                                        if t in self.restricted ]
424                inaccessible = [ t for t in restricted \
425                                    if t not in found[0].node_types]
426                if len(inaccessible) > 0:
427                    raise service_error(service_error.access,
428                            "Access denied (nodetypes %s)" % \
429                            str(', ').join(inaccessible))
430
431            ssh = [ x['sshPubkey'] \
432                    for x in req['access'] if x.has_key('sshPubkey')]
433
434            if len(ssh) > 0: 
435                if dyn[1]: 
436                    # Compose the dynamic project request
437                    # (only dynamic, dynamic currently allowed)
438                    preq = { 'AllocateProjectRequestBody': \
439                                { 'project' : {\
440                                    'user': [ \
441                                    { 'access': [ { 'sshPubkey': s } ] } \
442                                        for s in ssh ] \
443                                    }\
444                                }\
445                            }
446                    if restricted != None and len(restricted) > 0:
447                        preq['AllocateProjectRequestBody']['resources'] = \
448                            [ {'node': { 'hardware' :  [ h ] } } \
449                                    for h in restricted ]
450                               
451                    ap = self.allocate_project.dynamic_project(preq)
452                else:
453                    # XXX ssh key additions
454                    ap = { 'project': \
455                            { 'name' : { 'username' : found[0] },\
456                              'user' : [ {\
457                                'userID': { 'username' : found[1] }, \
458                                'access': [ { 'sshPubkey': s } for s in ssh]}\
459                                ]\
460                            }\
461                    }
462            else:
463                raise service_error(service_error.req, 
464                        "SSH access parameters required")
465
466            resp = self.build_response(req['allocID'], ap)
467            return resp
468        else:
469            p_fault = None      # Any SOAP failure (sent unless XMLRPC works)
470            try:
471                # Proxy the request using SOAP
472                return self.proxy_request(dt, req)
473            except service_error, e:
474                if e.code == service_error.proxy: p_fault = None
475                else: raise
476            except ZSI.FaultException, f:
477                p_fault = f.fault.detail[0]
478                   
479
480            # If we could not get a valid SOAP response to the request above,
481            # try the same address using XMLRPC and let any faults flow back
482            # out.
483            if p_fault == None:
484                return self.proxy_xmlrpc_request(dt, req)
485            else:
486                # Build the fault
487                body = p_fault.get_element_RequestAccessFaultBody()
488                if body != None:
489                    raise service_error(body.get_element_code(),
490                                body.get_element_desc());
491                else:
492                    raise service_error(\
493                            service_error.proxy,
494                            "Undefined fault from proxy??");
495
496
497    def soap_AllocateProject(self, ps, fid):
498        req = ps.Parse(AllocateProjectRequestMessage.typecode)
499
500        msg = self.allocate_project.dynamic_project(unpack_soap(req), fedid)
501
502        resp = AllocateProjectResponseMessage()
503        resp.set_element_AllocateProjectResponseBody(
504                pack_soap(resp, "AllocateProjectResponseBody", msg))
505
506        return resp
507
508    def soap_RequestAccess(self, ps, fid):
509        req = ps.Parse(RequestAccessRequestMessage.typecode)
510
511        msg = self.RequestAccess(unpack_soap(req), fedid)
512
513        resp = RequestAccessResponseMessage()
514        resp.set_element_RequestAccessResponseBody(
515                pack_soap(resp, "RequestAccessResponseBody", msg))
516
517        return resp
518
519    def xmlrpc_RequestAccess(self, params, fid):
520        msg = self.RequestAccess(params[0], fedid)
521
522        if msg != None:
523            return xmlrpclib.dumps(({ "RequestAccessResponseBody": msg },))
524        else:
525            raise service_error(service_error.internal,
526                    "No response generated?!");
527
528    def read_trust(self, trust):
529        """
530        Read a trust file that splits fedids into testbeds, users or projects
531
532        Format is:
533
534        [type]
535        fedid
536        fedid
537        default: type
538        """
539        lineno = 0;
540        cat = None
541        cat_re = re.compile("\[(user|testbed|project)\]$", re.IGNORECASE)
542        fedid_re = re.compile("[" + string.hexdigits + "]+$")
543        default_re = re.compile("default:\s*(user|testbed|project)$", 
544                re.IGNORECASE)
545
546        f = open(trust, "r")
547        for line in f:
548            lineno += 1
549            line = line.strip()
550            if len(line) == 0 or line.startswith("#"):
551                continue
552            # Category line
553            m = cat_re.match(line)
554            if m != None:
555                cat = m.group(1).lower()
556                continue
557            # Fedid line
558            m = fedid_re.match(line)
559            if m != None:
560                if cat != None:
561                    self.fedid_category[fedid(hexstr=m.string)] = cat
562                else:
563                    raise fedd_proj.parse_error(\
564                            "Bad fedid in trust file (%s) line: %d" % \
565                            (trust, lineno))
566                continue
567            # default line
568            m = default_re.match(line)
569            if m != None:
570                self.fedid_default = m.group(1).lower()
571                continue
572            # Nothing matched - bad line, raise exception
573            f.close()
574            raise fedd_proj.parse_error(\
575                    "Unparsable line in trustfile %s line %d" % (trust, lineno))
576        f.close()
577
578    def read_config(self, config):
579        """
580        Read a configuration file and set internal parameters.
581
582        The format is more complex than one might hope.  The basic format is
583        attribute value pairs separated by colons(:) on a signle line.  The
584        attributes in bool_attrs, emulab_attrs and id_attrs can all be set
585        directly using the name: value syntax.  E.g.
586        boss: hostname
587        sets self.boss to hostname.  In addition, there are access lines of the
588        form (tb, proj, user) -> (aproj, auser) that map the first tuple of
589        names to the second for access purposes.  Names in the key (left side)
590        can include "<NONE> or <ANY>" to act as wildcards or to require the
591        fields to be empty.  Similarly aproj or auser can be <SAME> or
592        <DYNAMIC> indicating that either the matching key is to be used or a
593        dynamic user or project will be created.  These names can also be
594        federated IDs (fedid's) if prefixed with fedid:.  Finally, the aproj
595        can be followed with a colon-separated list of node types to which that
596        project has access (or will have access if dynamic).
597        Testbed attributes outside the forms above can be given using the
598        format attribute: name value: value.  The name is a single word and the
599        value continues to the end of the line.  Empty lines and lines startin
600        with a # are ignored.
601
602        Parsing errors result in a parse_error exception being raised.
603        """
604        lineno=0
605        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
606        fedid_expr = "fedid:[" + string.hexdigits + "]+"
607        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
608        access_proj = "(<DYNAMIC>(?::" + name_expr +")*|"+ \
609                "<SAME>" + "(?::" + name_expr + ")*|" + \
610                fedid_expr + "(?::" + name_expr + ")*|" + \
611                name_expr + "(?::" + name_expr + ")*)"
612        access_name = "(<DYNAMIC>|<SAME>|" + fedid_expr + "|"+ name_expr + ")"
613
614        bool_re = re.compile('(' + '|'.join(fedd_proj.bool_attrs) + 
615                '):\s+(true|false)', re.IGNORECASE)
616        string_re = re.compile( "(" + \
617                '|'.join(fedd_proj.emulab_attrs + fedd_proj.id_attrs) + \
618                '):\s*(.*)', re.IGNORECASE)
619        attr_re = re.compile('attribute:\s*([\._\-a-z0-9]+)\s+value:\s*(.*)',
620                re.IGNORECASE)
621        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
622                key_name+'\s*\)\s*->\s*\('+access_proj + '\s*,\s*' + 
623                access_name + '\s*\)', re.IGNORECASE)
624        trustfile_re = re.compile("trustfile:\s*(.*)", re.IGNORECASE)
625        restricted_re = re.compile("restricted:\s*(.*)", re.IGNORECASE)
626
627        def parse_name(n):
628            if n.startswith('fedid:'): return fedid(n[len('fedid:'):])
629            else: return n
630
631        f = open(config, "r");
632        for line in f:
633            lineno += 1
634            line = line.strip();
635            if len(line) == 0 or line.startswith('#'):
636                continue
637
638            # Boolean attribute line
639            m = bool_re.match(line);
640            if m != None:
641                attr, val = m.group(1,2)
642                setattr(self, attr.lower(), bool(val.lower() == "true"))
643                continue
644
645            # String attribute line
646            m = string_re.match(line)
647            if m != None:
648                attr, val = m.group(1,2)
649                setattr(self, attr.lower(), val)
650                continue
651
652            # Extended (attribute: x value: y) attribute line
653            m = attr_re.match(line)
654            if m != None:
655                attr, val = m.group(1,2)
656                self.attrs[attr] = val
657                continue
658
659            # Access line (t, p, u) -> (ap, au) line
660            m = access_re.match(line)
661            if m != None:
662                access_key = tuple([ parse_name(x) for x in m.group(1,2,3)])
663                aps = m.group(4).split(":");
664                if aps[0] == 'fedid:':
665                    del aps[0]
666                    aps[0] = fedid(hexstr=aps[0])
667
668                au = m.group(5)
669                if au.startswith("fedid:"):
670                    au = fedid(hexstr=aus[len("fedid:"):])
671
672                access_val = (fedd_proj.access_project(aps[0], aps[1:]), au)
673
674                self.access[access_key] = access_val
675                continue
676
677            # Trustfile inclusion
678            m = trustfile_re.match(line)
679            if m != None:
680                self.read_trust(m.group(1))
681                continue
682            # Restricted node types
683
684            m = restricted_re.match(line)
685            if m != None:
686                self.restricted.append(m.group(1))
687                continue
688
689            # Nothing matched to here: unknown line - raise exception
690            f.close()
691            raise fedd_proj.parse_error("Unknown statement at line %d of %s" % \
692                    (lineno, config))
693        f.close()
694
695    def soap_dispatch(self, method, req, fid):
696        if fedd_proj.soap_methods.has_key(method):
697            try:
698                return getattr(self, fedd_proj.soap_methods[method])(req, fid)
699            except service_error, e:
700                de = ns0.faultType_Def(
701                        (ns0.faultType_Def.schema,
702                            "RequestAccessFaultBody")).pyclass()
703                de._code=e.code
704                de._errstr=e.code_string()
705                de._desc=e.desc
706                if  e.is_server_error():
707                    raise Fault(Fault.Server, e.code_string(), detail=de)
708                else:
709                    raise Fault(Fault.Client, e.code_string(), detail=de)
710        else:
711            raise Fault(Fault.Client, "Unknown method: %s" % method)
712
713    def xmlrpc_dispatch(self, method, req, fid):
714        if fedd_proj.xmlrpc_methods.has_key(method):
715            try:
716                return getattr(self, fedd_proj.xmlrpc_methods[method])(req, fid)
717            except service_error, e:
718                raise xmlrpclib.Fault(e.code_string(), e.desc)
719        else:
720            raise xmlrpclib.Fault(100, "Unknown method: %s" % method)
721
722def new_feddservice(configfile):
723    return fedd_proj(configfile)
Note: See TracBrowser for help on using the repository browser.