source: fedd/federation/access.py @ 1553d27

version-3.02
Last change on this file since 1553d27 was 1027cf7, checked in by Ted Faber <faber@…>, 14 years ago

whoops

  • Property mode set to 100644
File size: 23.3 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4import stat # for chmod constants
5import re
6import random
7import string
8import copy
9import pickle
10import logging
11import subprocess
12
13from threading import *
14from M2Crypto.SSL import SSLError
15
16from util import *
17from allocate_project import allocate_project_local, allocate_project_remote
18from access_project import access_project
19from fedid import fedid, generate_fedid
20from authorizer import authorizer
21from service_error import service_error
22from remote_service import xmlrpc_handler, soap_handler, service_caller
23
24import httplib
25import tempfile
26from urlparse import urlparse
27
28import topdl
29import list_log
30import proxy_emulab_segment
31import local_emulab_segment
32
33
34# Make log messages disappear if noone configures a fedd logger
35class nullHandler(logging.Handler):
36    def emit(self, record): pass
37
38fl = logging.getLogger("fedd.access")
39fl.addHandler(nullHandler())
40
41class access_base:
42    """
43    The implementation of access control based on mapping users to projects.
44
45    Users can be mapped to existing projects or have projects created
46    dynamically.  This implements both direct requests and proxies.
47    """
48
49    class parse_error(RuntimeError): pass
50
51    def __init__(self, config=None, auth=None):
52        """
53        Initializer.  Pulls parameters out of the ConfigParser's access section.
54        """
55
56        # Make sure that the configuration is in place
57        if not config: 
58            raise RunTimeError("No config to fedd.access")
59
60        self.project_priority = config.getboolean("access", "project_priority")
61
62        self.certdir = config.get("access","certdir")
63        self.create_debug = config.getboolean("access", "create_debug")
64        self.cleanup = not config.getboolean("access", "leave_tmpfiles")
65        self.access_type = config.get("access", "type")
66        self.log = logging.getLogger("fedd.access")
67        set_log_level(config, "access", self.log)
68        self.state_lock = Lock()
69        self.state = { }
70        # subclasses fill with what and how they export.
71        self.exports = { }
72        # XXX: Configurable
73        self.imports = set(('SMB', 'seer', 'userconfig', 'seer_master',
74            'hide_hosts'))
75
76        if auth: self.auth = auth
77        else:
78            self.log.error(\
79                    "[access]: No authorizer initialized, creating local one.")
80            auth = authorizer()
81
82        self.state_filename = config.get("access", "access_state")
83        self.read_state()
84
85        # Keep cert_file and cert_pwd coming from the same place
86        self.cert_file = config.get("access", "cert_file")
87        if self.cert_file:
88            self.cert_pwd = config.get("access", "cert_pw")
89        else:
90            self.cert_file = config.get("globals", "cert_file")
91            self.sert_pwd = config.get("globals", "cert_pw")
92
93        self.trusted_certs = config.get("access", "trusted_certs") or \
94                config.get("globals", "trusted_certs")
95
96
97    @staticmethod
98    def software_list(v):
99        """
100        From a string containing a sequence of space separated pairs, return a
101        list of tuples with pairs of location and file.
102        """
103        l = [ ]
104        if v:
105            ps = v.split(" ")
106            while len(ps):
107                loc, file = ps[0:2]
108                del ps[0:2]
109                l.append((loc, file))
110        return l
111
112    @staticmethod
113    def add_kit(e, kit):
114        """
115        Add a Software object created from the list of (install, location)
116        tuples passed as kit  to the software attribute of an object e.  We
117        do this enough to break out the code, but it's kind of a hack to
118        avoid changing the old tuple rep.
119        """
120
121        s = [ topdl.Software(install=i, location=l) for i, l in kit]
122
123        if isinstance(e.software, list): e.software.extend(s)
124        else: e.software = s
125
126
127    def read_access(self, config, access_obj=None):
128        """
129        Read an access DB with filename config  of the form:
130            (id, id, id) -> attribute, something
131        where the ids can be fedids, strings, or <any> or <none>, attribute is
132        the attribute to assign , and something is any set of charcters.  The
133        hash self.access is populated with mappings from those triples to the
134        results of access_obj being called on the remainder of the line (if
135        present).  If access_obj is not given, the string itself is entered in
136        the hash.  Additionally, a triple with <any> and <none> mapped to None
137        is entered in self.auth with the attribute given.
138
139        Parsing errors result in a self.parse_error exception being raised.
140        access_obj should throw that as well.
141        """
142        lineno=0
143        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
144        fedid_expr = "fedid:[" + string.hexdigits + "]+"
145        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
146        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
147                key_name+'\s*\)\s*->\s*([^,]+)\s*(.*)', re.IGNORECASE)
148
149        def parse_name(n):
150            if n.startswith('fedid:'): return fedid(hexstr=n[len('fedid:'):])
151            else: return n
152       
153        def auth_name(n):
154            if isinstance(n, basestring):
155                if n =='<any>' or n =='<none>': return None
156                else: return unicode(n)
157            else:
158                return n
159        def strip_comma(s):
160            s = s.strip()
161            if s.startswith(','):
162                s = s[1:].strip()
163            return s
164
165        if access_obj is None:
166            access_obj = lambda(x): "%s" % x
167
168        f = open(config, "r");
169        try:
170            for line in f:
171                lineno += 1
172                line = line.strip();
173                if len(line) == 0 or line.startswith('#'):
174                    continue
175
176                # Access line (t, p, u) -> anything
177                m = access_re.match(line)
178                if m != None:
179                    access_key = tuple([ parse_name(x) \
180                            for x in m.group(1,2,3)])
181                    attribute = m.group(4)
182                    auth_key = tuple([ auth_name(x) for x in access_key])
183                    self.auth.set_attribute(auth_key, attribute)
184                    if len(m.group(5)) > 0:
185                        access_val = access_obj(strip_comma(m.group(5)))
186                        self.access[access_key] = access_val
187                    continue
188
189                # Nothing matched to here: unknown line - raise exception
190                f.close()
191                raise self.parse_error(
192                        "Unknown statement at line %d of %s" % \
193                        (lineno, config))
194        finally:
195            if f: f.close()
196
197    def get_users(self, obj):
198        """
199        Return a list of the IDs of the users in dict
200        """
201        if obj.has_key('user'):
202            return [ unpack_id(u['userID']) \
203                    for u in obj['user'] if u.has_key('userID') ]
204        else:
205            return None
206
207    def write_state(self):
208        if self.state_filename:
209            try:
210                f = open(self.state_filename, 'w')
211                pickle.dump(self.state, f)
212                self.log.debug("Wrote state to %s" % self.state_filename)
213            except EnvironmentError, e:
214                self.log.error("Can't write file %s: %s" % \
215                        (self.state_filename, e))
216            except pickle.PicklingError, e:
217                self.log.error("Pickling problem: %s" % e)
218            except TypeError, e:
219                self.log.error("Pickling problem (TypeError): %s" % e)
220
221
222    def read_state(self):
223        """
224        Read a new copy of access state.  Old state is overwritten.
225
226        State format is a simple pickling of the state dictionary.
227        """
228        if self.state_filename:
229            try:
230                f = open(self.state_filename, "r")
231                self.state = pickle.load(f)
232                self.log.debug("[read_state]: Read state from %s" % \
233                        self.state_filename)
234            except EnvironmentError, e:
235                self.log.warning(("[read_state]: No saved state: " +\
236                        "Can't open %s: %s") % (self.state_filename, e))
237            except EOFError, e:
238                self.log.warning(("[read_state]: " +\
239                        "Empty or damaged state file: %s:") % \
240                        self.state_filename)
241            except pickle.UnpicklingError, e:
242                self.log.warning(("[read_state]: No saved state: " + \
243                        "Unpickling failed: %s") % e)
244
245
246
247    def permute_wildcards(self, a, p):
248        """Return a copy of a with various fields wildcarded.
249
250        The bits of p control the wildcards.  A set bit is a wildcard
251        replacement with the lowest bit being user then project then testbed.
252        """
253        if p & 1: user = ["<any>"]
254        else: user = a[2]
255        if p & 2: proj = "<any>"
256        else: proj = a[1]
257        if p & 4: tb = "<any>"
258        else: tb = a[0]
259
260        return (tb, proj, user)
261
262    def find_access(self, search):
263        """
264        Search the access DB for a match on this tuple.  Return the matching
265        access tuple and the user that matched.
266       
267        NB, if the initial tuple fails to match we start inserting wildcards in
268        an order determined by self.project_priority.  Try the list of users in
269        order (when wildcarded, there's only one user in the list).
270        """
271        if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7)
272        else: perm = (0, 2, 1, 3, 4, 6, 5, 7)
273
274        for p in perm: 
275            s = self.permute_wildcards(search, p)
276            # s[2] is None on an anonymous, unwildcarded request
277            if s[2] != None:
278                for u in s[2]:
279                    if self.access.has_key((s[0], s[1], u)):
280                        return (self.access[(s[0], s[1], u)], u)
281            else:
282                if self.access.has_key(s):
283                    return (self.access[s], None)
284        return None, None
285
286    def lookup_access_base(self, req, fid):
287        """
288        Determine the allowed access for this request.  Return the access and
289        which fields are dynamic.
290
291        The fedid is needed to construct the request
292        """
293        user_re = re.compile("user:\s(.*)")
294        project_re = re.compile("project:\s(.*)")
295
296        # Search keys
297        tb = fid
298        user = [ user_re.findall(x)[0] for x in req.get('credential', []) \
299                if user_re.match(x)]
300        project = [ project_re.findall(x)[0] \
301                for x in req.get('credential', []) \
302                    if project_re.match(x)]
303
304        if len(project) == 1: project = project[0]
305        elif len(project) == 0: project = None
306        else: 
307            raise service_error(service_error.req, 
308                    "More than one project credential")
309
310        # Confirm authorization
311        for u in user:
312            self.log.debug("[lookup_access] Checking access for %s" % \
313                    ((tb, project, u),))
314            if self.auth.check_attribute((tb, project, u), 'access'):
315                self.log.debug("[lookup_access] Access granted")
316                break
317            else:
318                self.log.debug("[lookup_access] Access Denied")
319        else:
320            raise service_error(service_error.access, "Access denied")
321
322        # This maps a valid user to the Emulab projects and users to use
323        found, user_match = self.find_access((tb, project, user))
324
325        return (found, (tb, project, user_match))
326       
327
328    def get_handler(self, path, fid):
329        self.log.info("Get handler %s %s" % (path, fid))
330        if self.auth.check_attribute(fid, path) and self.userconfdir:
331            return ("%s/%s" % (self.userconfdir, path), "application/binary")
332        else:
333            return (None, None)
334
335    def export_userconf(self, project):
336        dev_null = None
337        confid, confcert = generate_fedid("test", dir=self.userconfdir, 
338                log=self.log)
339        conffilename = "%s/%s" % (self.userconfdir, str(confid))
340        cf = None
341        try:
342            cf = open(conffilename, "w")
343            os.chmod(conffilename, stat.S_IRUSR | stat.S_IWUSR)
344        except EnvironmentError, e:
345            raise service_error(service_error.internal, 
346                    "Cannot create user configuration data")
347
348        try:
349            dev_null = open("/dev/null", "a")
350        except EnvironmentError, e:
351            self.log.error("export_userconf: can't open /dev/null: %s" % e)
352
353        cmd = "%s %s" % (self.userconfcmd, project)
354        conf = subprocess.call(cmd.split(" "),
355                stdout=cf, stderr=dev_null, close_fds=True)
356
357        self.auth.set_attribute(confid, "/%s" % str(confid))
358
359        return confid, confcert
360
361    def export_SMB(self, id, state, project, user, attrs):
362        if project and user:
363            return [{ 
364                    'id': id,
365                    'name': 'SMB',
366                    'visibility': 'export',
367                    'server': 'http://fs:139',
368                    'fedAttr': [
369                            { 'attribute': 'SMBSHARE', 'value': 'USERS' },
370                            { 'attribute': 'SMBUSER', 'value': user },
371                            { 'attribute': 'SMBPROJ', 'value': project },
372                        ]
373                    }]
374        else:
375            self.log.warn("Cannot export SMB w/o user and project")
376            return [ ]
377
378    def export_seer(self, id, state, project, user, attrs):
379        return [{ 
380                'id': id,
381                'name': 'seer',
382                'visibility': 'export',
383                'server': 'http://control:16606',
384                }]
385
386    def export_local_seer(self, id, state, project, user, attrs):
387        return [{ 
388                'id': id,
389                'name': 'local_seer_control',
390                'visibility': 'export',
391                'server': 'http://control:16606',
392                }]
393
394    def export_seer_master(self, id, state, project, user, attrs):
395        return [{ 
396                'id': id,
397                'name': 'seer_master',
398                'visibility': 'export',
399                'server': 'http://seer-master:17707',
400                }]
401
402    def export_tmcd(self, id, state, project, user, attrs):
403        return [{ 
404                'id': id,
405                'name': 'seer',
406                'visibility': 'export',
407                'server': 'http://boss:7777',
408                }]
409
410    def export_userconfig(self, id, state, project, user, attrs):
411        if self.userconfdir and self.userconfcmd \
412                and self.userconfurl:
413            cid, cert = self.export_userconf(project)
414            state['userconfig'] = unicode(cid)
415            return [{
416                    'id': id,
417                    'name': 'userconfig',
418                    'visibility': 'export',
419                    'server': "%s/%s" % (self.userconfurl, str(cid)),
420                    'fedAttr': [
421                        { 'attribute': 'cert', 'value': cert },
422                    ]
423                    }]
424        else:
425            return [ ]
426
427    def export_hide_hosts(self, id, state, project, user, attrs):
428        return [{
429                'id': id, 
430                'name': 'hide_hosts',
431                'visibility': 'export',
432                'fedAttr': [ x for x in attrs \
433                        if x.get('attribute', "") == 'hosts'],
434                }]
435
436    def export_project_export(self, id, state, project, user, attrs):
437        rv = [ ]
438        rv.extend(self.export_SMB(id, state, project, user, attrs))
439        rv.extend(self.export_userconfig(id, state, project, user, attrs))
440        return rv
441
442    def export_services(self, sreq, project=None, user=None):
443        exp = [ ]
444        state = { }
445        for s in sreq:
446            sname = s.get('name', '')
447            svis = s.get('visibility', '')
448            sattrs = s.get('fedAttr', [])
449            if svis == 'export':
450                if sname in self.exports:
451                    id = s.get('id', 'no_id')
452                    exp.extend(self.exports[sname](id, state, project, user,
453                            sattrs))
454
455        return (exp, state)
456
457    def build_access_response(self, alloc_id, ap, services):
458        """
459        Create the SOAP response.
460
461        Build the dictionary description of the response and use
462        fedd_utils.pack_soap to create the soap message.  ap is the allocate
463        project message returned from a remote project allocation (even if that
464        allocation was done locally).
465        """
466        # Because alloc_id is already a fedd_services_types.IDType_Holder,
467        # there's no need to repack it
468        msg = { 
469                'allocID': alloc_id,
470                'fedAttr': [
471                    { 'attribute': 'domain', 'value': self.domain } , 
472                    { 'attribute': 'project', 'value': 
473                        ap['project'].get('name', {}).get('localname', "???") },
474                ]
475            }
476
477        if self.dragon_endpoint:
478            msg['fedAttr'].append({'attribute': 'dragon',
479                'value': self.dragon_endpoint})
480        if self.deter_internal:
481            msg['fedAttr'].append({'attribute': 'deter_internal',
482                'value': self.deter_internal})
483        #XXX: ??
484        if self.dragon_vlans:
485            msg['fedAttr'].append({'attribute': 'vlans',
486                'value': self.dragon_vlans})
487
488        if services:
489            msg['service'] = services
490        return msg
491
492    def generate_portal_configs(self, topo, pubkey_base, secretkey_base, 
493            tmpdir, lproj, leid, connInfo, services):
494
495        def conninfo_to_dict(key, info):
496            """
497            Make a cpoy of the connection information about key, and flatten it
498            into a single dict by parsing out any feddAttrs.
499            """
500
501            rv = None
502            for i in info:
503                if key == i.get('portal', "") or \
504                        key in [e.get('element', "") \
505                        for e in i.get('member', [])]:
506                    rv = i.copy()
507                    break
508
509            else:
510                return rv
511
512            if 'fedAttr' in rv:
513                for a in rv['fedAttr']:
514                    attr = a.get('attribute', "")
515                    val = a.get('value', "")
516                    if attr and attr not in rv:
517                        rv[attr] = val
518                del rv['fedAttr']
519            return rv
520
521        # XXX: un hardcode this
522        def client_null(f, s):
523            print >>f, "Service: %s" % s['name']
524
525        def client_seer_master(f, s):
526            print >>f, 'PortalAlias: seer-master'
527
528        def client_smb(f, s):
529            print >>f, "Service: %s" % s['name']
530            smbshare = None
531            smbuser = None
532            smbproj = None
533            for a in s.get('fedAttr', []):
534                if a.get('attribute', '') == 'SMBSHARE':
535                    smbshare = a.get('value', None)
536                elif a.get('attribute', '') == 'SMBUSER':
537                    smbuser = a.get('value', None)
538                elif a.get('attribute', '') == 'SMBPROJ':
539                    smbproj = a.get('value', None)
540
541            if all((smbshare, smbuser, smbproj)):
542                print >>f, "SMBshare: %s" % smbshare
543                print >>f, "ProjectUser: %s" % smbuser
544                print >>f, "ProjectName: %s" % smbproj
545
546        def client_hide_hosts(f, s):
547            for a in s.get('fedAttr', [ ]):
548                if a.get('attribute', "") == 'hosts':
549                    print >>f, "Hide: %s" % a.get('value', "")
550
551        client_service_out = {
552                'SMB': client_smb,
553                'tmcd': client_null,
554                'seer': client_null,
555                'userconfig': client_null,
556                'project_export': client_null,
557                'seer_master': client_seer_master,
558                'hide_hosts': client_hide_hosts,
559            }
560
561        def client_seer_master_export(f, s):
562            print >>f, "AddedNode: seer-master"
563
564        def client_seer_local_export(f, s):
565            print >>f, "AddedNode: control"
566
567        client_export_service_out = {
568                'seer_master': client_seer_master_export,
569                'local_seer_control': client_seer_local_export,
570            }
571
572        def server_port(f, s):
573            p = urlparse(s.get('server', 'http://localhost'))
574            print >>f, 'port: remote:%s:%s:%s' % (p.port, p.hostname, p.port) 
575
576        def server_null(f,s): pass
577
578        def server_seer(f, s):
579            print >>f, 'seer: True'
580
581        server_service_out = {
582                'SMB': server_port,
583                'tmcd': server_port,
584                'userconfig': server_null,
585                'project_export': server_null,
586                'seer': server_seer,
587                'seer_master': server_port,
588                'hide_hosts': server_null,
589            }
590        # XXX: end un hardcode this
591
592
593        seer_out = False
594        client_out = False
595        mproj = None
596        mexp = None
597        control_gw = None
598        testbed = ""
599        # Create configuration files for the portals
600        for e in [ e for e in topo.elements \
601                if isinstance(e, topdl.Computer) and e.get_attribute('portal')]:
602            myname = e.name
603            type = e.get_attribute('portal_type')
604
605            info = conninfo_to_dict(myname, connInfo)
606
607            if not info:
608                raise service_error(service_error.req,
609                        "No connectivity info for %s" % myname)
610
611            peer = info.get('peer', "")
612            ldomain = self.domain
613            ssh_port = info.get('ssh_port', 22)
614
615            # Collect this for the client.conf file
616            if 'masterexperiment' in info:
617                mproj, meid = info['masterexperiment'].split("/", 1)
618
619            if type in ('control', 'both'):
620                testbed = e.get_attribute('testbed')
621                control_gw = myname
622
623            active = info.get('active', 'False')
624
625            cfn = "%s/%s.gw.conf" % (tmpdir, myname.lower())
626            tunnelconfig = self.tunnel_config
627            try:
628                f = open(cfn, "w")
629                if active == 'True':
630                    print >>f, "active: True"
631                    print >>f, "ssh_port: %s" % ssh_port
632                    if type in ('control', 'both'):
633                        for s in [s for s in services \
634                                if s.get('name', "") in self.imports]:
635                            server_service_out[s['name']](f, s)
636
637                if tunnelconfig:
638                    print >>f, "tunnelip: %s" % tunnelconfig
639                print >>f, "peer: %s" % peer.lower()
640                print >>f, "ssh_pubkey: /proj/%s/exp/%s/tmp/%s" % \
641                        (lproj, leid, pubkey_base)
642                print >>f, "ssh_privkey: /proj/%s/exp/%s/tmp/%s" % \
643                        (lproj, leid, secretkey_base)
644                f.close()
645            except EnvironmentError, e:
646                raise service_error(service_error.internal,
647                        "Can't write protal config %s: %s" % (cfn, e))
648
649        # Done with portals, write the client config file.
650        try:
651            f = open("%s/client.conf" % tmpdir, "w")
652            if control_gw:
653                print >>f, "ControlGateway: %s.%s.%s%s" % \
654                    (myname.lower(), leid.lower(), lproj.lower(),
655                            ldomain.lower())
656            for s in services:
657                if s.get('name',"") in self.imports and \
658                        s.get('visibility','') == 'import':
659                    client_service_out[s['name']](f, s)
660                if s.get('name', '') in self.exports and \
661                        s.get('visibility', '') == 'export' and \
662                        s['name'] in client_export_service_out:
663                    client_export_service_out[s['name']](f, s)
664            # Seer uses this.
665            if mproj and meid:
666                print >>f, "ExperimentID: %s/%s" % (mproj, meid)
667            f.close()
668        except EnvironmentError, e:
669            raise service_error(service_error.internal,
670                    "Cannot write client.conf: %s" %s)
671
672    def configure_userconf(self, services, tmpdir):
673        """
674        If the userconf service was imported, collect the configuration data.
675        """
676        for s in services:
677            s_name = s.get('name', '')
678            s_vis = s.get('visibility','')
679            if s_name  == 'userconfig' and s_vis == 'import':
680                # Collect ther server and certificate info.
681                u = s.get('server', None)
682                for a in s.get('fedAttr', []):
683                    if a.get('attribute',"") == 'cert':
684                        cert = a.get('value', None)
685                        break
686                else:
687                    cert = None
688
689                if cert:
690                    # Make a temporary certificate file for get_url.  The
691                    # finally clause removes it whether something goes
692                    # wrong (including an exception from get_url) or not.
693                    try:
694                        tfos, tn = tempfile.mkstemp(suffix=".pem")
695                        tf = os.fdopen(tfos, 'w')
696                        print >>tf, cert
697                        tf.close()
698                        self.log.debug("Getting userconf info: %s" % u)
699                        get_url(u, tn, tmpdir, "userconf")
700                        self.log.debug("Got userconf info: %s" % u)
701                    except EnvironmentError, e:
702                        raise service_error(service.error.internal, 
703                                "Cannot create temp file for " + 
704                                "userconfig certificates: %s" % e)
705                    except:
706                        t, v, st = sys.exc_info()
707                        raise service_error(service_error.internal,
708                                "Error retrieving %s: %s" % (u, v))
709                    finally:
710                        if tn: os.remove(tn)
711                else:
712                    raise service_error(service_error.req,
713                            "No certificate for retreiving userconfig")
714                break
715
716    def import_store_info(self, cf, connInfo):
717        """
718        Pull any import parameters in connInfo in.  We translate them either
719        into known member names or fedAddrs.
720        """
721
722        for c in connInfo:
723            for p in [ p for p in c.get('parameter', []) \
724                    if p.get('type', '') == 'input']:
725                name = p.get('name', None)
726                key = p.get('key', None)
727                store = p.get('store', None)
728
729                if name and key and store :
730                    req = { 'name': key, 'wait': True }
731                    self.log.debug("Waiting for %s (%s) from %s" % \
732                            (name, key, store))
733                    r = self.call_GetValue(store, req, cf)
734                    r = r.get('GetValueResponseBody', None)
735                    if r :
736                        if r.get('name', '') == key:
737                            v = r.get('value', None)
738                            if v is not None:
739                                if name == 'peer':
740                                    self.log.debug("Got peer %s" % v)
741                                    c['peer'] = v
742                                else:
743                                    self.log.debug("Got %s %s" % (name, v))
744                                    if c.has_key('fedAttr'):
745                                        c['fedAttr'].append({
746                                            'attribute': name, 'value': v})
747                                    else:
748                                        c['fedAttr']= [{
749                                            'attribute': name, 'value': v}]
750                            else:
751                                raise service_error(service_error.internal, 
752                                        'None value exported for %s'  % key)
753                        else:
754                            raise service_error(service_error.internal, 
755                                    'Different name returned for %s: %s' \
756                                            % (key, r.get('name','')))
757                    else:
758                        raise service_error(service_error.internal, 
759                            'Badly formatted response: no GetValueResponseBody')
760                else:
761                    raise service_error(service_error.internal, 
762                        'Bad Services missing info for import %s' % c)
763
764    def remove_dirs(self, dir):
765        """
766        Remove the directory tree and all files rooted at dir.  Log any errors,
767        but continue.
768        """
769        self.log.debug("[removedirs]: removing %s" % dir)
770        try:
771            for path, dirs, files in os.walk(dir, topdown=False):
772                for f in files:
773                    os.remove(os.path.join(path, f))
774                for d in dirs:
775                    os.rmdir(os.path.join(path, d))
776            os.rmdir(dir)
777        except EnvironmentError, e:
778            self.log.error("Error deleting directory tree in %s" % e);
Note: See TracBrowser for help on using the repository browser.