Changeset 72ed6e4 for fedd


Ignore:
Timestamp:
Oct 20, 2008 1:33:21 PM (16 years ago)
Author:
Ted Faber <faber@…>
Branches:
axis_example, compt_changes, info-ops, master, version-1.30, version-2.00, version-3.01, version-3.02
Children:
f4f4117
Parents:
4700b3b
Message:

refactor configuration parsing to make code extensions more modular

Location:
fedd
Files:
1 deleted
5 edited

Legend:

Unmodified
Added
Removed
  • fedd/fedd.py

    r4700b3b r72ed6e4  
    2323from time import sleep
    2424import logging
     25from ConfigParser import *
    2526
    2627# The SSL server here is based on the implementation described at
     
    2930# Turn off the matching of hostname to certificate ID
    3031SSL.Connection.clientPostConnectionCheck = None
     32
     33class fedd_config_parser(SafeConfigParser):
     34    """
     35    A SafeConfig parser with a more forgiving get attribute
     36    """
     37    def get(self, sect, opt, default=None):
     38        """
     39        This returns the requested option or a given default.
     40
     41        It's more like getattr than get.
     42        """
     43        if self.has_option(sect, opt):
     44            return SafeConfigParser.get(self, sect, opt)
     45        else:
     46            return default
    3147
    3248class fedd_server(ThreadingSSLServer):
     
    302318flog.addHandler(fh)
    303319
     320
     321
    304322# Initialize the implementation
    305323if opts.configfile != None:
    306324    try:
    307         impl = new_feddservice(opts.configfile)
    308     except RuntimeError, e:
    309         str = getattr(e, 'desc', None) or getattr(e,'message', None) or \
    310                 "No message"
    311         sys.exit("Error configuring fedd: %s" % str)
     325        config= fedd_config_parser()
     326        config.read(opts.configfile)
     327    except e:
     328        sys.exit("Cannot parse confgi file: %s" % e)
    312329else:
    313330    sys.exit("--configfile is required")
     331
     332try:
     333    impl = new_feddservice(config)
     334except RuntimeError, e:
     335    str = getattr(e, 'desc', None) or getattr(e,'message', None) or \
     336            "No message"
     337    sys.exit("Error configuring fedd: %s" % str)
    314338
    315339if impl.cert_file == None:
  • fedd/fedd_access.py

    r4700b3b r72ed6e4  
    1515import copy
    1616
    17 from fedd_config_file import config_file
    1817from fedd_access_project import access_project
    1918from fedd_services import *
     
    4039    """
    4140
     41    bool_attrs = ("dynamic_projects", "project_priority")
     42    emulab_attrs = ("boss", "ops", "domain", "fileserver", "eventserver")
     43    id_attrs = ("testbed", "proxy",
     44            "proxy_cert_file", "proxy_cert_pwd", "proxy_trusted_certs",
     45            "dynamic_projects_url", "dynamic_projects_cert_file",
     46            "dynamic_projects_cert_pwd", "dynamic_projects_trusted_certs")
     47    id_list_attrs = ("restricted",)
     48
     49    def read_trust(self, trust):
     50        """
     51        Read a trust file that splits fedids into testbeds, users or projects
     52
     53        Format is:
     54
     55        [type]
     56        fedid
     57        fedid
     58        default: type
     59        """
     60        lineno = 0;
     61        cat = None
     62        cat_re = re.compile("\[(user|testbed|project)\]$", re.IGNORECASE)
     63        fedid_re = re.compile("[" + string.hexdigits + "]+$")
     64        default_re = re.compile("default:\s*(user|testbed|project)$",
     65                re.IGNORECASE)
     66
     67        f = open(trust, "r")
     68        for line in f:
     69            lineno += 1
     70            line = line.strip()
     71            if len(line) == 0 or line.startswith("#"):
     72                continue
     73            # Category line
     74            m = cat_re.match(line)
     75            if m != None:
     76                cat = m.group(1).lower()
     77                continue
     78            # Fedid line
     79            m = fedid_re.match(line)
     80            if m != None:
     81                if cat != None:
     82                    self.fedid_category[fedid(hexstr=m.string)] = cat
     83                else:
     84                    raise parse_error(\
     85                            "Bad fedid in trust file (%s) line: %d" % \
     86                            (trust, lineno))
     87                continue
     88            # default line
     89            m = default_re.match(line)
     90            if m != None:
     91                self.fedid_default = m.group(1).lower()
     92                continue
     93            # Nothing matched - bad line, raise exception
     94            f.close()
     95            raise parse_error(\
     96                    "Unparsable line in trustfile %s line %d" % (trust, lineno))
     97        f.close()
     98
     99    def read_access(self, config):
     100        """
     101        Read a configuration file and set internal parameters.
     102
     103        The format is more complex than one might hope.  The basic format is
     104        attribute value pairs separated by colons(:) on a signle line.  The
     105        attributes in bool_attrs, emulab_attrs and id_attrs can all be set
     106        directly using the name: value syntax.  E.g.
     107        boss: hostname
     108        sets self.boss to hostname.  In addition, there are access lines of the
     109        form (tb, proj, user) -> (aproj, auser) that map the first tuple of
     110        names to the second for access purposes.  Names in the key (left side)
     111        can include "<NONE> or <ANY>" to act as wildcards or to require the
     112        fields to be empty.  Similarly aproj or auser can be <SAME> or
     113        <DYNAMIC> indicating that either the matching key is to be used or a
     114        dynamic user or project will be created.  These names can also be
     115        federated IDs (fedid's) if prefixed with fedid:.  Finally, the aproj
     116        can be followed with a colon-separated list of node types to which that
     117        project has access (or will have access if dynamic).
     118        Testbed attributes outside the forms above can be given using the
     119        format attribute: name value: value.  The name is a single word and the
     120        value continues to the end of the line.  Empty lines and lines startin
     121        with a # are ignored.
     122
     123        Parsing errors result in a parse_error exception being raised.
     124        """
     125        lineno=0
     126        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
     127        fedid_expr = "fedid:[" + string.hexdigits + "]+"
     128        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
     129        access_proj = "(<DYNAMIC>(?::" + name_expr +")*|"+ \
     130                "<SAME>" + "(?::" + name_expr + ")*|" + \
     131                fedid_expr + "(?::" + name_expr + ")*|" + \
     132                name_expr + "(?::" + name_expr + ")*)"
     133        access_name = "(<DYNAMIC>|<SAME>|" + fedid_expr + "|"+ name_expr + ")"
     134
     135        restricted_re = re.compile("restricted:\s*(.*)", re.IGNORECASE)
     136        attr_re = re.compile('attribute:\s*([\._\-a-z0-9]+)\s+value:\s*(.*)',
     137                re.IGNORECASE)
     138        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
     139                key_name+'\s*\)\s*->\s*\('+access_proj + '\s*,\s*' +
     140                access_name + '\s*\)', re.IGNORECASE)
     141
     142        def parse_name(n):
     143            if n.startswith('fedid:'): return fedid(n[len('fedid:'):])
     144            else: return n
     145
     146        f = open(config, "r");
     147        for line in f:
     148            lineno += 1
     149            line = line.strip();
     150            if len(line) == 0 or line.startswith('#'):
     151                continue
     152
     153            # Extended (attribute: x value: y) attribute line
     154            m = attr_re.match(line)
     155            if m != None:
     156                attr, val = m.group(1,2)
     157                self.attrs[attr] = val
     158                continue
     159
     160            # Restricted entry
     161            m = restricted_re.match(line)
     162            if m != None:
     163                val = m.group(1)
     164                self.restricted.append(val)
     165                continue
     166
     167            # Access line (t, p, u) -> (ap, au) line
     168            m = access_re.match(line)
     169            if m != None:
     170                access_key = tuple([ parse_name(x) for x in m.group(1,2,3)])
     171                aps = m.group(4).split(":");
     172                if aps[0] == 'fedid:':
     173                    del aps[0]
     174                    aps[0] = fedid(hexstr=aps[0])
     175
     176                au = m.group(5)
     177                if au.startswith("fedid:"):
     178                    au = fedid(hexstr=aus[len("fedid:"):])
     179
     180                access_val = (access_project(aps[0], aps[1:]), au)
     181
     182                self.access[access_key] = access_val
     183                continue
     184
     185            # Nothing matched to here: unknown line - raise exception
     186            f.close()
     187            raise parse_error("Unknown statement at line %d of %s" % \
     188                    (lineno, config))
     189        f.close()
     190
     191
    42192    def __init__(self, config=None):
    43193        """
    44         Initializer.  Parses a configuration if one is given.
    45         """
    46 
    47         # Read the configuration
    48         if not config: raise RunTimeError("No config to fedd_allocate")
     194        Initializer.  Pulls parameters out of the ConfigParser's access section.
     195        """
     196
     197        # Make sure that the configuration is in place
     198        if config:
     199            if not config.has_section("access"):
     200                config.add_section("access")
     201            if not config.has_section("globals"):
     202                config.add_section("globals")
     203        else:
     204            raise RunTimeError("No config to fedd_access")
     205
    49206
    50207        # Create instance attributes from the static lists
    51         for a in config_file.bool_attrs:
    52             setattr(self, a, getattr(config, a, False))
    53 
    54         for a in config_file.emulab_attrs + config_file.id_attrs:
    55             setattr(self, a, getattr(config, a, None))
    56 
    57 
    58         self.attrs = copy.deepcopy(config.attrs);
    59         self.access = copy.deepcopy(config.access);
    60         self.fedid_category = copy.deepcopy(config.fedid_category)
    61         self.fedid_default = config.fedid_default
    62         self.restricted = copy.copy(config.restricted)
     208        for a in fedd_access.bool_attrs:
     209            if config.has_option("access", a):
     210                setattr(self, a, config.get("access", a))
     211            else:
     212                setattr(self, a, False)
     213
     214        for a in fedd_access.emulab_attrs + fedd_access.id_attrs:
     215            if config.has_option("access", a):
     216                setattr(self, a, config.get("access",a))
     217            else:
     218                setattr(self, a, None)
     219
     220        self.attrs = { }
     221        self.access = { }
     222        self.restricted = [ ]
     223        self.fedid_category = { }
     224        self.fedid_default = "testbed"
     225        if config.has_option("access", "accessdb"):
     226            self.read_access(config.get("access", "accessdb"))
     227        if config.has_option("access", "trustdb"):
     228            self.read_trust(config.get("access", "trustdb"))
    63229
    64230        self.log = logging.getLogger("fedd.access")
     
    69235        # proxy certs are used, and if none of those the main certs.
    70236
    71         if config.proxy_cert_file:
     237        if config.has_option("globals", "proxy_cert_file"):
    72238            if not self.dynamic_projects_cert_file:
    73                 self.dynamic_projects_cert_file = config.proxy_cert_file
    74                 self.dynamic_projects_cert_pwd = config.proxy_cert_pwd
    75 
    76         if config.proxy_trusted_certs:
     239                self.dynamic_projects_cert_file = \
     240                        config.get("globals", "proxy_cert_file")
     241                if config.has_option("globals", "porxy_cert_pwd"):
     242                    self.dynamic_projects_cert_pwd = \
     243                            config.get("globals", "proxy_cert_pwd")
     244
     245        if config.has_option("globals", "proxy_trusted_certs"):
    77246            if not self.dynamic_projects_trusted_certs:
    78247                self.dynamic_projects_trusted_certs =\
    79                         config.proxy_trusted_certs
    80 
    81         if config.cert_file:
     248                        config.get("globals", proxy_trusted_certs)
     249
     250        if config.has_option("globals", "cert_file"):
     251            has_pwd = config.has_option("globals", "cert_pwd")
    82252            if not self.dynamic_projects_cert_file:
    83                 self.dynamic_projects_cert_file = config.cert_file
    84                 self.dynamic_projects_cert_pwd = config.cert_pwd
     253                self.dynamic_projects_cert_file = \
     254                        config.get("globals", "cert_file")
     255                if has_pwd:
     256                    self.dynamic_projects_cert_pwd = \
     257                            config.get("globals", "cert_pwd")
    85258            if not self.proxy_cert_file:
    86                 self.proxy_cert_file = config.cert_file
    87                 self.proxy_cert_pwd = config.cert_pwd
    88 
    89         if config.trusted_certs:
     259                self.proxy_cert_file = config.get("globals", "cert_file")
     260                if has_pwd:
     261                    self.proxy_cert_pwd = config.get("globals", "cert_pwd")
     262
     263        if config.get("globals", "trusted_certs"):
    90264            if not self.proxy_trusted_certs:
    91                 self.proxy_trusted_certs = config.trusted_certs
     265                self.proxy_trusted_certs = \
     266                        config.get("globals", "trusted_certs")
    92267            if not self.dynamic_projects_trusted_certs:
    93                 self.dynamic_projects_trusted_certs = config.trusted_certs
     268                self.dynamic_projects_trusted_certs = \
     269                        config.get("globals", "trusted_certs")
    94270
    95271        proj_certs = (self.dynamic_projects_cert_file,
     
    109285
    110286
    111         if config.dynamic_projects_url == None:
     287        if not config.has_option("access", "dynamic_projects_url"):
    112288            self.allocate_project = \
    113                 fedd_allocate_project_local(config.dynamic_projects,
    114                         config.dynamic_projects_url, proj_certs)
     289                fedd_allocate_project_local(self.dynamic_projects,
     290                        None, proj_certs)
    115291        else:
    116292            self.allocate_project = \
    117                 fedd_allocate_project_remote(config.dynamic_projects,
    118                         config.dynamic_projects_url, proj_certs)
     293                fedd_allocate_project_remote(self.dynamic_projects,
     294                        config.get("access", "dynamic_projects_url"),
     295                        proj_certs)
    119296
    120297        # If the project allocator exports services, put them in this object's
     
    127304        Dump the state read from a configuration file.  Mostly for debugging.
    128305        """
    129         for a in fedd_proj.bool_attrs:
     306        for a in fedd_access.bool_attrs:
    130307            print "%s: %s" % (a, getattr(self, a ))
    131         for a in fedd_proj.emulab_attrs + fedd_proj.id_attrs:
     308        for a in fedd_access.emulab_attrs + fedd_access.id_attrs:
    132309            print "%s: %s" % (a, getattr(self, a))
    133310        for k, v in self.attrs.iteritems():
  • fedd/fedd_client.py

    r4700b3b r72ed6e4  
    656656                'allocID': pack_id('test alloc'),
    657657                'destinationTestbed': pack_id(opts.testbed),
    658                 'access' : [ { a.type: a.buf } for a in access_keys ],
     658                'serviceAccess' : [ { a.type: a.buf } for a in access_keys ],
     659                'createAccess' : [ { a.type: a.buf } for a in access_keys ],
    659660                }
    660661
  • fedd/fedd_deter_impl.py

    r4700b3b r72ed6e4  
    33from fedd_access import *
    44from fedd_experiment_control import *
    5 from fedd_config_file import *
    65
    76class fedd_deter_impl:
     
    1615            'http://www.isi.edu/faber/fedd_internal.wsdl')
    1716
    18     def __init__(self, config_path=None):
     17    def __init__(self, config=None):
    1918        """
    2019        Initializer.  Parses a configuration if one is given.
    2120        """
    22         if config_path:
    23             self.soap_services = { }
    24             self.xmlrpc_services = { }
    25             config = config_file(config_path)
     21        self.soap_services = { }
     22        self.xmlrpc_services = { }
    2623
    27             self.cert_file = config.cert_file;
    28             self.cert_pwd = config.cert_pwd;
    29             self.trusted_certs = config.trusted_certs;
     24        if config:
     25            self.cert_file = config.get("globals", "cert_file");
     26            self.cert_pwd = config.get("globals", "cert_pwd");
     27            self.trusted_certs = config.get("globals", "trusted_certs");
    3028
    31             self.access = fedd_access(config)
    32             self.experiment = fedd_experiment_control_local(config)
     29            if config.has_section("access"):
     30                self.access = fedd_access(config)
     31                self.soap_services.update(self.access.soap_services)
     32                self.xmlrpc_services.update(self.access.xmlrpc_services)
    3333
    34             self.soap_services.update(self.access.soap_services)
    35             self.soap_services.update(self.experiment.soap_services)
     34            if config.has_section("experiment_control"):
     35                self.experiment = fedd_experiment_control_local(config)
     36                self.soap_services.update(self.experiment.soap_services)
     37                self.xmlrpc_services.update(self.experiment.xmlrpc_services)
    3638
    37             self.xmlrpc_services.update(self.access.xmlrpc_services)
    38             self.xmlrpc_services.update(self.experiment.xmlrpc_services)
    39 
    40 def new_feddservice(configfile):
    41     return fedd_deter_impl(configfile)
     39def new_feddservice(config):
     40    return fedd_deter_impl(config)
  • fedd/fedd_experiment_control.py

    r4700b3b r72ed6e4  
    163163        # attributes until the local certificate attributes can be resolved.
    164164        # The walk is from most specific to most general specification.
    165         for p in ("create_experiment_", "proxy_", ""):
    166             filen = "%scert_file" % p
    167             pwn = "%scert_pwd" % p
    168             trustn = "%strusted_certs" % p
    169 
    170             if getattr(config, filen, None):
    171                 if not self.cert_file:
    172                     self.cert_file = getattr(config, filen, None)
    173                     self.cert_pwd = getattr(config, pwn, None)
    174 
    175             if getattr(config, trustn, None):
    176                 if not self.trusted_certs:
    177                     self.trusted_certs = getattr(config, trustn, None)
     165        for s in ("experiment_control", "globals"):
     166            if config.has_section(s):
     167                if config.has_option(s, "cert_file"):
     168                    if not self.cert_file:
     169                        self.cert_file = config.get(s, "cert_file")
     170                        self.cert_pwd = config.get(s, "cert_pwd")
     171
     172                if config.has_option(s, "trusted_certs"):
     173                    if not self.trusted_certs:
     174                        self.trusted_certs = config.get(s, "trusted_certs")
    178175
    179176        self.exp_stem = "fed-stem"
    180         self.debug = config.create_debug
    181177        self.log = logging.getLogger("fedd.experiment_control")
    182178        self.muxmax = 2
    183179        self.nthreads = 2
    184180        self.randomize_experiments = False
     181
    185182        self.scp_exec = "/usr/bin/scp"
    186         self.scripts_dir = "/users/faber/testbed/federation"
    187183        self.splitter = None
    188184        self.ssh_exec="/usr/bin/ssh"
    189185        self.ssh_keygen = "/usr/bin/ssh-keygen"
    190186        self.ssh_identity_file = None
     187
     188        if config.has_section("experiment_control"):
     189            self.debug = config.get("experiment_control", "create_debug")
     190            self.state_filename = config.get("experiment_control",
     191                    "experiment_state_file")
     192            self.scripts_dir = config.get("experiment_control",
     193                    "federation_script_dir")
     194        else:
     195            self.debug = False
     196            self.state_filename = None
     197            self.scripts_dir = "/users/faber/testbed/federation"
     198
    191199        # XXX
    192200        self.ssh_pubkey_file = "/users/faber/.ssh/id_rsa.pub"
    193201        self.ssh_type = "rsa"
    194202        self.state = { }
    195         self.state_filename = config.experiment_state_file
    196203        self.state_lock = Lock()
    197204        self.tclsh = "/usr/local/bin/otclsh"
     
    227234        # hand finds the logging level constant corrersponding to the string.
    228235        # We're a little paranoid to avoid user mayhem.
    229         if config.experiment_log:
     236        if config.has_option("experiment_control", "log_level"):
    230237            try:
    231                 level = int(getattr(logging, config.experiment_log.upper(),-1))
     238                level = int(getattr(logging,
     239                    config.get("experiment_control", "log_level").upper(),-1))
    232240
    233241                if  logging.DEBUG <= level <= logging.CRITICAL:
     
    235243                else:
    236244                    self.log.error("Bad experiment_log value: %s" % \
    237                             config.experiment_log)
     245                            config.get("experiment_control", "log_level"))
    238246
    239247            except ValueError:
Note: See TracChangeset for help on using the changeset viewer.