source: fedd/federation/dragon_access.py @ ab33158

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

final tweaks to get working

  • Property mode set to 100644
File size: 27.0 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4import re
5import string
6import copy
7import pickle
8import logging
9import time
10
11from threading import *
12from subprocess import Popen, call, PIPE, STDOUT
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 dragon_access.access")
61
62        self.project_priority = config.getboolean("access", "project_priority")
63        self.allow_proxy = False
64        self.certdir = config.get("access","certdir")
65        self.create_debug = config.getboolean("access", "create_debug")
66        self.cli_dir = config.get("access", "cli_dir")
67        self.axis2_home = config.get("access", "axis2_home")
68        self.idc_url = config.get("access", "idc")
69
70        self.attrs = { }
71        self.access = { }
72        # State is a dict of dicts indexed by segment fedid that includes the
73        # owners of the segment as fedids (who can manipulate it, key: owners),
74        # the repo dir/user for the allocation (key: user),  Current allocation
75        # log (key: log), and GRI of the reservation once made (key: gri)
76        self.state = { }
77        self.log = logging.getLogger("fedd.access")
78        set_log_level(config, "access", self.log)
79        self.state_lock = Lock()
80        if not (self.cli_dir and self.axis2_home and self.idc_url):
81            self.log.error("Must specify all of cli_dir, axis2_home, " +\
82                    "idc in the [access] section of the configuration")
83
84        if auth: self.auth = auth
85        else:
86            self.log.error(\
87                    "[access]: No authorizer initialized, creating local one.")
88            auth = authorizer()
89
90        tb = config.get('access', 'testbed')
91        if tb: self.testbed = [ t.strip() for t in tb.split(',') ]
92        else: self.testbed = [ ]
93
94        if config.has_option("access", "accessdb"):
95            self.read_access(config.get("access", "accessdb"))
96
97        self.state_filename = config.get("access", "access_state")
98        self.read_state()
99
100        # Keep cert_file and cert_pwd coming from the same place
101        self.cert_file = config.get("access", "cert_file")
102        if self.cert_file:
103            self.sert_pwd = config.get("access", "cert_pw")
104        else:
105            self.cert_file = config.get("globals", "cert_file")
106            self.sert_pwd = config.get("globals", "cert_pw")
107
108        self.trusted_certs = config.get("access", "trusted_certs") or \
109                config.get("globals", "trusted_certs")
110
111        self.soap_services = {\
112            'RequestAccess': soap_handler("RequestAccess", self.RequestAccess),
113            'ReleaseAccess': soap_handler("ReleaseAccess", self.ReleaseAccess),
114            'StartSegment': soap_handler("StartSegment", self.StartSegment),
115            'TerminateSegment': soap_handler("TerminateSegment", 
116                self.TerminateSegment),
117            }
118        self.xmlrpc_services =  {\
119            'RequestAccess': xmlrpc_handler('RequestAccess',
120                self.RequestAccess),
121            'ReleaseAccess': xmlrpc_handler('ReleaseAccess',
122                self.ReleaseAccess),
123            'StartSegment': xmlrpc_handler("StartSegment", self.StartSegment),
124            'TerminateSegment': xmlrpc_handler('TerminateSegment',
125                self.TerminateSegment),
126            }
127
128    def read_access(self, config):
129        """
130        Read a configuration file and set internal parameters.
131
132        There are access lines of the
133        form (tb, proj, user) -> user that map the first tuple of
134        names to the user for for access purposes.  Names in the key (left side)
135        can include "<NONE> or <ANY>" to act as wildcards or to require the
136        fields to be empty.  Similarly aproj or auser can be <SAME> or
137        <DYNAMIC> indicating that either the matching key is to be used or a
138        dynamic user or project will be created.  These names can also be
139        federated IDs (fedid's) if prefixed with fedid:.  The user is the repo
140        directory that contains the DRAGON user credentials.
141        Testbed attributes outside the forms above can be given using the
142        format attribute: name value: value.  The name is a single word and the
143        value continues to the end of the line.  Empty lines and lines startin
144        with a # are ignored.
145
146        Parsing errors result in a self.parse_error exception being raised.
147        """
148        lineno=0
149        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
150        fedid_expr = "fedid:[" + string.hexdigits + "]+"
151        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
152
153        attr_re = re.compile('attribute:\s*([\._\-a-z0-9]+)\s+value:\s*(.*)',
154                re.IGNORECASE)
155        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
156                key_name+'\s*\)\s*->\s*\(('+name_expr +')\s*\)', re.IGNORECASE)
157
158        def parse_name(n):
159            if n.startswith('fedid:'): return fedid(hexstr=n[len('fedid:'):])
160            else: return n
161       
162        def auth_name(n):
163            if isinstance(n, basestring):
164                if n =='<any>' or n =='<none>': return None
165                else: return unicode(n)
166            else:
167                return n
168
169        f = open(config, "r");
170        for line in f:
171            lineno += 1
172            line = line.strip();
173            if len(line) == 0 or line.startswith('#'):
174                continue
175
176            # Extended (attribute: x value: y) attribute line
177            m = attr_re.match(line)
178            if m != None:
179                attr, val = m.group(1,2)
180                self.attrs[attr] = val
181                continue
182
183            # Access line (t, p, u) -> (a) line
184            # XXX: you are here
185            m = access_re.match(line)
186            if m != None:
187                access_key = tuple([ parse_name(x) for x in m.group(1,2,3)])
188                auth_key = tuple([ auth_name(x) for x in access_key])
189                user_name = auth_name(parse_name(m.group(4)))
190
191                self.access[access_key] = user_name
192                self.auth.set_attribute(auth_key, "access")
193                continue
194
195            # Nothing matched to here: unknown line - raise exception
196            f.close()
197            raise self.parse_error("Unknown statement at line %d of %s" % \
198                    (lineno, config))
199        f.close()
200
201        print self.access
202
203    def get_users(self, obj):
204        """
205        Return a list of the IDs of the users in dict
206        """
207        if obj.has_key('user'):
208            return [ unpack_id(u['userID']) \
209                    for u in obj['user'] if u.has_key('userID') ]
210        else:
211            return None
212
213    def write_state(self):
214        if self.state_filename:
215            try:
216                f = open(self.state_filename, 'w')
217                pickle.dump(self.state, f)
218            except IOError, e:
219                self.log.error("Can't write file %s: %s" % \
220                        (self.state_filename, e))
221            except pickle.PicklingError, e:
222                self.log.error("Pickling problem: %s" % e)
223            except TypeError, e:
224                self.log.error("Pickling problem (TypeError): %s" % e)
225
226
227    def read_state(self):
228        """
229        Read a new copy of access state.  Old state is overwritten.
230
231        State format is a simple pickling of the state dictionary.
232        """
233        if self.state_filename:
234            try:
235                f = open(self.state_filename, "r")
236                self.state = pickle.load(f)
237                self.log.debug("[read_state]: Read state from %s" % \
238                        self.state_filename)
239            except IOError, e:
240                self.log.warning(("[read_state]: No saved state: " +\
241                        "Can't open %s: %s") % (self.state_filename, e))
242            except EOFError, e:
243                self.log.warning(("[read_state]: " +\
244                        "Empty or damaged state file: %s:") % \
245                        self.state_filename)
246            except pickle.UnpicklingError, e:
247                self.log.warning(("[read_state]: No saved state: " + \
248                        "Unpickling failed: %s") % e)
249
250            # Add the ownership attributes to the authorizer.  Note that the
251            # indices of the allocation dict are strings, but the attributes are
252            # fedids, so there is a conversion.
253            for k in self.state.keys():
254                for o in self.state[k].get('owners', []):
255                    self.auth.set_attribute(o, fedid(hexstr=k))
256                self.auth.set_attribute(fedid(hexstr=k),fedid(hexstr=k))
257
258
259    def permute_wildcards(self, a, p):
260        """Return a copy of a with various fields wildcarded.
261
262        The bits of p control the wildcards.  A set bit is a wildcard
263        replacement with the lowest bit being user then project then testbed.
264        """
265        if p & 1: user = ["<any>"]
266        else: user = a[2]
267        if p & 2: proj = "<any>"
268        else: proj = a[1]
269        if p & 4: tb = "<any>"
270        else: tb = a[0]
271
272        return (tb, proj, user)
273
274    def find_access(self, search):
275        """
276        Search the access DB for a match on this tuple.  Return the matching
277        user (repo dir).
278       
279        NB, if the initial tuple fails to match we start inserting wildcards in
280        an order determined by self.project_priority.  Try the list of users in
281        order (when wildcarded, there's only one user in the list).
282        """
283        if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7)
284        else: perm = (0, 2, 1, 3, 4, 6, 5, 7)
285
286        for p in perm: 
287            s = self.permute_wildcards(search, p)
288            # s[2] is None on an anonymous, unwildcarded request
289            if s[2] != None:
290                for u in s[2]:
291                    if self.access.has_key((s[0], s[1], u)):
292                        return self.access[(s[0], s[1], u)]
293            else:
294                if self.access.has_key(s):
295                    return self.access[s]
296        return None
297
298    def lookup_access(self, req, fid):
299        """
300        Determine the allowed access for this request.  Return the access and
301        which fields are dynamic.
302
303        The fedid is needed to construct the request
304        """
305        # Search keys
306        tb = None
307        project = None
308        user = None
309        # Return values
310        rp = access_project(None, ())
311        ru = None
312
313        if req.has_key('project'):
314            p = req['project']
315            if p.has_key('name'):
316                project = unpack_id(p['name'])
317            user = self.get_users(p)
318        else:
319            user = self.get_users(req)
320
321        user_fedids = [ u for u in user if isinstance(u, fedid)]
322        # Determine how the caller is representing itself.  If its fedid shows
323        # up as a project or a singleton user, let that stand.  If neither the
324        # usernames nor the project name is a fedid, the caller is a testbed.
325        if project and isinstance(project, fedid):
326            if project == fid:
327                # The caller is the project (which is already in the tuple
328                # passed in to the authorizer)
329                owners = user_fedids
330                owners.append(project)
331            else:
332                raise service_error(service_error.req,
333                        "Project asserting different fedid")
334        else:
335            if fid not in user_fedids:
336                tb = fid
337                owners = user_fedids
338                owners.append(fid)
339            else:
340                if len(fedids) > 1:
341                    raise service_error(service_error.req,
342                            "User asserting different fedid")
343                else:
344                    # Which is a singleton
345                    owners = user_fedids
346        # Confirm authorization
347
348        for u in user:
349            self.log.debug("[lookup_access] Checking access for %s" % \
350                    ((tb, project, u),))
351            if self.auth.check_attribute((tb, project, u), 'access'):
352                self.log.debug("[lookup_access] Access granted")
353                break
354            else:
355                self.log.debug("[lookup_access] Access Denied")
356        else:
357            raise service_error(service_error.access, "Access denied")
358
359        # This maps a valid user to the Emulab projects and users to use
360        found = self.find_access((tb, project, user))
361       
362        if found == None:
363            raise service_error(service_error.access,
364                    "Access denied - cannot map access")
365        return found, owners
366
367    def RequestAccess(self, req, fid):
368        """
369        Handle the access request.  Proxy if not for us.
370
371        Parse out the fields and make the allocations or rejections if for us,
372        otherwise, assuming we're willing to proxy, proxy the request out.
373        """
374
375        # The dance to get into the request body
376        if req.has_key('RequestAccessRequestBody'):
377            req = req['RequestAccessRequestBody']
378        else:
379            raise service_error(service_error.req, "No request!?")
380
381        if req.has_key('destinationTestbed'):
382            dt = unpack_id(req['destinationTestbed'])
383
384        if dt == None or dt in self.testbed:
385            # Request for this fedd
386            found, owners = self.lookup_access(req, fid)
387            # keep track of what's been added
388            allocID, alloc_cert = generate_fedid(subj="alloc", log=self.log)
389            aid = unicode(allocID)
390
391            self.state_lock.acquire()
392            self.state[aid] = { }
393            self.state[aid]['user'] = found
394            self.state[aid]['owners'] = owners
395            self.write_state()
396            self.state_lock.release()
397            for o in owners:
398                self.auth.set_attribute(o, allocID)
399            self.auth.set_attribute(allocID, allocID)
400
401            try:
402                f = open("%s/%s.pem" % (self.certdir, aid), "w")
403                print >>f, alloc_cert
404                f.close()
405            except IOError, e:
406                raise service_error(service_error.internal, 
407                        "Can't open %s/%s : %s" % (self.certdir, aid, e))
408            print { 'allocID': allocID }
409            return { 'allocID': { 'fedid': allocID } }
410        else:
411            if self.allow_proxy:
412                resp = self.proxy_RequestAccess.call_service(dt, req,
413                            self.cert_file, self.cert_pwd,
414                            self.trusted_certs)
415                if resp.has_key('RequestAccessResponseBody'):
416                    return resp['RequestAccessResponseBody']
417                else:
418                    return None
419            else:
420                raise service_error(service_error.access,
421                        "Access proxying denied")
422
423    def ReleaseAccess(self, req, fid):
424        # The dance to get into the request body
425        if req.has_key('ReleaseAccessRequestBody'):
426            req = req['ReleaseAccessRequestBody']
427        else:
428            raise service_error(service_error.req, "No request!?")
429
430        if req.has_key('destinationTestbed'):
431            dt = unpack_id(req['destinationTestbed'])
432        else:
433            dt = None
434
435        if dt == None or dt in self.testbed:
436            # Local request
437            try:
438                if req['allocID'].has_key('localname'):
439                    auth_attr = aid = req['allocID']['localname']
440                elif req['allocID'].has_key('fedid'):
441                    aid = unicode(req['allocID']['fedid'])
442                    auth_attr = req['allocID']['fedid']
443                else:
444                    raise service_error(service_error.req,
445                            "Only localnames and fedids are understood")
446            except KeyError:
447                raise service_error(service_error.req, "Badly formed request")
448
449            self.log.debug("[access] deallocation requested for %s", aid)
450            if not self.auth.check_attribute(fid, auth_attr):
451                self.log.debug("[access] deallocation denied for %s", aid)
452                raise service_error(service_error.access, "Access Denied")
453
454            self.state_lock.acquire()
455            if self.state.has_key(aid):
456                self.log.debug("Found allocation for %s" %aid)
457                del self.state[aid]
458                self.write_state()
459                self.state_lock.release()
460                # And remove the access cert
461                cf = "%s/%s.pem" % (self.certdir, aid)
462                self.log.debug("Removing %s" % cf)
463                os.remove(cf)
464                return { 'allocID': req['allocID'] } 
465            else:
466                self.state_lock.release()
467                raise service_error(service_error.req, "No such allocation")
468
469        else:
470            if self.allow_proxy:
471                resp = self.proxy_ReleaseAccess.call_service(dt, req,
472                            self.cert_file, self.cert_pwd,
473                            self.trusted_certs)
474                if resp.has_key('ReleaseAccessResponseBody'):
475                    return resp['ReleaseAccessResponseBody']
476                else:
477                    return None
478            else:
479                raise service_error(service_error.access,
480                        "Access proxying denied")
481
482    def extract_parameters(self, top):
483        """
484        DRAGON currently supports a fixed capacity link between two endpoints.
485        Those endpoints may specify a VPN (or list or range) to use.  This
486        extracts the DRAGON endpoints and vpn preferences from the segments (as
487        attributes) and the capacity of the connection as given by the
488        substrate.  The two endpoints VLAN choices are intersected to get set
489        of VLANs that are acceptable (no VLAN requiremnets means any is
490        acceptable).
491        """
492        segments = filter(lambda x: isinstance(x, topdl.Segment), top.elements)
493
494        print segments
495
496        if len(segments) != 2 or len(top.substrates) != 1:
497            raise service_error(service_error.req,
498                    "Requests to DRAGON must have exactlty two segments " +\
499                            "and one substrate")
500
501        ends = [ ]
502        for s in segments:
503            ep = s.get_attribute('dragon_endpoint')
504            if not ep:
505                raise service_error(service_error.req, 
506                        "Missing DRAGON endpoint for %s" % s.id)
507            v = s.get_attribute('vlans')
508            vlans = None
509            vset = set()
510            # the vlans can be a single integer, a comma separated list or a
511            # comma separated lists of dashed ranges.  E.g 100 or 100,300 or
512            # 100,300-305,400
513            if v:
514                if v.count(",") > 0 :
515                    vl = [ x.strip() for x in v.split(",") ]
516                else:
517                    vl = [ v ]
518                for v in vl:
519                    try:
520                        if v.count("-")> 0:
521                            f, t = v.split("-", 1)
522                            for i in range(int(f), int(t)+1):
523                                vset.add(i)
524                        else:
525                            vset.add(int(v))
526                    except ValueError:
527                        raise service_error(service_error.req, 
528                                "VLAN tags must be integers (%s)" %s.name)
529            if len(vset) > 0:
530                if vlans: vlans &= vest
531                else: vlans = vset
532            ends.append(ep)
533
534
535        sub = top.substrates[0]
536        if sub.capacity:
537            cap = int(sub.capacity.rate / 1000.0)
538            if cap < 1: cap = 1
539        else:
540            cap = 100
541
542        return cap, ends[0], ends[1], vlans
543
544
545    def start_segment(self, repo, fr, to, cap, vpns=[], start=None, end=None,
546            log=None):
547        """
548        Do the actual work of creating the dragon connecton.
549        """
550        if not log: log = self.log
551        gri_re = re.compile("GRI:\s*(.*)", re.IGNORECASE)
552        status_re = re.compile("Status:\s*(.*)", re.IGNORECASE)
553        source_re = re.compile("Source\s+Endpoint:\s*(.*)", re.IGNORECASE)
554        dest_re = re.compile("Destination\s+Endpoint:\s*(.*)", re.IGNORECASE)
555        path_re = re.compile("Path:")
556
557        if not vpns:
558            vpns = [ None, None, None, None, None]
559
560        if not start:
561            start = time.time()
562        if not end:
563            end = start + 120 *60
564
565
566        status = None
567        gri = None
568        rv = None
569        vlan_no = None
570        for v in vpns:
571            cmd = ['env', 'AXIS2_HOME=%s' % self.axis2_home, './run.sh', 
572                'createReservation', '-repo',  repo , '-url', self.idc_url,
573                '-l2source', fr, '-l2dest', to, '-bwidth', "%s" % cap,
574                '-vlan', "%s" % v, '-desc', 'fedd created connection',
575                '-pathsetup', 'timer-automatic', '-start', "%d" % int(start),
576                '-end', "%d" % int(end)]
577            log.debug("[start_segment]: %s" % " ".join(cmd))
578            if not self.create_debug: 
579                p = Popen(cmd, cwd=self.cli_dir, 
580                        stdout=PIPE, stderr=STDOUT,
581                        close_fds=True)
582                for line in p.stdout:
583                    m = status_re.match(line)
584                    if m:
585                        status = m.group(1)
586                        continue
587                    m = gri_re.match(line)
588                    if m:
589                        gri = m.group(1)
590                rv = p.wait()
591            else: 
592                rv = 0
593                status = 'ACCEPTED'
594                gri = 'debug_gri'
595
596            # Reservation in progress.  Poll the IDC until we know the outcome
597            cmd = ['env', 'AXIS2_HOME=%s' % self.axis2_home, './run.sh', 
598                'query', '-repo',  repo , '-url', self.idc_url, '-gri', gri]
599
600            while status in ('ACCEPTED', 'INSETUP', 'INCREATE', 'PENDING'):
601                log.debug("[start_segment]: %s" % " ".join(cmd))
602                if not self.create_debug:
603                    p = Popen(cmd, cwd=self.cli_dir, 
604                            stdout=PIPE, stderr=STDOUT,
605                            close_fds=True)
606                    in_path = False
607                    vpn1 = None
608                    vpn2 = None
609                    src = None
610                    dest = None
611                    for line in p.stdout:
612                        if not in_path:
613                            m = status_re.match(line)
614                            if m: 
615                                status = m.group(1)
616                                continue
617                            m = source_re.match(line)
618                            if m:
619                                src = m.group(1)
620                                continue
621                            m = dest_re.match(line)
622                            if m:
623                                dest = m.group(1)
624                                continue
625                            m = path_re.match(line)
626                            if m:
627                                in_path = True
628                                if src and dest:
629                                    vpn1_re = re.compile(
630                                            "\s*%s,\s*\w+\s*,\s*(\d+)" % \
631                                                    src.replace("*", "\*"))
632                                    vpn2_re = re.compile(
633                                            "\s*%s,\s*\w+\s*,\s*(\d+)" % \
634                                                    dest.replace("*", "\*"))
635                                else:
636                                    raise service_error(service_error.internal, 
637                                            "Strange output from query")
638                        else:
639                            m = vpn1_re.match(line)
640                            if m:
641                                vpn1 = m.group(1)
642                                continue
643                            m = vpn2_re.match(line)
644                            if m:
645                                vpn2 = m.group(1)
646                                continue
647                    rv = p.wait()
648                    if vpn1 == vpn2:
649                        if v is not None:
650                            if int(vpn1) == v:
651                                vlan_no = int(v)
652                            else:
653                                raise service_error(service_error.federant, 
654                                        "Unexpected vlan assignment")
655                        else:
656                            vlan_no = int(v)
657                    else:
658                        raise service_error(service_error.internal,
659                                "Different VPNs on DRAGON ends")
660                    log.debug("Status: %s" % status or "none")
661                else:
662                    status = 'ACTIVE'
663                    vlan_no = int(v) or 1
664                if status in ('ACCEPTED', 'INSETUP', 'INCREATE', 'PENDING'):
665                    time.sleep(45)
666            if status in ('ACTIVE', 'FINISHED', 'CANCELLED'):
667                break
668
669        if (rv == 0 and gri and vlan_no and status == 'ACTIVE'):
670            return gri, vlan_no
671        else:
672            raise service_error(service_error.federant, 
673                    "Cannot make reservation")
674
675    def stop_segment(self, repo, gri, log=None):
676        if not log: log = self.log
677        cmd = ['env', 'AXIS2_HOME=%s' % self.axis2_home, './run.sh', 
678            'cancel', '-repo',  repo , '-url', self.idc_url, '-gri', gri]
679
680
681        self.log.debug("[stop_segment]: %s" % " ".join(cmd))
682        if not self.create_debug:
683            try:
684                f = open("/dev/null", "w")
685                call(cmd, cwd=self.cli_dir, stdout=f, stderr=f, close_fds=True)
686                f.close()
687            except IOError, e:
688                raise service_error(service_error.internal, 
689                        "Failed to open /dev/null: %s" % e)
690
691    def StartSegment(self, req, fid):
692        err = None  # Any service_error generated after tmpdir is created
693        rv = None   # Return value from segment creation
694
695        try:
696            req = req['StartSegmentRequestBody']
697        except KeyError:
698            raise service_error(server_error.req, "Badly formed request")
699
700        auth_attr = req['allocID']['fedid']
701        aid = "%s" % auth_attr
702        attrs = req.get('fedAttr', [])
703        if not self.auth.check_attribute(fid, auth_attr):
704            raise service_error(service_error.access, "Access denied")
705
706        if req.has_key('segmentdescription') and \
707                req['segmentdescription'].has_key('topdldescription'):
708            topo = \
709                topdl.Topology(**req['segmentdescription']['topdldescription'])
710        else:
711            raise service_error(service_error.req, 
712                    "Request missing segmentdescription'")
713
714        cap, src, dest, vlans = self.extract_parameters(topo)
715        ename = aid
716
717        for a in attrs:
718            if a['attribute'] == 'experiment_name':
719                ename = a['value']
720
721        repo = None
722        self.state_lock.acquire()
723        if self.state.has_key(aid):
724            repo = self.state[aid]['user']
725            self.state[aid]['log'] = [ ]
726            # Create a logger that logs to the experiment's state object as
727            # well as to the main log file.
728            alloc_log = logging.getLogger('fedd.access.%s' % ename)
729            h = logging.StreamHandler(
730                    list_log.list_log(self.state[aid]['log']))
731            # XXX: there should be a global one of these rather than
732            # repeating the code.
733            h.setFormatter(logging.Formatter(
734                "%(asctime)s %(name)s %(message)s",
735                        '%d %b %y %H:%M:%S'))
736            alloc_log.addHandler(h)
737            self.write_state()
738        self.state_lock.release()
739
740        if not repo:
741            raise service_error(service_error.internal, 
742                    "Can't find creation user for %s" %aid)
743
744        gri, vlan_no = self.start_segment(repo, src, dest, cap, vlans,
745                log=alloc_log)
746
747        if gri:
748            # Grab the log (this is some anal locking, but better safe than
749            # sorry)
750            self.state_lock.acquire()
751            self.state[aid]['gri'] = gri
752            logv = "".join(self.state[aid]['log'])
753            self.write_state()
754            self.state_lock.release()
755
756            return { 
757                    'allocID': req['allocID'],
758                    'allocationLog': logv,
759                    'fedAttr': [
760                        {'attribute': 'vlan', 'value': '%d' % vlan_no }
761                        ]
762                    }
763        elif err:
764            raise service_error(service_error.federant,
765                    "Swapin failed: %s" % err)
766        else:
767            raise service_error(service_error.federant, "Swapin failed")
768
769    def TerminateSegment(self, req, fid):
770        try:
771            req = req['TerminateSegmentRequestBody']
772        except KeyError:
773            raise service_error(server_error.req, "Badly formed request")
774
775        auth_attr = req['allocID']['fedid']
776        aid = "%s" % auth_attr
777
778        self.log.debug("Terminate request for %s" %aid)
779        attrs = req.get('fedAttr', [])
780        if not self.auth.check_attribute(fid, auth_attr):
781            raise service_error(service_error.access, "Access denied")
782
783        self.state_lock.acquire()
784        if self.state.has_key(aid):
785            gri = self.state[aid].get('gri', None)
786            user = self.state[aid].get('user', None)
787        else:
788            gri = None
789            user = None
790        self.state_lock.release()
791        self.log.debug("Stop segment for user: %s gre %s" %(user, gri))
792
793        if not gri:
794            raise service_error(service_error.internal, 
795                    "Can't find gri for %s" % aid)
796
797        if not user:
798            raise service_error(service_error.internal, 
799                    "Can't find creation user for %s" % aid)
800   
801        self.log.debug("Stop segment for GRI: %s" %gri)
802        self.stop_segment(user, gri)
803        return { 'allocID': req['allocID'] }
Note: See TracBrowser for help on using the repository browser.