source: fedd/federation/allocate_project.py @ ee7f7e4

axis_examplecompt_changesinfo-ops
Last change on this file since ee7f7e4 was d3c8759, checked in by Ted Faber <faber@…>, 15 years ago

Wholesale change of IOError to EnvironmentError? for file operations. Lots of
uncaught EnvironmentErrors? were causing spurious error conditions, e.g. on disk
full.

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