source: fedd/federation/emulab_access.py @ d87778f

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

Some bugs that weren't shaken out. Wrong peer in the config file, active configs not marked, and startcmds not munged.

  • Property mode set to 100644
File size: 50.0 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4import stat # for chmod constants
5import re
6import string
7import copy
8import pickle
9import logging
10import subprocess
11
12from threading import *
13
14from util import *
15from allocate_project import allocate_project_local, allocate_project_remote
16from access_project import access_project
17from fedid import fedid, generate_fedid
18from authorizer import authorizer
19from service_error import service_error
20from remote_service import xmlrpc_handler, soap_handler, service_caller
21
22import httplib
23import tempfile
24from urlparse import urlparse
25
26import topdl
27import list_log
28import proxy_emulab_segment
29import local_emulab_segment
30
31
32# Make log messages disappear if noone configures a fedd logger
33class nullHandler(logging.Handler):
34    def emit(self, record): pass
35
36fl = logging.getLogger("fedd.access")
37fl.addHandler(nullHandler())
38
39class access:
40    """
41    The implementation of access control based on mapping users to projects.
42
43    Users can be mapped to existing projects or have projects created
44    dynamically.  This implements both direct requests and proxies.
45    """
46
47    class parse_error(RuntimeError): pass
48
49
50    proxy_RequestAccess= service_caller('RequestAccess')
51    proxy_ReleaseAccess= service_caller('ReleaseAccess')
52
53    def __init__(self, config=None, auth=None):
54        """
55        Initializer.  Pulls parameters out of the ConfigParser's access section.
56        """
57
58        # Make sure that the configuration is in place
59        if not config: 
60            raise RunTimeError("No config to fedd.access")
61
62        self.project_priority = config.getboolean("access", "project_priority")
63        self.allow_proxy = config.getboolean("access", "allow_proxy")
64
65        self.boss = config.get("access", "boss")
66        self.ops = config.get("access", "ops")
67        self.domain = config.get("access", "domain")
68        self.fileserver = config.get("access", "fileserver")
69        self.eventserver = config.get("access", "eventserver")
70        self.certdir = config.get("access","certdir")
71        self.userconfdir = config.get("access","userconfdir")
72        self.userconfcmd = config.get("access","userconfcmd")
73        self.userconfurl = config.get("access","userconfurl")
74        self.ssh_privkey_file = config.get("access","ssh_privkey_file")
75        self.ssh_pubkey_file = config.get("access","ssh_pubkey_file")
76        self.create_debug = config.getboolean("access", "create_debug")
77        self.cleanup = not config.getboolean("access", "leave_tmpfiles")
78        self.access_type = config.get("access", "type")
79
80        self.access_type = self.access_type.lower()
81        if self.access_type == 'remote_emulab':
82            self.start_segment = proxy_emulab_segment.start_segment
83            self.stop_segment = proxy_emulab_segment.stop_segment
84        elif self.access_type == 'local_emulab':
85            self.start_segment = local_emulab_segment.start_segment
86            self.stop_segment = local_emulab_segment.stop_segment
87        else:
88            self.start_segment = None
89            self.stop_segment = None
90
91        self.attrs = { }
92        self.access = { }
93        self.restricted = [ ]
94        self.projects = { }
95        self.keys = { }
96        self.types = { }
97        self.allocation = { }
98        self.state = { 
99            'projects': self.projects,
100            'allocation' : self.allocation,
101            'keys' : self.keys,
102            'types': self.types
103        }
104        self.log = logging.getLogger("fedd.access")
105        set_log_level(config, "access", self.log)
106        self.state_lock = Lock()
107        # XXX: Configurable
108        self.exports = set(('SMB', 'seer', 'tmcd', 'userconfig'))
109        self.imports = set(('SMB', 'seer', 'tmcd'))
110
111        if auth: self.auth = auth
112        else:
113            self.log.error(\
114                    "[access]: No authorizer initialized, creating local one.")
115            auth = authorizer()
116
117        tb = config.get('access', 'testbed')
118        if tb: self.testbed = [ t.strip() for t in tb.split(',') ]
119        else: self.testbed = [ ]
120
121        if config.has_option("access", "accessdb"):
122            self.read_access(config.get("access", "accessdb"))
123
124        self.state_filename = config.get("access", "access_state")
125        self.read_state()
126
127        # Keep cert_file and cert_pwd coming from the same place
128        self.cert_file = config.get("access", "cert_file")
129        if self.cert_file:
130            self.sert_pwd = config.get("access", "cert_pw")
131        else:
132            self.cert_file = config.get("globals", "cert_file")
133            self.sert_pwd = config.get("globals", "cert_pw")
134
135        self.trusted_certs = config.get("access", "trusted_certs") or \
136                config.get("globals", "trusted_certs")
137
138        self.soap_services = {\
139            'RequestAccess': soap_handler("RequestAccess", self.RequestAccess),
140            'ReleaseAccess': soap_handler("ReleaseAccess", self.ReleaseAccess),
141            'StartSegment': soap_handler("StartSegment", self.StartSegment),
142            'TerminateSegment': soap_handler("TerminateSegment", self.TerminateSegment),
143            }
144        self.xmlrpc_services =  {\
145            'RequestAccess': xmlrpc_handler('RequestAccess',
146                self.RequestAccess),
147            'ReleaseAccess': xmlrpc_handler('ReleaseAccess',
148                self.ReleaseAccess),
149            'StartSegment': xmlrpc_handler("StartSegment", self.StartSegment),
150            'TerminateSegment': xmlrpc_handler('TerminateSegment',
151                self.TerminateSegment),
152            }
153
154
155        if not config.has_option("allocate", "uri"):
156            self.allocate_project = \
157                allocate_project_local(config, auth)
158        else:
159            self.allocate_project = \
160                allocate_project_remote(config, auth)
161
162        # If the project allocator exports services, put them in this object's
163        # maps so that classes that instantiate this can call the services.
164        self.soap_services.update(self.allocate_project.soap_services)
165        self.xmlrpc_services.update(self.allocate_project.xmlrpc_services)
166
167
168    def read_access(self, config):
169        """
170        Read a configuration file and set internal parameters.
171
172        The format is more complex than one might hope.  The basic format is
173        attribute value pairs separated by colons(:) on a signle line.  The
174        attributes in bool_attrs, emulab_attrs and id_attrs can all be set
175        directly using the name: value syntax.  E.g.
176        boss: hostname
177        sets self.boss to hostname.  In addition, there are access lines of the
178        form (tb, proj, user) -> (aproj, auser) that map the first tuple of
179        names to the second for access purposes.  Names in the key (left side)
180        can include "<NONE> or <ANY>" to act as wildcards or to require the
181        fields to be empty.  Similarly aproj or auser can be <SAME> or
182        <DYNAMIC> indicating that either the matching key is to be used or a
183        dynamic user or project will be created.  These names can also be
184        federated IDs (fedid's) if prefixed with fedid:.  Finally, the aproj
185        can be followed with a colon-separated list of node types to which that
186        project has access (or will have access if dynamic).
187        Testbed attributes outside the forms above can be given using the
188        format attribute: name value: value.  The name is a single word and the
189        value continues to the end of the line.  Empty lines and lines startin
190        with a # are ignored.
191
192        Parsing errors result in a self.parse_error exception being raised.
193        """
194        lineno=0
195        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
196        fedid_expr = "fedid:[" + string.hexdigits + "]+"
197        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
198        access_proj = "(<DYNAMIC>(?::" + name_expr +")*|"+ \
199                "<SAME>" + "(?::" + name_expr + ")*|" + \
200                fedid_expr + "(?::" + name_expr + ")*|" + \
201                name_expr + "(?::" + name_expr + ")*)"
202        access_name = "(<DYNAMIC>|<SAME>|" + fedid_expr + "|"+ name_expr + ")"
203
204        restricted_re = re.compile("restricted:\s*(.*)", re.IGNORECASE)
205        attr_re = re.compile('attribute:\s*([\._\-a-z0-9]+)\s+value:\s*(.*)',
206                re.IGNORECASE)
207        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
208                key_name+'\s*\)\s*->\s*\('+access_proj + '\s*,\s*' + 
209                access_name + '\s*,\s*' + access_name + '\s*\)', re.IGNORECASE)
210
211        def parse_name(n):
212            if n.startswith('fedid:'): return fedid(hexstr=n[len('fedid:'):])
213            else: return n
214       
215        def auth_name(n):
216            if isinstance(n, basestring):
217                if n =='<any>' or n =='<none>': return None
218                else: return unicode(n)
219            else:
220                return n
221
222        f = open(config, "r");
223        for line in f:
224            lineno += 1
225            line = line.strip();
226            if len(line) == 0 or line.startswith('#'):
227                continue
228
229            # Extended (attribute: x value: y) attribute line
230            m = attr_re.match(line)
231            if m != None:
232                attr, val = m.group(1,2)
233                self.attrs[attr] = val
234                continue
235
236            # Restricted entry
237            m = restricted_re.match(line)
238            if m != None:
239                val = m.group(1)
240                self.restricted.append(val)
241                continue
242
243            # Access line (t, p, u) -> (ap, cu, su) line
244            m = access_re.match(line)
245            if m != None:
246                access_key = tuple([ parse_name(x) for x in m.group(1,2,3)])
247                auth_key = tuple([ auth_name(x) for x in access_key])
248                aps = m.group(4).split(":");
249                if aps[0] == 'fedid:':
250                    del aps[0]
251                    aps[0] = fedid(hexstr=aps[0])
252
253                cu = parse_name(m.group(5))
254                su = parse_name(m.group(6))
255
256                access_val = (access_project(aps[0], aps[1:]),
257                        parse_name(m.group(5)), parse_name(m.group(6)))
258
259                self.access[access_key] = access_val
260                self.auth.set_attribute(auth_key, "access")
261                continue
262
263            # Nothing matched to here: unknown line - raise exception
264            f.close()
265            raise self.parse_error("Unknown statement at line %d of %s" % \
266                    (lineno, config))
267        f.close()
268
269    def get_users(self, obj):
270        """
271        Return a list of the IDs of the users in dict
272        """
273        if obj.has_key('user'):
274            return [ unpack_id(u['userID']) \
275                    for u in obj['user'] if u.has_key('userID') ]
276        else:
277            return None
278
279    def write_state(self):
280        if self.state_filename:
281            try:
282                f = open(self.state_filename, 'w')
283                pickle.dump(self.state, f)
284            except IOError, e:
285                self.log.error("Can't write file %s: %s" % \
286                        (self.state_filename, e))
287            except pickle.PicklingError, e:
288                self.log.error("Pickling problem: %s" % e)
289            except TypeError, e:
290                self.log.error("Pickling problem (TypeError): %s" % e)
291
292
293    def read_state(self):
294        """
295        Read a new copy of access state.  Old state is overwritten.
296
297        State format is a simple pickling of the state dictionary.
298        """
299        if self.state_filename:
300            try:
301                f = open(self.state_filename, "r")
302                self.state = pickle.load(f)
303
304                self.allocation = self.state['allocation']
305                self.projects = self.state['projects']
306                self.keys = self.state['keys']
307                self.types = self.state['types']
308
309                self.log.debug("[read_state]: Read state from %s" % \
310                        self.state_filename)
311            except IOError, e:
312                self.log.warning(("[read_state]: No saved state: " +\
313                        "Can't open %s: %s") % (self.state_filename, e))
314            except EOFError, e:
315                self.log.warning(("[read_state]: " +\
316                        "Empty or damaged state file: %s:") % \
317                        self.state_filename)
318            except pickle.UnpicklingError, e:
319                self.log.warning(("[read_state]: No saved state: " + \
320                        "Unpickling failed: %s") % e)
321
322            # Add the ownership attributes to the authorizer.  Note that the
323            # indices of the allocation dict are strings, but the attributes are
324            # fedids, so there is a conversion.
325            for k in self.allocation.keys():
326                for o in self.allocation[k].get('owners', []):
327                    self.auth.set_attribute(o, fedid(hexstr=k))
328                if self.allocation[k].has_key('userconfig'):
329                    sfid = self.allocation[k]['userconfig']
330                    fid = fedid(hexstr=sfid)
331                    self.auth.set_attribute(fid, "/%s" % sfid)
332
333
334    def permute_wildcards(self, a, p):
335        """Return a copy of a with various fields wildcarded.
336
337        The bits of p control the wildcards.  A set bit is a wildcard
338        replacement with the lowest bit being user then project then testbed.
339        """
340        if p & 1: user = ["<any>"]
341        else: user = a[2]
342        if p & 2: proj = "<any>"
343        else: proj = a[1]
344        if p & 4: tb = "<any>"
345        else: tb = a[0]
346
347        return (tb, proj, user)
348
349    def find_access(self, search):
350        """
351        Search the access DB for a match on this tuple.  Return the matching
352        access tuple and the user that matched.
353       
354        NB, if the initial tuple fails to match we start inserting wildcards in
355        an order determined by self.project_priority.  Try the list of users in
356        order (when wildcarded, there's only one user in the list).
357        """
358        if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7)
359        else: perm = (0, 2, 1, 3, 4, 6, 5, 7)
360
361        for p in perm: 
362            s = self.permute_wildcards(search, p)
363            # s[2] is None on an anonymous, unwildcarded request
364            if s[2] != None:
365                for u in s[2]:
366                    if self.access.has_key((s[0], s[1], u)):
367                        return (self.access[(s[0], s[1], u)], u)
368            else:
369                if self.access.has_key(s):
370                    return (self.access[s], None)
371        return None, None
372
373    def lookup_access(self, req, fid):
374        """
375        Determine the allowed access for this request.  Return the access and
376        which fields are dynamic.
377
378        The fedid is needed to construct the request
379        """
380        user_re = re.compile("user:\s(.*)")
381        project_re = re.compile("project:\s(.*)")
382
383        # Search keys
384        tb = None
385        project = None
386        user = None
387        # Return values
388        rp = access_project(None, ())
389        ru = None
390
391        user = [ user_re.findall(x)[0] for x in req.get('credential', []) \
392                if user_re.match(x)]
393        project = [ project_re.findall(x)[0] \
394                for x in req.get('credential', []) \
395                    if project_re.match(x)]
396
397        if len(project) == 1: project = project[0]
398        elif len(project) == 0: project = None
399        else: 
400            raise service_error(service_error.req, 
401                    "More than one project credential")
402
403
404        user_fedids = [ u for u in user if isinstance(u, fedid)]
405        # Determine how the caller is representing itself.  If its fedid shows
406        # up as a project or a singleton user, let that stand.  If neither the
407        # usernames nor the project name is a fedid, the caller is a testbed.
408        if project and isinstance(project, fedid):
409            if project == fid:
410                # The caller is the project (which is already in the tuple
411                # passed in to the authorizer)
412                owners = user_fedids
413                owners.append(project)
414            else:
415                raise service_error(service_error.req,
416                        "Project asserting different fedid")
417        else:
418            if fid not in user_fedids:
419                tb = fid
420                owners = user_fedids
421                owners.append(fid)
422            else:
423                if len(fedids) > 1:
424                    raise service_error(service_error.req,
425                            "User asserting different fedid")
426                else:
427                    # Which is a singleton
428                    owners = user_fedids
429        # Confirm authorization
430
431        for u in user:
432            self.log.debug("[lookup_access] Checking access for %s" % \
433                    ((tb, project, u),))
434            if self.auth.check_attribute((tb, project, u), 'access'):
435                self.log.debug("[lookup_access] Access granted")
436                break
437            else:
438                self.log.debug("[lookup_access] Access Denied")
439        else:
440            raise service_error(service_error.access, "Access denied")
441
442        # This maps a valid user to the Emulab projects and users to use
443        found, user_match = self.find_access((tb, project, user))
444       
445        if found == None:
446            raise service_error(service_error.access,
447                    "Access denied - cannot map access")
448
449        # resolve <dynamic> and <same> in found
450        dyn_proj = False
451        dyn_create_user = False
452        dyn_service_user = False
453
454        if found[0].name == "<same>":
455            if project != None:
456                rp.name = project
457            else : 
458                raise service_error(\
459                        service_error.server_config,
460                        "Project matched <same> when no project given")
461        elif found[0].name == "<dynamic>":
462            rp.name = None
463            dyn_proj = True
464        else:
465            rp.name = found[0].name
466        rp.node_types = found[0].node_types;
467
468        if found[1] == "<same>":
469            if user_match == "<any>":
470                if user != None: rcu = user[0]
471                else: raise service_error(\
472                        service_error.server_config,
473                        "Matched <same> on anonymous request")
474            else:
475                rcu = user_match
476        elif found[1] == "<dynamic>":
477            rcu = None
478            dyn_create_user = True
479        else:
480            rcu = found[1]
481       
482        if found[2] == "<same>":
483            if user_match == "<any>":
484                if user != None: rsu = user[0]
485                else: raise service_error(\
486                        service_error.server_config,
487                        "Matched <same> on anonymous request")
488            else:
489                rsu = user_match
490        elif found[2] == "<dynamic>":
491            rsu = None
492            dyn_service_user = True
493        else:
494            rsu = found[2]
495
496        return (rp, rcu, rsu), (dyn_create_user, dyn_service_user, dyn_proj),\
497                owners
498
499    def get_handler(self, path, fid):
500        self.log.info("Get handler %s %s" % (path, fid))
501        if self.auth.check_attribute(fid, path) and self.userconfdir:
502            return ("%s/%s" % (self.userconfdir, path), "application/binary")
503        else:
504            return (None, None)
505
506    def export_userconf(self, project):
507        dev_null = None
508        confid, confcert = generate_fedid("test", dir=self.userconfdir, 
509                log=self.log)
510        conffilename = "%s/%s" % (self.userconfdir, str(confid))
511        cf = None
512        try:
513            cf = open(conffilename, "w")
514            os.chmod(conffilename, stat.S_IRUSR | stat.S_IWUSR)
515        except IOError, e:
516            raise service_error(service_error.internal, 
517                    "Cannot create user configuration data")
518
519        try:
520            dev_null = open("/dev/null", "a")
521        except IOError, e:
522            self.log.error("export_userconf: can't open /dev/null: %s" % e)
523
524        cmd = "%s %s" % (self.userconfcmd, project)
525        conf = subprocess.call(cmd.split(" "),
526                stdout=cf, stderr=dev_null, close_fds=True)
527
528        self.auth.set_attribute(confid, "/%s" % str(confid))
529
530        return confid, confcert
531
532
533    def export_services(self, sreq, project, user):
534        exp = [ ]
535        state = { }
536        # XXX: Filthy shortcut here using http: so urlparse will give the right
537        # answers.
538        for s in sreq:
539            sname = s.get('name', '')
540            svis = s.get('visibility', '')
541            if svis == 'export':
542                if sname in self.exports:
543                    outs = s.copy()
544                    if sname == 'SMB':
545                        outs = s.copy()
546                        outs['server'] = "http://fs:139" 
547                        outs['fedAttr'] = [
548                                { 'attribute': 'SMBSHARE', 'value': 'USERS' },
549                                { 'attribute': 'SMBUSER', 'value': user },
550                                { 'attribute': 'SMBPROJ', 'value': project },
551                            ]
552                    elif sname == 'seer':
553                        outs['server'] = "http://control:16606"
554                    elif sname == 'tmcd':
555                        outs['server'] = "http://boss:7777"
556                    elif sname == 'userconfig':
557                        if self.userconfdir and self.userconfcmd \
558                                and self.userconfurl:
559                            cid, cert = self.export_userconf(project)
560                            outs['server'] = "%s/%s" % \
561                                    (self.userconfurl, str(cid))
562                            outs['fedAttr'] = [
563                                    { 'attribute': 'cert', 'value': cert },
564                                ]
565                            state['userconfig'] = unicode(cid)
566                    exp.append(outs)
567        return (exp, state)
568
569    def build_response(self, alloc_id, ap, services):
570        """
571        Create the SOAP response.
572
573        Build the dictionary description of the response and use
574        fedd_utils.pack_soap to create the soap message.  ap is the allocate
575        project message returned from a remote project allocation (even if that
576        allocation was done locally).
577        """
578        # Because alloc_id is already a fedd_services_types.IDType_Holder,
579        # there's no need to repack it
580        msg = { 
581                'allocID': alloc_id,
582                'fedAttr': [
583                    { 'attribute': 'domain', 'value': self.domain } , 
584                    { 'attribute': 'project', 'value': 
585                        ap['project'].get('name', {}).get('localname', "???") },
586                ]
587            }
588        if len(self.attrs) > 0:
589            msg['fedAttr'].extend(
590                [ { 'attribute': x, 'value' : y } \
591                        for x,y in self.attrs.iteritems()])
592
593        if services:
594            msg['service'] = services
595        return msg
596
597    def RequestAccess(self, req, fid):
598        """
599        Handle the access request.  Proxy if not for us.
600
601        Parse out the fields and make the allocations or rejections if for us,
602        otherwise, assuming we're willing to proxy, proxy the request out.
603        """
604
605        def gateway_hardware(h):
606            if h == 'GWTYPE': return self.attrs.get('connectorType', 'GWTYPE')
607            else: return h
608
609        # The dance to get into the request body
610        if req.has_key('RequestAccessRequestBody'):
611            req = req['RequestAccessRequestBody']
612        else:
613            raise service_error(service_error.req, "No request!?")
614
615        if req.has_key('destinationTestbed'):
616            dt = unpack_id(req['destinationTestbed'])
617
618        if dt == None or dt in self.testbed:
619            # Request for this fedd
620            found, dyn, owners = self.lookup_access(req, fid)
621            restricted = None
622            ap = None
623
624            # If this is a request to export a project and the access project
625            # is not the project to export, access denied.
626            if req.has_key('exportProject'):
627                ep = unpack_id(req['exportProject'])
628                if ep != found[0].name:
629                    raise service_error(service_error.access,
630                            "Cannot export %s" % ep)
631
632            # Check for access to restricted nodes
633            if req.has_key('resources') and req['resources'].has_key('node'):
634                resources = req['resources']
635                restricted = [ gateway_hardware(t) for n in resources['node'] \
636                                if n.has_key('hardware') \
637                                    for t in n['hardware'] \
638                                        if gateway_hardware(t) \
639                                            in self.restricted ]
640                inaccessible = [ t for t in restricted \
641                                    if t not in found[0].node_types]
642                if len(inaccessible) > 0:
643                    raise service_error(service_error.access,
644                            "Access denied (nodetypes %s)" % \
645                            str(', ').join(inaccessible))
646
647            # These were passed around before, but now are hidden from users
648            # and configurators alike, beyond a configuration file entry.
649            create_ssh = [ self.ssh_pubkey_file ]
650            service_ssh = [ self.ssh_pubkey_file ]
651
652            if len(create_ssh) > 0 and len(service_ssh) >0: 
653                if dyn[1]: 
654                    # Compose the dynamic project request
655                    # (only dynamic, dynamic currently allowed)
656                    preq = { 'AllocateProjectRequestBody': \
657                                { 'project' : {\
658                                    'user': [ \
659                                    { \
660                                        'access': [ { 'sshPubkey': s } \
661                                            for s in service_ssh ], 
662                                         'role': "serviceAccess",\
663                                    }, \
664                                    { \
665                                        'access': [ { 'sshPubkey': s } \
666                                            for s in create_ssh ], 
667                                         'role': "experimentCreation",\
668                                    }, \
669                                    ], \
670                                    }\
671                                }\
672                            }
673                    if restricted != None and len(restricted) > 0:
674                        preq['AllocateProjectRequestBody']['resources'] = \
675                             {'node': [ { 'hardware' :  [ h ] } \
676                                    for h in restricted ] } 
677                    ap = self.allocate_project.dynamic_project(preq)
678                else:
679                    preq = {'StaticProjectRequestBody' : \
680                            { 'project': \
681                                { 'name' : { 'localname' : found[0].name },\
682                                  'user' : [ \
683                                    {\
684                                        'userID': { 'localname' : found[1] }, \
685                                        'access': [ { 'sshPubkey': s } 
686                                            for s in create_ssh ],
687                                        'role': 'experimentCreation'\
688                                    },\
689                                    {\
690                                        'userID': { 'localname' : found[2] }, \
691                                        'access': [ { 'sshPubkey': s } 
692                                            for s in service_ssh ],
693                                        'role': 'serviceAccess'\
694                                    },\
695                                ]}\
696                            }\
697                    }
698                    if restricted != None and len(restricted) > 0:
699                        preq['StaticProjectRequestBody']['resources'] = \
700                            {'node': [ { 'hardware' :  [ h ] } \
701                                    for h in restricted ] } 
702                    ap = self.allocate_project.static_project(preq)
703            else:
704                raise service_error(service_error.req, 
705                        "SSH access parameters required")
706            # keep track of what's been added
707            allocID, alloc_cert = generate_fedid(subj="alloc", log=self.log)
708            aid = unicode(allocID)
709
710            self.state_lock.acquire()
711            self.allocation[aid] = { }
712            try:
713                pname = ap['project']['name']['localname']
714            except KeyError:
715                pname = None
716
717            if dyn[1]:
718                if not pname:
719                    self.state_lock.release()
720                    raise service_error(service_error.internal,
721                            "Misformed allocation response?")
722                if self.projects.has_key(pname): self.projects[pname] += 1
723                else: self.projects[pname] = 1
724                self.allocation[aid]['project'] = pname
725            else:
726                # sproject is a static project associated with this allocation.
727                self.allocation[aid]['sproject'] = pname
728
729            if ap.has_key('resources'):
730                if not pname:
731                    self.state_lock.release()
732                    raise service_error(service_error.internal,
733                            "Misformed allocation response?")
734                self.allocation[aid]['types'] = set()
735                nodes = ap['resources'].get('node', [])
736                for n in nodes:
737                    for h in n.get('hardware', []):
738                        if self.types.has_key((pname, h)):
739                            self.types[(pname, h)] += 1
740                        else:
741                            self.types[(pname, h)] = 1
742                        self.allocation[aid]['types'].add((pname,h))
743
744
745            self.allocation[aid]['keys'] = [ ]
746
747            try:
748                for u in ap['project']['user']:
749                    uname = u['userID']['localname']
750                    if u['role'] == 'experimentCreation':
751                        self.allocation[aid]['user'] = uname
752                    for k in [ k['sshPubkey'] for k in u['access'] \
753                            if k.has_key('sshPubkey') ]:
754                        kv = "%s:%s" % (uname, k)
755                        if self.keys.has_key(kv): self.keys[kv] += 1
756                        else: self.keys[kv] = 1
757                        self.allocation[aid]['keys'].append((uname, k))
758            except KeyError:
759                self.state_lock.release()
760                raise service_error(service_error.internal,
761                        "Misformed allocation response?")
762
763            self.allocation[aid]['owners'] = owners
764            services, svc_state = self.export_services(req.get('service',[]),
765                    pname, uname)
766            # Store services state in global state
767            for k, v in svc_state.items():
768                self.allocation[aid][k] = v
769            self.write_state()
770            self.state_lock.release()
771            for o in owners:
772                self.auth.set_attribute(o, allocID)
773            try:
774                f = open("%s/%s.pem" % (self.certdir, aid), "w")
775                print >>f, alloc_cert
776                f.close()
777            except IOError, e:
778                raise service_error(service_error.internal, 
779                        "Can't open %s/%s : %s" % (self.certdir, aid, e))
780            resp = self.build_response({ 'fedid': allocID } , ap, services)
781            return resp
782        else:
783            if self.allow_proxy:
784                resp = self.proxy_RequestAccess.call_service(dt, req,
785                            self.cert_file, self.cert_pwd,
786                            self.trusted_certs)
787                if resp.has_key('RequestAccessResponseBody'):
788                    return resp['RequestAccessResponseBody']
789                else:
790                    return None
791            else:
792                raise service_error(service_error.access,
793                        "Access proxying denied")
794
795    def ReleaseAccess(self, req, fid):
796        # The dance to get into the request body
797        if req.has_key('ReleaseAccessRequestBody'):
798            req = req['ReleaseAccessRequestBody']
799        else:
800            raise service_error(service_error.req, "No request!?")
801
802        if req.has_key('destinationTestbed'):
803            dt = unpack_id(req['destinationTestbed'])
804        else:
805            dt = None
806
807        if dt == None or dt in self.testbed:
808            # Local request
809            try:
810                if req['allocID'].has_key('localname'):
811                    auth_attr = aid = req['allocID']['localname']
812                elif req['allocID'].has_key('fedid'):
813                    aid = unicode(req['allocID']['fedid'])
814                    auth_attr = req['allocID']['fedid']
815                else:
816                    raise service_error(service_error.req,
817                            "Only localnames and fedids are understood")
818            except KeyError:
819                raise service_error(service_error.req, "Badly formed request")
820
821            self.log.debug("[access] deallocation requested for %s", aid)
822            if not self.auth.check_attribute(fid, auth_attr):
823                self.log.debug("[access] deallocation denied for %s", aid)
824                raise service_error(service_error.access, "Access Denied")
825
826            # If we know this allocation, reduce the reference counts and
827            # remove the local allocations.  Otherwise report an error.  If
828            # there is an allocation to delete, del_users will be a dictonary
829            # of sets where the key is the user that owns the keys in the set.
830            # We use a set to avoid duplicates.  del_project is just the name
831            # of any dynamic project to delete.  We're somewhat lazy about
832            # deleting authorization attributes.  Having access to something
833            # that doesn't exist isn't harmful.
834            del_users = { }
835            del_project = None
836            del_types = set()
837
838            if self.allocation.has_key(aid):
839                self.log.debug("Found allocation for %s" %aid)
840                self.state_lock.acquire()
841                for k in self.allocation[aid]['keys']:
842                    kk = "%s:%s" % k
843                    self.keys[kk] -= 1
844                    if self.keys[kk] == 0:
845                        if not del_users.has_key(k[0]):
846                            del_users[k[0]] = set()
847                        del_users[k[0]].add(k[1])
848                        del self.keys[kk]
849
850                if self.allocation[aid].has_key('project'):
851                    pname = self.allocation[aid]['project']
852                    self.projects[pname] -= 1
853                    if self.projects[pname] == 0:
854                        del_project = pname
855                        del self.projects[pname]
856
857                if self.allocation[aid].has_key('types'):
858                    for t in self.allocation[aid]['types']:
859                        self.types[t] -= 1
860                        if self.types[t] == 0:
861                            if not del_project: del_project = t[0]
862                            del_types.add(t[1])
863                            del self.types[t]
864
865                del self.allocation[aid]
866                self.write_state()
867                self.state_lock.release()
868                # If we actually have resources to deallocate, prepare the call.
869                if del_project or del_users:
870                    msg = { 'project': { }}
871                    if del_project:
872                        msg['project']['name']= {'localname': del_project}
873                    users = [ ]
874                    for u in del_users.keys():
875                        users.append({ 'userID': { 'localname': u },\
876                            'access' :  \
877                                    [ {'sshPubkey' : s } for s in del_users[u]]\
878                        })
879                    if users: 
880                        msg['project']['user'] = users
881                    if len(del_types) > 0:
882                        msg['resources'] = { 'node': \
883                                [ {'hardware': [ h ] } for h in del_types ]\
884                            }
885                    if self.allocate_project.release_project:
886                        msg = { 'ReleaseProjectRequestBody' : msg}
887                        self.allocate_project.release_project(msg)
888                # And remove the access cert
889                cf = "%s/%s.pem" % (self.certdir, aid)
890                self.log.debug("Removing %s" % cf)
891                os.remove(cf)
892                return { 'allocID': req['allocID'] } 
893            else:
894                raise service_error(service_error.req, "No such allocation")
895
896        else:
897            if self.allow_proxy:
898                resp = self.proxy_ReleaseAccess.call_service(dt, req,
899                            self.cert_file, self.cert_pwd,
900                            self.trusted_certs)
901                if resp.has_key('ReleaseAccessResponseBody'):
902                    return resp['ReleaseAccessResponseBody']
903                else:
904                    return None
905            else:
906                raise service_error(service_error.access,
907                        "Access proxying denied")
908
909    def generate_portal_configs(self, topo, pubkey_base, secretkey_base, 
910            tmpdir, master, lproj, leid, connInfo, services):
911
912        def conninfo_to_dict(key, info):
913            """
914            Make a cpoy of the connection information about key, and flatten it
915            into a single dict by parsing out any feddAttrs.
916            """
917
918            rv = None
919            for i in info:
920                if i.get('portal', "") == key:
921                    rv = i.copy()
922                    break
923            else:
924                return rv
925
926            if 'fedAttr' in rv:
927                for a in rv['fedAttr']:
928                    attr = a.get('attribute', "")
929                    val = a.get('value', "")
930                    if attr and attr not in rv:
931                        rv[attr] = val
932                del rv['fedAttr']
933            return rv
934
935        # XXX: un hardcode this
936        def client_null(f, s): pass
937
938        def client_smb(f, s):
939            smbshare = None
940            smbuser = None
941            smbproj = None
942            for a in s.get('fedAttr', []):
943                if a.get('attribute', '') == 'SMBSHARE':
944                    smbshare = a.get('value', None)
945                elif a.get('attribute', '') == 'SMBUSER':
946                    smbuser = a.get('value', None)
947                elif a.get('attribute', '') == 'SMBPROJ':
948                    smbproj = a.get('value', None)
949
950            if all((smbshare, smbuser, smbproj)):
951                print >>f, "SMBshare: %s" % smbshare
952                print >>f, "ProjectUser: %s" % smbuser
953                print >>f, "ProjectName: %s" % smbproj
954
955        client_service_out = {
956                'SMB': client_smb,
957                'tmcd': client_null,
958                'seer': client_null,
959                'userconfig': client_null,
960            }
961        # XXX: end un hardcode this
962
963
964        seer_out = False
965        client_out = False
966        for e in [ e for e in topo.elements \
967                if isinstance(e, topdl.Computer) and e.get_attribute('portal')]:
968            myname = e.name[0]
969            type = e.get_attribute('portal_type')
970
971            info = conninfo_to_dict(myname, connInfo)
972
973            if not info:
974                raise service_error(service_error.req,
975                        "No connectivity info for %s" % myname)
976
977            peer = info.get('peer', "")
978            ldomain = self.domain;
979
980            mexp = info.get('masterexperiment',"")
981            mproj, meid = mexp.split("/", 1)
982            mdomain = info.get('masterdomain',"")
983            muser = info.get('masteruser','root')
984            smbshare = info.get('smbshare', 'USERS')
985
986            active = info.get('active', 'False')
987
988            cfn = "%s/%s.gw.conf" % (tmpdir, myname.lower())
989            tunnelconfig = self.attrs.has_key('TunnelCfg')
990            try:
991                f = open(cfn, "w")
992                if active == 'True':
993                    print >>f, "active: True"
994                    if type in ('control', 'both'):
995                        for s in [s for s in services \
996                                if s.get('name', "") in self.imports]:
997                            p = urlparse(s.get('server', 'http://localhost'))
998                            print >>f, 'port: remote:%s:%s:%s' % \
999                                    (p.port, p.hostname, p.port)
1000
1001                if tunnelconfig:
1002                    print >>f, "tunnelip: %s" % tunnelconfig
1003                # XXX: send this an fedattr
1004                #print >>f, "seercontrol: control.%s.%s%s" % \
1005                        #(meid.lower(), mproj.lower(), mdomain)
1006                print >>f, "peer: %s" % peer.lower()
1007                print >>f, "ssh_pubkey: /proj/%s/exp/%s/tmp/%s" % \
1008                        (lproj, leid, pubkey_base)
1009                print >>f, "ssh_privkey: /proj/%s/exp/%s/tmp/%s" % \
1010                        (lproj, leid, secretkey_base)
1011                f.close()
1012            except IOError, e:
1013                raise service_error(service_error.internal,
1014                        "Can't write protal config %s: %s" % (cfn, e))
1015           
1016            # XXX: This little seer config file needs to go away.
1017            if not seer_out:
1018                try:
1019                    seerfn = "%s/seer.conf" % tmpdir
1020                    f = open(seerfn, "w")
1021                    if not master:
1022                        print >>f, "ControlNode: control.%s.%s%s" % \
1023                            (meid.lower(), mproj.lower(), mdomain)
1024                    print >>f, "ExperimentID: %s" % mexp
1025                    f.close()
1026                except IOError, e:
1027                    raise service_error(service_error.internal, 
1028                            "Can't write seer.conf: %s" %e)
1029                seer_out = True
1030
1031            if not client_out and type in ('control', 'both'):
1032                try:
1033                    f = open("%s/client.conf" % tmpdir, "w")
1034                    print >>f, "ControlGateway: %s.%s.%s%s" % \
1035                        (myname.lower(), leid.lower(), lproj.lower(),
1036                                ldomain.lower())
1037                    for s in services:
1038                        if s.get('name',"") in self.imports:
1039                            client_service_out[s['name']](f, s)
1040                    # Does seer need this?
1041                    # print >>f, "ExperimentID: %s/%s" % (mproj, meid)
1042                    f.close()
1043                except IOError, e:
1044                    raise service_error(service_error.internal,
1045                            "Cannot write client.conf: %s" %s)
1046                client_out = True
1047
1048
1049    def generate_ns2(self, topo, expfn, softdir, master):
1050        def dragon_commands(e):
1051            s = ""
1052            if isinstance(e, topdl.Computer):
1053                for i in e.interface:
1054                    vlan = i.get_attribute('dragon_vlan')
1055                    if vlan:
1056                        type = i.get_attribute('dragon_type')
1057                        ip = i.get_attribute('ip4_address')
1058                        if type == 'link':
1059                            s = ("tb-allow-external $%s dragonportal " + \
1060                                    "ip %s vlan %s\n") % \
1061                                    (topdl.to_tcl_name(e.name[0]), ip, vlan)
1062                        elif type == 'lan':
1063                            s = ("tb-allow-external $%s dragonportal " + \
1064                                    "ip %s vlan %s usurp %s\n") % \
1065                                    (topdl.to_tcl_name(e.name[0]), ip, vlan,
1066                                        i.substrate[0])
1067                        else:
1068                            raise service_error(service_error_internal,
1069                                    "Unknown DRAGON type %s" % type)
1070            return s
1071
1072        def not_dragon(e):
1073            return all([not i.get_attribute('dragon_vlan') \
1074                    for i in e.interface])
1075
1076        t = topo.clone()
1077
1078        # The startcmds for master and slave testbeds
1079        if master: 
1080            gate_cmd = self.attrs.get('MasterConnectorStartCmd', '/bin/true')
1081            node_cmd = self.attrs.get('MasterNodeStartCmd', 'bin/true')
1082        else: 
1083            gate_cmd = self.attrs.get('SlaveConnectorStartCmd', '/bin/true')
1084            node_cmd = self.attrs.get('SlaveNodeStartCmd', 'bin/true')
1085
1086        # Weed out the things we aren't going to instantiate: Segments, portal
1087        # substrates, and portal interfaces.  (The copy in the for loop allows
1088        # us to delete from e.elements in side the for loop).  While we're
1089        # touching all the elements, we also adjust paths from the original
1090        # testbed to local testbed paths and put the federation commands into
1091        # the start commands
1092        for e in [e for e in t.elements]:
1093            if isinstance(e, topdl.Segment):
1094                t.elements.remove(e)
1095            # Fix software paths
1096            for s in getattr(e, 'software', []):
1097                s.location = re.sub("^.*/", softdir, s.location)
1098            if isinstance(e, topdl.Computer):
1099                if e.get_attribute('portal') and gate_cmd:
1100                    # Portals never have a user-specified start command
1101                    e.set_attribute('startup', gate_cmd)
1102                elif node_cmd:
1103                    if e.get_attribute('startup'):
1104                        e.set_attribute('startup', "%s \\$USER '%s'" % \
1105                                (node_cmd, e.get_attribute('startup')))
1106                    else:
1107                        e.set_attribute('startup', node_cmd)
1108
1109                # Remove portal interfaces that do not connect to DRAGON
1110                e.interface = [i for i in e.interface \
1111                        if not i.get_attribute('portal') or \
1112                            i.get_attribute('dragon_vlan')]
1113
1114        t.substrates = [ s.clone() for s in t.substrates ]
1115        t.incorporate_elements()
1116
1117        # Customize the ns2 output for local portal commands and images
1118        filters = []
1119
1120        # NB: these are extra commands issued for the node, not the startcmds
1121        if master: cmd = self.attrs.get('MasterConnectorCmd', '')
1122        else: cmd = self.attrs.get('SlaveConnectorCmd', '')
1123
1124        if self.attrs.has_key('dragon'):
1125            add_filter = not_dragon
1126            filters.append(dragon_commands)
1127        else:
1128            add_filter = None
1129
1130        if cmd:
1131            filters.append(topdl.generate_portal_command_filter(cmd,
1132                add_filter=add_filter))
1133
1134        if self.attrs.has_key('connectorImage'):
1135            filters.append(topdl.generate_portal_image_filter(
1136                self.attrs.get('connectorImage')))
1137
1138        if self.attrs.has_key('connectorType'):
1139            filters.append(topdl.generate_portal_hardware_filter(
1140                self.attrs.get('connectorType')))
1141
1142        # Convert to ns and write it out
1143        expfile = topdl.topology_to_ns2(t, filters)
1144        try:
1145            f = open(expfn, "w")
1146            print >>f, expfile
1147            f.close()
1148        except IOError:
1149            raise service_error(service_error.internal,
1150                    "Cannot write experiment file %s: %s" % (expfn,e))
1151
1152    def StartSegment(self, req, fid):
1153        def get_url(url, cf, destdir, fn=None):
1154            po = urlparse(url)
1155            if not fn:
1156                fn = po.path.rpartition('/')[2]
1157            try:
1158                conn = httplib.HTTPSConnection(po.hostname, port=po.port, 
1159                        cert_file=cf, key_file=cf)
1160                conn.putrequest('GET', po.path)
1161                conn.endheaders()
1162                response = conn.getresponse()
1163
1164                lf = open("%s/%s" % (destdir, fn), "w")
1165                buf = response.read(4096)
1166                while buf:
1167                    lf.write(buf)
1168                    buf = response.read(4096)
1169                lf.close()
1170            except IOError, e:
1171                print e
1172                raise service_error(service_error.internal,
1173                        "Error writing tempfile: %s" %e)
1174            except httplib.HTTPException, e:
1175                print e
1176                raise service_error(service_error.internal, 
1177                        "Error retrieving data: %s" % e)
1178
1179        configs = set(('hosts', 'ssh_pubkey', 'ssh_secretkey'))
1180
1181        err = None  # Any service_error generated after tmpdir is created
1182        rv = None   # Return value from segment creation
1183
1184        try:
1185            req = req['StartSegmentRequestBody']
1186        except KeyError:
1187            raise service_error(server_error.req, "Badly formed request")
1188
1189        connInfo = req.get('connection', [])
1190        services = req.get('service', [])
1191        auth_attr = req['allocID']['fedid']
1192        aid = "%s" % auth_attr
1193        attrs = req.get('fedAttr', [])
1194        if not self.auth.check_attribute(fid, auth_attr):
1195            raise service_error(service_error.access, "Access denied")
1196
1197        if req.has_key('segmentdescription') and \
1198                req['segmentdescription'].has_key('topdldescription'):
1199            topo = \
1200                topdl.Topology(**req['segmentdescription']['topdldescription'])
1201        else:
1202            raise service_error(service_error.req, 
1203                    "Request missing segmentdescription'")
1204
1205        master = req.get('master', False)
1206
1207        certfile = "%s/%s.pem" % (self.certdir, auth_attr)
1208        try:
1209            tmpdir = tempfile.mkdtemp(prefix="access-")
1210            softdir = "%s/software" % tmpdir
1211        except IOError:
1212            raise service_error(service_error.internal, "Cannot create tmp dir")
1213
1214        # Try block alllows us to clean up temporary files.
1215        try:
1216            sw = set()
1217            for e in topo.elements:
1218                for s in getattr(e, 'software', []):
1219                    sw.add(s.location)
1220            if len(sw) > 0:
1221                os.mkdir(softdir)
1222            for s in sw:
1223                self.log.debug("Retrieving %s" % s)
1224                get_url(s, certfile, softdir)
1225
1226            for a in attrs:
1227                if a['attribute'] in configs:
1228                    get_url(a['value'], certfile, tmpdir)
1229                if a['attribute'] == 'ssh_pubkey':
1230                    pubkey_base = a['value'].rpartition('/')[2]
1231                if a['attribute'] == 'ssh_secretkey':
1232                    secretkey_base = a['value'].rpartition('/')[2]
1233                if a['attribute'] == 'experiment_name':
1234                    ename = a['value']
1235
1236            # If the userconf service was imported, collect the configuration
1237            # data.
1238            for s in services:
1239                if s.get("name", "") == 'userconfig' \
1240                        and s.get('visibility',"") == 'import':
1241
1242                    # Collect ther server and certificate info.
1243                    u = s.get('server', None)
1244                    for a in s.get('fedAttr', []):
1245                        if a.get('attribute',"") == 'cert':
1246                            cert = a.get('value', None)
1247                            break
1248                    else:
1249                        cert = None
1250
1251                    if cert:
1252                        # Make a temporary certificate file for get_url.  The
1253                        # finally clause removes it whether something goes
1254                        # wrong (including an exception from get_url) or not.
1255                        try:
1256                            tfos, tn = tempfile.mkstemp(suffix=".pem")
1257                            tf = os.fdopen(tfos, 'w')
1258                            print >>tf, cert
1259                            tf.close()
1260                            get_url(u, tn, tmpdir, "userconf")
1261                        except IOError, e:
1262                            raise service_error(service.error.internal, 
1263                                    "Cannot create temp file for " + 
1264                                    "userconfig certificates: %s e")
1265                        finally:
1266                            if tn: os.remove(tn)
1267                    else:
1268                        raise service_error(service_error.req,
1269                                "No certificate for retreiving userconfig")
1270                    break
1271
1272
1273
1274            proj = None
1275            user = None
1276            self.state_lock.acquire()
1277            if self.allocation.has_key(aid):
1278                proj = self.allocation[aid].get('project', None)
1279                if not proj: 
1280                    proj = self.allocation[aid].get('sproject', None)
1281                user = self.allocation[aid].get('user', None)
1282                self.allocation[aid]['experiment'] = ename
1283                self.allocation[aid]['log'] = [ ]
1284                # Create a logger that logs to the experiment's state object as
1285                # well as to the main log file.
1286                alloc_log = logging.getLogger('fedd.access.%s' % ename)
1287                h = logging.StreamHandler(
1288                        list_log.list_log(self.allocation[aid]['log']))
1289                # XXX: there should be a global one of these rather than
1290                # repeating the code.
1291                h.setFormatter(logging.Formatter(
1292                    "%(asctime)s %(name)s %(message)s",
1293                            '%d %b %y %H:%M:%S'))
1294                alloc_log.addHandler(h)
1295                self.write_state()
1296            self.state_lock.release()
1297
1298            if not proj:
1299                raise service_error(service_error.internal, 
1300                        "Can't find project for %s" %aid)
1301
1302            if not user:
1303                raise service_error(service_error.internal, 
1304                        "Can't find creation user for %s" %aid)
1305
1306            expfile = "%s/experiment.tcl" % tmpdir
1307
1308            self.generate_portal_configs(topo, pubkey_base, 
1309                    secretkey_base, tmpdir, master, proj, ename, connInfo, 
1310                    services)
1311            self.generate_ns2(topo, expfile, 
1312                    "/proj/%s/software/%s/" % (proj, ename), master)
1313
1314            starter = self.start_segment(keyfile=self.ssh_privkey_file, 
1315                    debug=self.create_debug, log=alloc_log)
1316            rv = starter(self, ename, proj, user, expfile, tmpdir)
1317        except service_error, e:
1318            err = e
1319        except e:
1320            err = service_error(service_error.internal, str(e))
1321
1322        # Walk up tmpdir, deleting as we go
1323        if self.cleanup:
1324            self.log.debug("[StartSegment]: removing %s" % tmpdir)
1325            for path, dirs, files in os.walk(tmpdir, topdown=False):
1326                for f in files:
1327                    os.remove(os.path.join(path, f))
1328                for d in dirs:
1329                    os.rmdir(os.path.join(path, d))
1330            os.rmdir(tmpdir)
1331        else:
1332            self.log.debug("[StartSegment]: not removing %s" % tmpdir)
1333
1334        if rv:
1335            # Grab the log (this is some anal locking, but better safe than
1336            # sorry)
1337            self.state_lock.acquire()
1338            logv = "".join(self.allocation[aid]['log'])
1339            self.state_lock.release()
1340
1341            return { 'allocID': req['allocID'], 'allocationLog': logv }
1342        elif err:
1343            raise service_error(service_error.federant,
1344                    "Swapin failed: %s" % err)
1345        else:
1346            raise service_error(service_error.federant, "Swapin failed")
1347
1348    def TerminateSegment(self, req, fid):
1349        try:
1350            req = req['TerminateSegmentRequestBody']
1351        except KeyError:
1352            raise service_error(server_error.req, "Badly formed request")
1353
1354        auth_attr = req['allocID']['fedid']
1355        aid = "%s" % auth_attr
1356        attrs = req.get('fedAttr', [])
1357        if not self.auth.check_attribute(fid, auth_attr):
1358            raise service_error(service_error.access, "Access denied")
1359
1360        self.state_lock.acquire()
1361        if self.allocation.has_key(aid):
1362            proj = self.allocation[aid].get('project', None)
1363            if not proj: 
1364                proj = self.allocation[aid].get('sproject', None)
1365            user = self.allocation[aid].get('user', None)
1366            ename = self.allocation[aid].get('experiment', None)
1367        else:
1368            proj = None
1369            user = None
1370            ename = None
1371        self.state_lock.release()
1372
1373        if not proj:
1374            raise service_error(service_error.internal, 
1375                    "Can't find project for %s" % aid)
1376
1377        if not user:
1378            raise service_error(service_error.internal, 
1379                    "Can't find creation user for %s" % aid)
1380        if not ename:
1381            raise service_error(service_error.internal, 
1382                    "Can't find experiment name for %s" % aid)
1383        stopper = self.stop_segment(keyfile=self.ssh_privkey_file,
1384                debug=self.create_debug)
1385        stopper(self, user, proj, ename)
1386        return { 'allocID': req['allocID'] }
Note: See TracBrowser for help on using the repository browser.