source: fedd/federation/allocate_project.py @ c3dcf48

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

Snapshot of new state-based allocation

  • Property mode set to 100644
File size: 25.1 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4import re
5import random
6import string
7import subprocess
8import threading
9import pickle
10import tempfile
11
12from util import *
13from fedid import fedid
14from fixed_resource import read_key_db, read_project_db, read_user_db
15from remote_service import xmlrpc_handler, soap_handler, service_caller
16from service_error import service_error
17import logging
18
19
20# Configure loggers to dump to /dev/null which avoids errors if calling classes
21# don't configure them.
22class nullHandler(logging.Handler):
23    def emit(self, record): pass
24
25fl = logging.getLogger("fedd.allocate.local")
26fl.addHandler(nullHandler())
27fl = logging.getLogger("fedd.allocate.remote")
28fl.addHandler(nullHandler())
29
30
31class allocate_project_local:
32    """
33    Allocate projects on this machine in response to an access request.
34    """
35    dynamic_projects = 4
36    dynamic_keys= 2
37    confirm_keys = 1
38    none = 0
39
40    levels = {
41            'dynamic_projects': dynamic_projects,
42            'dynamic_keys': dynamic_keys,
43            'confirm_keys': confirm_keys,
44            'none': none,
45    }
46
47    def __init__(self, config, auth=None):
48        """
49        Initializer.  Parses a configuration if one is given.
50        """
51
52        self.debug = config.getboolean("allocate", "debug", False)
53        self.wap = config.get('allocate', 'wap', '/usr/testbed/sbin/wap')
54        self.newproj = config.get('allocate', 'newproj',
55                '/usr/testbed/sbin/newproj')
56        self.mkproj = config.get('allocate', 'mkproj', 
57                '/usr/testbed/sbin/mkproj')
58        self.rmproj = config.get('allocate', 'rmproj',
59                '/usr/testbed/sbin/rmproj')
60        self.rmuser = config.get('allocate', 'rmuser',
61                '/usr/testbed/sbin/rmuser')
62        self.newuser = config.get('allocate', 'newuser',
63                '/usr/testbed/sbin/newuser')
64        self.addpubkey = config.get('allocate', 'addpubkey', 
65                '/usr/testbed/sbin/addpubkey')
66        self.grantnodetype = config.get('allocate', 'grantnodetype', 
67                '/usr/testbed/sbin/grantnodetype')
68        self.confirmkey = config.get('allocate', 'confirmkey', 
69                '/usr/testbed/sbin/taddpubkey')
70        self.user_to_project=config.get("allocate", 'user_to_project',
71                '/usr/local/bin/user_to_project.py')
72        self.allocation_level = config.get("allocate", "allocation_level", 
73                "none")
74        self.log = logging.getLogger("fedd.allocate.local")
75        set_log_level(config, "allocate", self.log)
76
77        if auth:
78            self.auth = auth
79        else:
80            auth = authorizer()
81            log.warn("[allocate] No authorizer passed in, using local one")
82
83        try:
84            self.allocation_level = \
85                    self.levels[self.allocation_level.strip().lower()]
86        except KeyError:
87            self.log.error("Bad allocation_level %s.  Defaulting to none" % \
88                    self.allocation_error)
89            self.allocation_level = self.none
90
91        self.state = { 
92                'keys': set(), 
93                'types': set(),
94                'projects': set(),
95                'users': set(),
96                }
97        self.state_filename = config.get('allocate', 'allocation_state')
98        self.state_lock = threading.Lock()
99        self.read_state()
100
101        access_db = config.get("allocate", "accessdb")
102        if access_db:
103            try:
104                read_simple_accessdb(access_db, self.auth, 'allocate')
105            except IOError, e:
106                raise service_error(service_error.internal,
107                        "Error reading accessDB %s: %s" % (access_db, e))
108            except ValueError:
109                raise service_error(service_error.internal, "%s" % e)
110
111
112        fixed_key_db = config.get("allocate", "fixed_keys", None)
113        fixed_project_db = config.get("allocate", "fixed_projects", None)
114        fixed_user_db = config.get("allocate", "fixed_users", None)
115        self.fixed_keys = set()
116        self.fixed_projects = set()
117        self.fixed_users = set()
118
119        # initialize the fixed resource sets
120        for db, rset, fcn in (\
121                (fixed_key_db, self.fixed_keys, read_key_db), \
122                (fixed_project_db, self.fixed_projects, read_project_db),
123                (fixed_user_db, self.fixed_users, read_user_db)):
124            if db:
125                try:
126                    rset.update(fcn(db))
127                except:
128                    self.log.debug("Can't read resources from %s" % db)
129       
130        # Internal services are SOAP only
131        self.soap_services = {\
132                "AllocateProject": soap_handler("AllocateProject", 
133                    self.dynamic_project),
134                "StaticProject": soap_handler("StaticProject", 
135                    self.static_project),
136                "ReleaseProject": soap_handler("ReleaseProject", 
137                    self.release_project),
138                }
139        self.xmlrpc_services = { }
140
141    def read_state(self):
142        """
143        Read a new copy of access state.  Old state is overwritten.
144
145        State format is a simple pickling of the state dictionary.
146        """
147        if self.state_filename:
148            try:
149                f = open(self.state_filename, "r")
150                self.state = pickle.load(f)
151                self.log.debug("[allocation]: Read state from %s" % \
152                        self.state_filename)
153            except IOError, e:
154                self.log.warning(("[allocation]: No saved state: " +\
155                        "Can't open %s: %s") % (self.state_filename, e))
156            except EOFError, e:
157                self.log.warning(("[allocation]: " +\
158                        "Empty or damaged state file: %s:") % \
159                        self.state_filename)
160            except pickle.UnpicklingError, e:
161                self.log.warning(("[allocation]: No saved state: " + \
162                        "Unpickling failed: %s") % e)
163            # These should all be in the picked representation, but make sure
164            if not self.state.has_key('keys'): self.state['keys'] = set()
165            if not self.state.has_key('types'): self.state['types'] = set()
166            if not self.state.has_key('projects'):
167                self.state['projects'] = set()
168            if not self.state.has_key('users'): self.state['users'] = set()
169
170    def write_state(self):
171        if self.state_filename:
172            try:
173                f = open(self.state_filename, 'w')
174                pickle.dump(self.state, f)
175            except IOError, e:
176                self.log.error("Can't write file %s: %s" % \
177                        (self.state_filename, e))
178            except pickle.PicklingError, e:
179                self.log.error("Pickling problem: %s" % e)
180            except TypeError, e:
181                self.log.error("Pickling problem (TypeError): %s" % e)
182
183
184    def random_string(self, s, n=3):
185        """Append n random ASCII characters to s and return the string"""
186        rv = s
187        for i in range(0,n):
188            rv += random.choice(string.ascii_letters)
189        return rv
190
191    def write_attr_xml(self, file, root, lines):
192        """
193        Write an emulab config file for a dynamic project.
194
195        Format is <root><attribute name=lines[0]>lines[1]</attribute></root>
196        """
197        # Convert a pair to an attribute line
198        out_attr = lambda a,v : \
199                '<attribute name="%s"><value>%s</value></attribute>' % (a, v)
200
201        f = os.fdopen(file, "w")
202        f.write("<%s>\n" % root)
203        f.write("\n".join([out_attr(*l) for l in lines]))
204        f.write("</%s>\n" % root)
205        f.close()
206
207    def run_cmd(self, cmd, log_prefix='allocate'):
208        """
209        Run the command passed in.  Cmd is a list containing the words of the
210        command.  Return the exit value from the subprocess - that is 0 on
211        success.  On an error running the command -  python or OS error, raise
212        a service exception.
213        """
214        self.log.debug("[%s]: %s" % (log_prefix, ' '.join(cmd)))
215        if not self.debug:
216            try:
217                return subprocess.call(cmd)
218            except OSError, e:
219                raise service_error(service_error.internal,
220                        "Static project subprocess creation error "+ \
221                                "[%s] (%s)" %  (cmd[0], e.strerror))
222        else:
223            return 0
224
225    def confirm_key(self, user, key):
226        """
227        Call run_cmd to comfirm the key.  Return a boolean rather
228        than the subprocess code.
229        """
230        return self.run_cmd((self.wap, self.confirmkey, '-C', 
231            '-u', user, '-k', key)) ==0
232
233    def add_key(self, user, key):
234        """
235        Call run_cmd to add the key.  Return a boolean rather
236        than the subprocess code.
237        """
238        return self.run_cmd((self.wap, self.addpubkey, '-u', user,
239            '-k', key)) == 0
240
241    def remove_key(self, user, key):
242        """
243        Call run_cmd to remove the key.  Return a boolean rather
244        than the subprocess code.
245        """
246        return self.run_cmd((self.wap, self.addpubkey, '-R', '-u', user, 
247            '-k', key)) == 0
248
249    def confirm_access(self, project, type):
250        """
251        Call run_cmd to comfirm the key.  Return a boolean rather
252        than the subprocess code.
253        """
254        return self.run_cmd((self.wap, self.grantnodetype, '-C', 
255            '-p', project, type)) ==0
256
257    def add_access(self, project, type):
258        """
259        Call run_cmd to add the key.  Return a boolean rather
260        than the subprocess code.
261        """
262        return self.run_cmd((self.wap, self.grantnodetype, 
263            '-p', project, type)) == 0
264
265    def remove_access(self, project, type):
266        """
267        Call run_cmd to remove the key.  Return a boolean rather
268        than the subprocess code.
269        """
270
271        return self.run_cmd((self.wap, self.grantnodetype, '-R', 
272            '-p', project, type)) == 0
273
274    def add_project(self, project, projfile):
275        """
276        Create a project using run_cmd.  This is two steps, and assumes that
277        the relevant XML files are in place and correct.  Make the return value
278        boolean.  Note that if a new user is specified in the XML, that user is
279        created on success.
280        """
281
282        if self.run_cmd((self.wap, self.newproj, projfile)) == 0:
283            return self.run_cmd((self.wap, self.mkproj, project)) ==0
284        else:
285            return False
286
287    def remove_project(self, project):
288        """
289        Call run_cmd to remove the project.  Make the return value boolean.
290        """
291
292        return self.run_cmd(self.wap, self.rmproj, project) == 0
293
294   
295    def add_user(self, name, param_file, project):
296        """
297        Create a user and link them to the given project.  Similar to
298        add_project, this requires a two step approach.  Returns True on success
299        False on failure.
300        """
301
302        if self.run_cmd((self.wap, self.newuser, param_file)) == 0:
303           return self.run_cmd((self.wap, self.user_to_project,
304               user, project)) == 0
305        else:
306           return False
307
308    def remove_user(self, user):
309        """
310        Call run_cmd to remove the user.  Make the return value boolean.
311        """
312
313        return self.run_cmd(self.wap, self.rmuser, user) == 0
314
315
316
317    def dynamic_project(self, req, fedid=None):
318        """
319        Create a dynamic project with ssh access
320
321        Req includes the project and resources as a dictionary
322        """
323
324        # Internal calls do not have a fedid parameter (i.e., local calls on
325        # behalf of already vetted fedids)
326        if fedid and not self.auth.check_attribute(fedid, "allocate"):
327            self.log.debug("[allocate] Access denied (%s)" % fedid)
328            raise service_error(service_error.access, "Access Denied")
329
330        if self.allocation_level < self.dynamic_projects:
331            raise service_error(service_error.access, 
332                    "[dynamic_project] dynamic project allocation not " + \
333                            "permitted: check allocation level")
334        # tempfiles for the parameter files
335        cuf, create_userfile = tempfile.mkstemp(prefix="usr", suffix=".xml",
336                dir="/tmp")
337        suf, service_userfile = tempfile.mkstemp(prefix="usr", suffix=".xml",
338                dir="/tmp")
339        pf, projfile = tempfile.mkstemp(prefix="proj", suffix=".xml",
340                dir="/tmp")
341
342        if req.has_key('AllocateProjectRequestBody'):
343            proj = req['AllocateProjectRequestBody'].get('project', None)
344            if not proj:
345                raise service_error(service_error.req, 
346                        "Badly formed allocation request")
347            resources = req['AllocateProjectRequestBody'].get('resources', { })
348        else:
349            raise service_error(service_error.req, 
350                    "Badly formed allocation request")
351        # Take the first user and ssh key
352        name = proj.get('name', None) or self.random_string("proj",4)
353        user = proj.get('user', [])
354
355        uname = { }
356        ssh = { }
357        for u in user:
358            role = u.get('role', None)
359            if not role: continue
360            if u.has_key('userID'):
361                uid = u['userID']
362                uname[role] = uid.get('localname', None) or \
363                        uid.get('kerberosUsername', None) or \
364                        uid.get('uri', None)
365                if uname[role] == None:
366                    raise service_error(service_error.req, "No ID for user")
367            else:
368                uname[role] = self.random_string("user", 3)
369
370            access = u.get('access', None)
371            if access:
372                # XXX collect and call addpubkey later, for now use first one.
373                for a in access:
374                    ssh[role] = a.get('sshPubkey', None)
375                    if ssh: break
376                else:
377                    raise service_error(service_error.req,
378                            "No SSH key for user %s" % uname[role])
379            else:
380                raise service_error(service_error.req,
381                        "No access mechanisms for for user %s" % uname[role])
382
383        if not (uname.has_key('experimentCreation') and \
384                uname.has_key('serviceAccess')):
385            raise service_error(service_error.req,
386                    "Must specify both user roles")
387       
388
389        create_user_fields = [
390                ("name", "Federation User %s" % uname['experimentCreation']),
391                ("email", "%s-fed@isi.deterlab.net" % \
392                        uname['experimentCreation']),
393                ("password", self.random_string("", 8)),
394                ("login", uname['experimentCreation']),
395                ("address", "4676 Admiralty"),
396                ("city", "Marina del Rey"),
397                ("state", "CA"),
398                ("zip", "90292"),
399                ("country", "USA"),
400                ("phone", "310-448-9190"),
401                ("title", "None"),
402                ("affiliation", "USC/ISI"),
403                ("pubkey", ssh['experimentCreation'])
404        ]
405
406        service_user_fields = [
407                ("name", "Federation User %s" % uname['serviceAccess']),
408                ("email", "%s-fed@isi.deterlab.net" % uname['serviceAccess']),
409                ("password", self.random_string("", 8)),
410                ("login", uname['serviceAccess']),
411                ("address", "4676 Admiralty"),
412                ("city", "Marina del Rey"),
413                ("state", "CA"),
414                ("zip", "90292"),
415                ("country", "USA"),
416                ("phone", "310-448-9190"),
417                ("title", "None"),
418                ("affiliation", "USC/ISI"),
419                ("pubkey", ssh['serviceAccess'])
420        ]
421
422        proj_fields = [
423                ("name", name),
424                ("short description", "dynamic federated project"),
425                ("URL", "http://www.isi.edu/~faber"),
426                ("funders", "USC/USU"),
427                ("long description", "Federation access control"),
428                ("public", "1"),
429                ("num_pcs", "100"),
430                ("linkedtous", "1"),
431                ("newuser_xml", create_userfile)
432        ]
433       
434
435
436        added_projects = [ ]
437        added_users = [ ]
438        added_types = [ ]
439
440        self.state_lock.acquire()
441        try:
442            # Write out the files
443            self.write_attr_xml(cuf, "user", create_user_fields)
444            self.write_attr_xml(suf, "user", service_user_fields)
445            self.write_attr_xml(pf, "project", proj_fields)
446            try:
447                if self.add_project(name, projfile):
448                    # add_project adds a user as well in this case
449                    added_projects.append(name)
450                    added_users.append(uname['createExperiment'])
451                    self.state['projects'].add(name)
452                    self.state['users'].add(uname['createExperiment'])
453
454                    if self.add_user(uname['serviceAccess'], 
455                            service_userfile, name):
456                        added_users.append(uname['serviceAccess'])
457                        self.state['users'].add(uname['serviceAccess'])
458                    else:
459                        raise service_error("Unable to create user %s" % \
460                                uname['serviceAccess'])
461                else:
462                    raise service_error("Unable to create project/user %s/%s" % \
463                            (name, uname['experimentCreation']))
464
465                nodes = resources.get('node', [])
466                # Grant access to restricted resources.  This is simpler than
467                # the corresponding loop from static_project because this is a
468                # clean slate.
469                for nt in [ h for n in nodes\
470                        if n.has_key('hardware')\
471                            for h in n['hardware'] ] :
472                    if self.add_access(name, nt):
473                        self.state['types'].add((name, nt))
474                        added_types.append((name, nt))
475                    else:
476                        raise service_error(service_error.internal,
477                                "Failed to add access for %s to %s"\
478                                        % (name, nt))
479            except service_error, e:
480                # Something failed.  Back out the partial allocation as
481                # completely as possible and re-raise the error.
482                for p, t in added_types:
483                    self.state['types'].discard((p, t))
484                    try:
485                        self.remove_access(p, t)
486                    except service_error:
487                        pass
488                for u in added_users:
489                    self.state['users'].discard(u)
490                    try:
491                        self.remove_user(u)
492                    except service_error:
493                        pass
494
495                for p in added_projects:
496                    self.state['projects'].discard(p)
497                    try:
498                        self.remove_project(p)
499                    except service_error:
500                        pass
501                self.state_lock.release()
502                raise e
503        finally:
504            # Clean up tempfiles
505            os.unlink(create_userfile)
506            os.unlink(service_userfile)
507            os.unlink(projfile)
508
509        rv = {\
510            'project': {\
511                'name': { 'localname': name }, 
512                'user' : [\
513                    {\
514                        'userID': { 'localname' : uname['experimentCreation'] },
515                        'access': [ {'sshPubkey': ssh['experimentCreation'] } ],
516                        'role': 'experimentCreation',
517                    }, \
518                    {\
519                        'userID': { 'localname' : uname['serviceAccess'] },
520                        'access': [ { 'sshPubkey' : ssh['serviceAccess'] } ], 
521                        'role': 'serviceAccess',
522                    } \
523                ]\
524            }\
525        }
526        return rv
527
528    def static_project(self, req, fedid=None):
529        """
530        Be certain that the local project in the request has access to the
531        proper resources and users have correct keys.  Add them if necessary.
532        """
533        # Internal calls do not have a fedid parameter (i.e., local calls on
534        # behalf of already vetted fedids)
535        if fedid and not self.auth.check_attribute(fedid, "allocate"):
536            self.log.debug("[allocate] Access denied (%s)" % fedid)
537            raise service_error(service_error.access, "Access Denied")
538        # While we should be more careful about this, for the short term, add
539        # the keys to the specified users.
540
541        try:
542            users = req['StaticProjectRequestBody']['project']['user']
543            pname = req['StaticProjectRequestBody']['project']\
544                    ['name']['localname']
545            resources = req['StaticProjectRequestBody'].get('resources', { })
546        except KeyError:
547            raise service_error(service_error.req, "Badly formed request")
548
549        added_keys = [ ]
550        added_types = [ ]
551        # Keep track of changes made to the system
552        self.state_lock.acquire()
553
554        try:
555            for u in users:
556                try:
557                    name = u['userID']['localname']
558                except KeyError:
559                    raise service_error(service_error.req, "Badly formed user")
560                for sk in [ k['sshPubkey'] for k in u.get('access', []) \
561                        if k.has_key('sshPubkey')]:
562                    if self.allocation_level >=self.confirm_keys:
563                        key_ok = self.confirm_key(name, sk)
564                        if not key_ok:
565                            if self.allocation_level >= self.dynamic_keys:
566                                if self.add_key(name, sk):
567                                    self.state['keys'].add((name, sk))
568                                    added_keys.append((name, sk))
569                                else:
570                                    raise service_error(service_error.internal,
571                                            "Failed to add key for %s" % name)
572                            else:
573                                raise service_error(service_error.internal,
574                                        "Failed to confirm key for %s" % name)
575                    else:
576                        self.log.warning("[static_project] no checking of " + \
577                            "static keys")
578
579            # Grant access to any resources in the request.  The
580            # list comprehension pulls out the hardware types in the node
581            # entries in the resources list.  The access module knows to
582            # only send resources that are restricted and needed by the
583            # project.
584            nodes = resources.get('node', [])
585            for nt in [ h for n in nodes\
586                    if n.has_key('hardware')\
587                        for h in n['hardware'] ] :
588                if self.allocation_level >= self.confirm_keys:
589                    access_ok = self.confirm_access(pname, nt)
590                    if not access_ok:
591                        if self.allocation_level >= self.dynamic_keys:
592                            if self.add_access(pname, nt):
593                                self.state['types'].add((pname, nt))
594                                added_types.append((pname, nt))
595                            else:
596                                raise service_error(service_error.internal,
597                                        "Failed to add access for %s to %s"\
598                                                % (pname, nt))
599                        else:
600                            raise service_error(service_error.internal,
601                                    "Failed to confirm access for %s to %s"\
602                                            % (pname, nt))
603                else:
604                    self.log.warning("[static_project] no checking of " + \
605                        "node access")
606        except service_error, e:
607            # Do our best to clean up partial allocation and reraise the
608            # error.  Do our best to make sure that both allocation state and
609            # testbed state is restored.
610            for u, k in added_keys:
611                self.state['keys'].discard((u, k))
612                try:
613                    self.remove_key(u, k)
614                except service_error:
615                    pass
616            for p, t in added_types:
617                self.state['types'].discard((p, t))
618                try:
619                    self.remove_access(p, t)
620                except service_error:
621                    pass
622            self.state_lock.release()
623            raise e
624        # All is well, save state and release the lock
625        self.write_state()
626        self.state_lock.release()
627        # return { 'project': req['StaticProjectRequestBody']['project']}
628        return req['StaticProjectRequestBody']
629
630    def release_project(self, req, fedid=None):
631        """
632        Remove user keys from users and delete dynamic projects.
633
634        Only keys not in the set of fixed keys are deleted. and there are
635        similar protections for projects.
636        """
637        # Internal calls do not have a fedid parameter (i.e., local calls on
638        # behalf of already vetted fedids)
639        if fedid and not self.auth.check_attribute(fedid, "allocate"):
640            self.log.debug("[allocate] Access denied (%s)" % fedid)
641            raise service_error(service_error.access, "Access Denied")
642
643        pname = None
644        users = []
645        nodes = [ ]
646
647        print req
648
649        try:
650            if req['ReleaseProjectRequestBody']['project'].has_key('name'):
651                pname = req['ReleaseProjectRequestBody']['project']\
652                        ['name']['localname']
653            if req['ReleaseProjectRequestBody']['project'].has_key('user'):
654                users = req['ReleaseProjectRequestBody']['project']['user']
655            if req['ReleaseProjectRequestBody'].has_key('resources'):
656                nodes = req['ReleaseProjectRequestBody']\
657                        ['resources'].get('node', [])
658        except KeyError:
659            raise service_error(service_error.req, "Badly formed request")
660
661        if nodes and not pname:
662            raise service_error(service_error.req, 
663                    "Badly formed request (nodes without project)")
664
665        self.state_lock.acquire()
666        try:
667            for nt in [ h for n in nodes if n.has_key('hardware')\
668                    for h in n['hardware'] ] :
669                if (pname, nt ) in self.state['types']:
670                    self.remove_access(pname, nt)
671                    self.state['types'].discard((pname, nt))
672
673            for u in users:
674                try:
675                    name = u['userID']['localname']
676                except KeyError:
677                    raise service_error(service_error.req, "Badly formed user")
678                if name in self.state['users']:
679                    # If we created this user, discard the user, keys and all
680                    self.remove_user(name)
681                    self.state['users'].discard(name)
682                else:
683                    # If not, just strip any keys we added
684                    for sk in [ k['sshPubkey'] for k in u.get('access', []) \
685                            if k.has_key('sshPubkey')]:
686                        if (name, sk) in self.state['keys']:
687                            self.remove_key(name, sk)
688                            self.state['keys'].discard((name, sk))
689            if pname in self.state['projects']:
690                self.remove_project(pname)
691                self.state['projects'].discard(pname)
692
693        except service_error, e:
694            self.write_state()
695            self.state_lock.release()
696            raise e
697        self.write_state()
698        self.state_lock.release()
699
700        return { 'project': req['ReleaseProjectRequestBody']['project']}
701
702class allocate_project_remote:
703    """
704    Allocate projects on a remote machine using the internal SOAP interface
705    """
706    class proxy(service_caller):
707        """
708        This class is a proxy functor (callable) that has the same signature as
709        a function called by soap_handler or xmlrpc_handler, but that used the
710        service_caller class to call the function remotely.
711        """
712
713        def __init__(self, url, cert_file, cert_pwd, trusted_certs, auth, 
714                method):
715            service_caller.__init__(self, method)
716            self.url = url
717            self.cert_file = cert_file
718            self.cert_pwd = cert_pwd
719            self.trusted_certs = trusted_certs
720            self.request_body__name = "%sRequestBody" % method
721            self.resp_name = "%sResponseBody" % method
722            self.auth = auth
723            # Calling the proxy object directly invokes the proxy_call method,
724            # not the service_call method.
725            self.__call__ = self.proxy_call
726           
727
728        # Define the proxy, NB, the parameters to make_proxy are visible to the
729        # definition of proxy.
730        def proxy_call(self, req, fid=None):
731            """
732            Send req on to a remote project instantiator.
733
734            Req is just the message to be sent.  This function re-wraps it.
735            It also rethrows any faults.
736            """
737
738            if req.has_key(self.request_body_name):
739                req = req[self.request_body_name]
740            else:
741                raise service_error(service_error.req, "Bad formated request");
742
743            try:
744                r = self.call_service(self.url, req, self.cert_file,
745                        self.cert_pwd, self.trusted_certs)
746            except service_error, e:
747                if e.code == service_error.connect:
748                    raise service_error(service_error.internal, 
749                            "Cannot connect to internal service: (%d) %s" % \
750                                    (e.code, e.desc))
751                else: raise
752            if r.has_key(self.resp_name):
753                return r[self.resp_name]
754            else:
755                raise service_error(service_error.protocol, 
756                        "Bad proxy response")
757
758    # back to defining the allocate_project_remote class
759    def __init__(self, config, auth=None):
760        """
761        Initializer.  Parses a configuration if one is given.
762        """
763
764        self.debug = config.get("allocate", "debug", False)
765        self.url = config.get("allocate", "uri", "")
766
767        self.cert_file = config.get("allocate", "cert_file", None)
768        self.cert_pwd = config.get("allocate", "cert_pwd", None)
769        self.trusted_certs = config.get("allocate", "trusted_certs", None)
770
771        # Certs are promoted from the generic to the specific, so without a if
772        # no dynamic project certificates, then proxy certs are used, and if
773        # none of those the main certs.
774
775        if config.has_option("globals", "proxy_cert_file"):
776            if not self.cert_file:
777                self.cert_file = config.get("globals", "proxy_cert_file")
778                if config.has_option("globals", "proxy_cert_pwd"):
779                    self.cert_pwd = config.get("globals", "proxy_cert_pwd")
780
781        if config.has_option("globals", "proxy_trusted_certs") and \
782                not self.trusted_certs:
783                self.trusted_certs = \
784                        config.get("globals", "proxy_trusted_certs")
785
786        if config.has_option("globals", "cert_file"):
787            has_pwd = config.has_option("globals", "cert_pwd")
788            if not self.cert_file:
789                self.cert_file = config.get("globals", "cert_file")
790                if has_pwd: 
791                    self.cert_pwd = config.get("globals", "cert_pwd")
792
793        if config.get("globals", "trusted_certs") and not self.trusted_certs:
794                self.trusted_certs = \
795                        config.get("globals", "trusted_certs")
796
797        self.soap_services = { }
798        self.xmlrpc_services = { }
799        self.log = logging.getLogger("fedd.allocate.remote")
800        set_log_level(config, "allocate", self.log)
801
802        if auth:
803            self.auth = auth
804        else:
805            auth = authorizer()
806            log.warn("[allocate] No authorizer passed in, using local one")
807
808        # The specializations of the proxy functions
809        self.dynamic_project = self.proxy(self.url, self.cert_file, 
810                self.cert_pwd, self.trusted_certs, self.auth, 
811                "AllocateProject")
812        self.static_project = self.proxy(self.url, self.cert_file, 
813                self.cert_pwd, self.trusted_certs, self.auth, 
814                "StaticProject")
815        self.release_project = self.proxy(self.url, self.cert_file,
816                self.cert_pwd, self.trusted_certs, self.auth, 
817                "ReleaseProject")
818
Note: See TracBrowser for help on using the repository browser.