source: fedd/fedd_proj.py @ b234bb9

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

Experiment creation integrated

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