source: fedd/fedd_config_file.py @ 19cc408

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

refactoring

  • Property mode set to 100644
File size: 6.9 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4
5import re
6import string
7
8from fedd_util import *
9from fedd_access_project import access_project
10
11# Used to report errors parsing the configuration files, not in providing
12# service
13class parse_error(RuntimeError): pass
14
15class config_file:
16    """
17    The implementation of access control based on mapping users to projects.
18
19    Users can be mapped to existing projects or have projects created
20    dynamically.  This implements both direct requests and proxies.
21    """
22    # Attributes that can be parsed from the configuration file
23    bool_attrs = ("dynamic_projects", "project_priority", "create_debug")
24    emulab_attrs = ("boss", "ops", "domain", "fileserver", "eventserver")
25    id_attrs = ("testbed", "cert_file", "cert_pwd", "trusted_certs", "proxy",
26            "proxy_cert_file", "proxy_cert_pwd", "proxy_trusted_certs",
27            "dynamic_projects_url", "dynamic_projects_cert_file", 
28            "dynamic_projects_cert_pwd", "dynamic_projects_trusted_certs",
29            "create_experiment_cert_file", "create_experiment_cert_pwd",
30            "create_experiment_trusted_certs", "federation_script_dir",
31            "ssh_pubkey_file")
32
33
34    def __init__(self, config=None):
35        """
36        Initializer.  Parses a configuration if one is given.
37        """
38
39        # Create instance attributes from the static lists
40        for a in config_file.bool_attrs:
41            setattr(self, a, False)
42
43        for a in config_file.emulab_attrs + config_file.id_attrs:
44            setattr(self, a, None)
45
46        self.attrs = { }
47        self.fedid_category = { }
48        self.fedid_default = "user"
49        self.access = { }
50        self.restricted = []
51
52        if config:
53            self.read_config(config)
54
55    def read_trust(self, trust):
56        """
57        Read a trust file that splits fedids into testbeds, users or projects
58
59        Format is:
60
61        [type]
62        fedid
63        fedid
64        default: type
65        """
66        lineno = 0;
67        cat = None
68        cat_re = re.compile("\[(user|testbed|project)\]$", re.IGNORECASE)
69        fedid_re = re.compile("[" + string.hexdigits + "]+$")
70        default_re = re.compile("default:\s*(user|testbed|project)$", 
71                re.IGNORECASE)
72
73        f = open(trust, "r")
74        for line in f:
75            lineno += 1
76            line = line.strip()
77            if len(line) == 0 or line.startswith("#"):
78                continue
79            # Category line
80            m = cat_re.match(line)
81            if m != None:
82                cat = m.group(1).lower()
83                continue
84            # Fedid line
85            m = fedid_re.match(line)
86            if m != None:
87                if cat != None:
88                    self.fedid_category[fedid(hexstr=m.string)] = cat
89                else:
90                    raise parse_error(\
91                            "Bad fedid in trust file (%s) line: %d" % \
92                            (trust, lineno))
93                continue
94            # default line
95            m = default_re.match(line)
96            if m != None:
97                self.fedid_default = m.group(1).lower()
98                continue
99            # Nothing matched - bad line, raise exception
100            f.close()
101            raise parse_error(\
102                    "Unparsable line in trustfile %s line %d" % (trust, lineno))
103        f.close()
104
105    def read_config(self, config):
106        """
107        Read a configuration file and set internal parameters.
108
109        The format is more complex than one might hope.  The basic format is
110        attribute value pairs separated by colons(:) on a signle line.  The
111        attributes in bool_attrs, emulab_attrs and id_attrs can all be set
112        directly using the name: value syntax.  E.g.
113        boss: hostname
114        sets self.boss to hostname.  In addition, there are access lines of the
115        form (tb, proj, user) -> (aproj, auser) that map the first tuple of
116        names to the second for access purposes.  Names in the key (left side)
117        can include "<NONE> or <ANY>" to act as wildcards or to require the
118        fields to be empty.  Similarly aproj or auser can be <SAME> or
119        <DYNAMIC> indicating that either the matching key is to be used or a
120        dynamic user or project will be created.  These names can also be
121        federated IDs (fedid's) if prefixed with fedid:.  Finally, the aproj
122        can be followed with a colon-separated list of node types to which that
123        project has access (or will have access if dynamic).
124        Testbed attributes outside the forms above can be given using the
125        format attribute: name value: value.  The name is a single word and the
126        value continues to the end of the line.  Empty lines and lines startin
127        with a # are ignored.
128
129        Parsing errors result in a parse_error exception being raised.
130        """
131        lineno=0
132        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
133        fedid_expr = "fedid:[" + string.hexdigits + "]+"
134        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
135        access_proj = "(<DYNAMIC>(?::" + name_expr +")*|"+ \
136                "<SAME>" + "(?::" + name_expr + ")*|" + \
137                fedid_expr + "(?::" + name_expr + ")*|" + \
138                name_expr + "(?::" + name_expr + ")*)"
139        access_name = "(<DYNAMIC>|<SAME>|" + fedid_expr + "|"+ name_expr + ")"
140
141        bool_re = re.compile('(' + '|'.join(config_file.bool_attrs) + 
142                '):\s+(true|false)', re.IGNORECASE)
143        string_re = re.compile( "(" + \
144                '|'.join(config_file.emulab_attrs + config_file.id_attrs) + \
145                '):\s*(.*)', re.IGNORECASE)
146        attr_re = re.compile('attribute:\s*([\._\-a-z0-9]+)\s+value:\s*(.*)',
147                re.IGNORECASE)
148        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
149                key_name+'\s*\)\s*->\s*\('+access_proj + '\s*,\s*' + 
150                access_name + '\s*\)', re.IGNORECASE)
151        trustfile_re = re.compile("trustfile:\s*(.*)", re.IGNORECASE)
152        restricted_re = re.compile("restricted:\s*(.*)", re.IGNORECASE)
153
154        def parse_name(n):
155            if n.startswith('fedid:'): return fedid(n[len('fedid:'):])
156            else: return n
157
158        f = open(config, "r");
159        for line in f:
160            lineno += 1
161            line = line.strip();
162            if len(line) == 0 or line.startswith('#'):
163                continue
164
165            # Boolean attribute line
166            m = bool_re.match(line);
167            if m != None:
168                attr, val = m.group(1,2)
169                setattr(self, attr.lower(), bool(val.lower() == "true"))
170                continue
171
172            # String attribute line
173            m = string_re.match(line)
174            if m != None:
175                attr, val = m.group(1,2)
176                setattr(self, attr.lower(), val)
177                continue
178
179            # Extended (attribute: x value: y) attribute line
180            m = attr_re.match(line)
181            if m != None:
182                attr, val = m.group(1,2)
183                self.attrs[attr] = val
184                continue
185
186            # Access line (t, p, u) -> (ap, au) line
187            m = access_re.match(line)
188            if m != None:
189                access_key = tuple([ parse_name(x) for x in m.group(1,2,3)])
190                aps = m.group(4).split(":");
191                if aps[0] == 'fedid:':
192                    del aps[0]
193                    aps[0] = fedid(hexstr=aps[0])
194
195                au = m.group(5)
196                if au.startswith("fedid:"):
197                    au = fedid(hexstr=aus[len("fedid:"):])
198
199                access_val = (access_project(aps[0], aps[1:]), au)
200
201                self.access[access_key] = access_val
202                continue
203
204            # Trustfile inclusion
205            m = trustfile_re.match(line)
206            if m != None:
207                self.read_trust(m.group(1))
208                continue
209            # Restricted node types
210
211            m = restricted_re.match(line)
212            if m != None:
213                self.restricted.append(m.group(1))
214                continue
215
216            # Nothing matched to here: unknown line - raise exception
217            f.close()
218            raise parse_error("Unknown statement at line %d of %s" % \
219                    (lineno, config))
220        f.close()
221
222if __name__ == '__main__':
223    if sys.argv[1]:
224        config = config_file(sys.argv[1])
225        for a in [ a for a in dir(config) if a[0] != '_'  \
226                and not callable(getattr(config,a))]:
227            print "%s: %s" % (a, getattr(config, a))
Note: See TracBrowser for help on using the repository browser.