Reworking Environment, moved it to config.py

This commit is contained in:
Martin Nagy 2008-10-17 22:55:03 +02:00
parent ae8370be44
commit 3a80297b04
8 changed files with 280 additions and 226 deletions

View File

@ -25,11 +25,12 @@ import re
import sys import sys
import code import code
import optparse import optparse
import frontend import frontend
import errors import errors
import plugable import plugable
import ipa_types import ipa_types
import config from config import set_default_env, read_config
def exit_error(error): def exit_error(error):
sys.exit('ipa: ERROR: %s' % error) sys.exit('ipa: ERROR: %s' % error)
@ -207,7 +208,6 @@ class CLI(object):
self.__api = api self.__api = api
self.__all_interactive = False self.__all_interactive = False
self.__not_interactive = False self.__not_interactive = False
self.__config = None
def __get_api(self): def __get_api(self):
return self.__api return self.__api
@ -256,9 +256,8 @@ class CLI(object):
def run(self): def run(self):
self.finalize() self.finalize()
(args, env_dict) = self.parse_globals() set_default_env(self.api.env)
env_dict.update(config.read_config(self.__config)) args = self.parse_globals()
self.api.env.update(config.generate_env(env_dict))
if len(args) < 1: if len(args) < 1:
self.print_commands() self.print_commands()
print 'Usage: ipa [global-options] COMMAND' print 'Usage: ipa [global-options] COMMAND'
@ -329,7 +328,6 @@ class CLI(object):
return parser return parser
def parse_globals(self, argv=sys.argv[1:]): def parse_globals(self, argv=sys.argv[1:]):
env_dict = {}
parser = optparse.OptionParser() parser = optparse.OptionParser()
parser.disable_interspersed_args() parser.disable_interspersed_args()
parser.add_option('-a', dest='interactive', action='store_true', parser.add_option('-a', dest='interactive', action='store_true',
@ -348,20 +346,23 @@ class CLI(object):
self.__all_interactive = True self.__all_interactive = True
elif options.interactive == False: elif options.interactive == False:
self.__not_interactive = True self.__not_interactive = True
if options.config_file: if options.verbose != None:
self.__config = options.config_file self.api.env.verbose = True
if options.environment: if options.environment:
env_dict = {}
for a in options.environment.split(','): for a in options.environment.split(','):
a = a.split('=', 1) a = a.split('=', 1)
if len(a) < 2: if len(a) < 2:
parser.error('badly specified environment string,'\ parser.error('badly specified environment string,'\
'use var1=val1[,var2=val2]..') 'use var1=val1[,var2=val2]..')
env_dict[a[0].strip()] = a[1].strip() env_dict[a[0].strip()] = a[1].strip()
if options.verbose != None: self.api.env.update(env_dict, True)
env_dict.update(verbose=True) if options.config_file:
self.api.env.update(read_config(options.config_file), True)
return (args, env_dict) else:
self.api.env.update(read_config(), True)
return args
def get_usage(self, cmd): def get_usage(self, cmd):
return ' '.join(self.get_usage_iter(cmd)) return ' '.join(self.get_usage_iter(cmd))

View File

@ -21,74 +21,193 @@ from ConfigParser import SafeConfigParser, ParsingError
import types import types
import os import os
from errors import check_isinstance, raise_TypeError
DEFAULT_CONF='/etc/ipa/ipa.conf' DEFAULT_CONF='/etc/ipa/ipa.conf'
def generate_env(d={}):
default = dict( class Environment(object):
container_accounts = 'cn=accounts', """
basedn = 'dc=example,dc=com', A mapping object used to store the environment variables.
container_user = 'cn=users,cn=accounts', """
container_group = 'cn=groups,cn=accounts',
container_service = 'cn=services,cn=accounts', def __init__(self):
domain = LazyProp(get_domain), object.__setattr__(self, '_Environment__map', {})
interactive = True,
query_dns = True, def __getattr__(self, name):
realm = LazyProp(get_realm), """
server_context = True, Return the attribute named ``name``.
server = LazyIter(get_servers), """
verbose = False, return self[name]
)
for key, value in d.iteritems(): def __setattr__(self, name, value):
if key in default: """
if isinstance(default[key], (LazyIter, LazyProp)): Set the attribute named ``name`` to ``value``.
default[key].set_value(value) """
else: self[name] = value
default[key] = convert_val(type(default[key]), value)
def __delattr__(self, name):
"""
Raise AttributeError (deletion is not allowed).
"""
raise AttributeError('cannot del %s.%s' %
(self.__class__.__name__, name)
)
def __getitem__(self, key):
"""
Return the value corresponding to ``key``.
"""
val = self.__map[key]
if hasattr(val, 'get_value'):
return val.get_value()
else: else:
default[key] = value return val
return default def __setitem__(self, key, value):
"""
Set the item at ``key`` to ``value``.
"""
if key in self or hasattr(self, key):
if hasattr(self.__map[key], 'set_value'):
self.__map[key].set_value(value)
else:
raise AttributeError('cannot overwrite %s.%s' %
(self.__class__.__name__, key)
)
else:
self.__map[key] = value
def __contains__(self, key):
"""
Return True if instance contains ``key``; otherwise return False.
"""
return key in self.__map
def __iter__(self):
"""
Iterate through keys in ascending order.
"""
for key in sorted(self.__map):
yield key
def update(self, new_vals, ignore_errors = False):
assert type(new_vals) == dict
for key, value in new_vals.iteritems():
if ignore_errors:
try:
self[key] = value
except (AttributeError, KeyError):
pass
else:
self[key] = value
def get(self, name, default=None):
return self.__map.get(name, default)
# TODO: Add a validation function
def convert_val(target_type, value):
bool_true = ('true', 'yes', 'on')
bool_false = ('false', 'no', 'off')
if target_type == bool and isinstance(value, basestring): def set_default_env(env):
if value.lower() in bool_true: assert isinstance(env, Environment)
return True
elif value.lower() in bool_false: default = dict(
return False basedn = EnvProp(basestring, 'dc=example,dc=com'),
return target_type(value) container_accounts = EnvProp(basestring, 'cn=accounts'),
container_user = EnvProp(basestring, 'cn=users,cn=accounts'),
container_group = EnvProp(basestring, 'cn=groups,cn=accounts'),
container_service = EnvProp(basestring, 'cn=services,cn=accounts'),
domain = LazyProp(basestring, get_domain),
interactive = EnvProp(bool, True),
query_dns = EnvProp(bool, True),
realm = LazyProp(basestring, get_realm),
server_context = EnvProp(bool, True),
server = LazyIter(basestring, get_servers),
verbose = EnvProp(bool, False),
)
env.update(default)
class LazyProp(object): class EnvProp(object):
def __init__(self, func, value=None): def __init__(self, type_, default, multi_value=False):
assert isinstance(func, types.FunctionType) if multi_value:
self._func = func if isinstance(default, tuple) and len(default):
self._value = value check_isinstance(default[0], type_, allow_none=True)
self._type = type_
def set_value(self, value): self._default = default
self._value = value self._value = None
self._multi_value = multi_value
def get_value(self): def get_value(self):
if self._value == None: if self._get() != None:
return self._func() return self._get()
else: else:
raise KeyError, 'Value not set'
def set_value(self, value):
if self._value != None:
raise KeyError, 'Value already set'
self._value = self._validate(value)
def _get(self):
if self._value != None:
return self._value return self._value
elif self._default != None:
return self._default
else:
return None
def _validate(self, value):
if self._multi_value and isinstance(value, tuple):
converted = []
for val in value:
converted.append(self._validate_value(val))
return tuple(converted)
else:
return self._validate_value(value)
def _validate_value(self, value):
bool_true = ('true', 'yes', 'on')
bool_false = ('false', 'no', 'off')
if self._type == bool and isinstance(value, basestring):
if value.lower() in bool_true:
return True
elif value.lower() in bool_false:
return False
else:
raise raise_TypeError(value, bool, 'value')
check_isinstance(value, self._type, 'value')
return value
class LazyProp(EnvProp):
def __init__(self, type_, func, default=None, multi_value=False):
check_isinstance(func, types.FunctionType, 'func')
self._func = func
EnvProp.__init__(self, type_, default, multi_value)
def get_value(self):
if self._get() != None:
return self._get()
else:
return self._func()
class LazyIter(LazyProp): class LazyIter(LazyProp):
def __init__(self, type_, func, default=None):
LazyProp.__init__(self, type_, func, default, multi_value=True)
def get_value(self): def get_value(self):
if self._value != None: val = self._get()
if type(self._value) == tuple: if val != None:
for item in self._value: if type(val) == tuple:
for item in val:
yield item yield item
else: else:
yield self._value yield val
for item in self._func(): for item in self._func():
if not self._value or item not in self._value: if not val or item not in val:
yield item yield item

View File

@ -29,6 +29,7 @@ import re
import inspect import inspect
import errors import errors
from errors import check_type, check_isinstance from errors import check_type, check_isinstance
from config import Environment
class ReadOnly(object): class ReadOnly(object):
@ -692,77 +693,6 @@ class Registrar(DictProxy):
self.__registered.add(klass) self.__registered.add(klass)
class Environment(object):
"""
A mapping object used to store the environment variables.
"""
def __init__(self):
object.__setattr__(self, '_Environment__map', {})
def __getattr__(self, name):
"""
Return the attribute named ``name``.
"""
return self[name]
def __setattr__(self, name, value):
"""
Set the attribute named ``name`` to ``value``.
"""
self[name] = value
def __delattr__(self, name):
"""
Raise AttributeError (deletion is not allowed).
"""
raise AttributeError('cannot del %s.%s' %
(self.__class__.__name__, name)
)
def __getitem__(self, key):
"""
Return the value corresponding to ``key``.
"""
val = self.__map[key]
if hasattr(val, 'get_value'):
return val.get_value()
else:
return val
def __setitem__(self, key, value):
"""
Set the item at ``key`` to ``value``.
"""
if key in self or hasattr(self, key):
raise AttributeError('cannot overwrite %s.%s' %
(self.__class__.__name__, key)
)
self.__map[key] = value
def __contains__(self, key):
"""
Return True if instance contains ``key``; otherwise return False.
"""
return key in self.__map
def __iter__(self):
"""
Iterate through keys in ascending order.
"""
for key in sorted(self.__map):
yield key
def update(self, new_vals, ignore_errors = False):
assert type(new_vals) == dict
for key, value in new_vals.iteritems():
if key in self and ignore_errors:
continue
self[key] = value
def get(self, name, default=None):
return self.__map.get(name, default)
class API(DictProxy): class API(DictProxy):
""" """
Dynamic API object through which `Plugin` instances are accessed. Dynamic API object through which `Plugin` instances are accessed.

View File

@ -146,9 +146,10 @@ XMLRPCServer.register_introspection_functions()
api.finalize() api.finalize()
# Initialize our environment # Initialize our environment
config.set_default_env(api.env)
env_dict = config.read_config() env_dict = config.read_config()
env_dict['server_context'] = True env_dict['server_context'] = True
api.env.update(config.generate_env(env_dict)) api.env.update(env_dict)
# Get and register all the methods # Get and register all the methods
for cmd in api.Command: for cmd in api.Command:

View File

@ -23,29 +23,111 @@ Test the `ipalib.config` module.
import types import types
from tests.util import raises from tests.util import raises, setitem, delitem
#from tests.util import getitem, setitem, delitem
from ipalib import config from ipalib import config
def test_generate_env(): def test_Environment():
""" """
Test the `ipalib.config.generate_env` function. Test the `ipalib.config.Environment` class.
"""
# This has to be the same as iter_cnt
control_cnt = 0
class prop_class:
def __init__(self, val):
self._val = val
def get_value(self):
return self._val
class iter_class(prop_class):
# Increment this for each time iter_class yields
iter_cnt = 0
def get_value(self):
for item in self._val:
self.__class__.iter_cnt += 1
yield item
# Tests for basic functionality
basic_tests = (
('a', 1),
('b', 'basic_foo'),
('c', ('basic_bar', 'basic_baz')),
)
# Tests with prop classes
prop_tests = (
('d', prop_class(2), 2),
('e', prop_class('prop_foo'), 'prop_foo'),
('f', prop_class(('prop_bar', 'prop_baz')), ('prop_bar', 'prop_baz')),
)
# Tests with iter classes
iter_tests = (
('g', iter_class((3, 4, 5)), (3, 4, 5)),
('h', iter_class(('iter_foo', 'iter_bar', 'iter_baz')),
('iter_foo', 'iter_bar', 'iter_baz')
),
)
# Set all the values
env = config.Environment()
for name, val in basic_tests:
env[name] = val
for name, val, dummy in prop_tests:
env[name] = val
for name, val, dummy in iter_tests:
env[name] = val
# Test if the values are correct
for name, val in basic_tests:
assert env[name] == val
for name, dummy, val in prop_tests:
assert env[name] == val
# Test if the get_value() function is called only when needed
for name, dummy, correct_values in iter_tests:
values_in_env = []
for val in env[name]:
control_cnt += 1
assert iter_class.iter_cnt == control_cnt
values_in_env.append(val)
assert tuple(values_in_env) == correct_values
# Test __setattr__()
env.spam = 'ham'
assert env.spam == 'ham'
# Test if we throw AttributeError exception when trying to overwrite
# existing value, or delete it
raises(AttributeError, setitem, env, 'a', 1)
raises(AttributeError, setattr, env, 'a', 1)
raises(AttributeError, delitem, env, 'a')
raises(AttributeError, delattr, env, 'a')
raises(AttributeError, config.Environment.update, env, dict(a=1000))
# This should be silently ignored
env.update(dict(a=1000), True)
assert env.a != 1000
def test_set_default_env():
"""
Test the `ipalib.config.set_default_env` function.
""" """
# Make sure we don't overwrite any properties # Make sure we don't overwrite any properties
env = dict( d = dict(
query_dns = False, query_dns = False,
server = ('first', 'second'), server = ('first', 'second'),
realm = 'myrealm', realm = 'myrealm',
# test right conversions # test right conversions
server_context = 'off', server_context = 'off',
) )
d = config.generate_env(env) env = config.Environment()
assert d['server_context'] == False config.set_default_env(env)
assert d['query_dns'] == False env.update(d)
assert env['server_context'] == False
assert env['query_dns'] == False
# Make sure the servers is overwrote properly (that it is still LazyProp) # Make sure the servers is overwrote properly (that it is still LazyProp)
iter = d['server'].get_value() iter = env['server']
assert iter.next() == 'first' assert iter.next() == 'first'
assert iter.next() == 'second' assert iter.next() == 'second'
@ -59,13 +141,13 @@ def test_LazyProp():
return 1 return 1
# Basic sanity testing with no initial value # Basic sanity testing with no initial value
prop = config.LazyProp(dummy) prop = config.LazyProp(int, dummy)
assert prop.get_value() == 1 assert prop.get_value() == 1
prop.set_value(2) prop.set_value(2)
assert prop.get_value() == 2 assert prop.get_value() == 2
# Basic sanity testing with initial value # Basic sanity testing with initial value
prop = config.LazyProp(dummy, 3) prop = config.LazyProp(int, dummy, 3)
assert prop.get_value() == 3 assert prop.get_value() == 3
prop.set_value(4) prop.set_value(4)
assert prop.get_value() == 4 assert prop.get_value() == 4
@ -81,14 +163,14 @@ def test_LazyIter():
yield 2 yield 2
# Basic sanity testing with no initial value # Basic sanity testing with no initial value
prop = config.LazyIter(dummy) prop = config.LazyIter(int, dummy)
iter = prop.get_value() iter = prop.get_value()
assert iter.next() == 1 assert iter.next() == 1
assert iter.next() == 2 assert iter.next() == 2
raises(StopIteration, iter.next) raises(StopIteration, iter.next)
# Basic sanity testing with initial value # Basic sanity testing with initial value
prop = config.LazyIter(dummy, 0) prop = config.LazyIter(int, dummy, 0)
iter = prop.get_value() iter = prop.get_value()
assert iter.next() == 0 assert iter.next() == 0
assert iter.next() == 1 assert iter.next() == 1

View File

@ -40,7 +40,7 @@ class CrudChecker(ClassChecker):
frontend.Method, frontend.Method,
frontend.Property, frontend.Property,
) )
api.env.update(config.generate_env()) config.set_default_env(api.env)
class user(frontend.Object): class user(frontend.Object):
takes_params = ( takes_params = (
'givenname', 'givenname',

View File

@ -894,7 +894,7 @@ class test_Object(ClassChecker):
frontend.Method, frontend.Method,
frontend.Property, frontend.Property,
) )
api.env.update(config.generate_env()) config.set_default_env(api.env)
api.finalize() api.finalize()
# Test with no primary keys: # Test with no primary keys:
@ -951,7 +951,7 @@ class test_Object(ClassChecker):
frontend.Property, frontend.Property,
backend.Backend, backend.Backend,
) )
api.env.update(config.generate_env()) config.set_default_env(api.env)
class ldap(backend.Backend): class ldap(backend.Backend):
whatever = 'It worked!' whatever = 'It worked!'
api.register(ldap) api.register(ldap)

View File

@ -659,85 +659,6 @@ class test_NameSpace(ClassChecker):
'NameSpace(<%d members>, sort=%r)' % (cnt, sort) 'NameSpace(<%d members>, sort=%r)' % (cnt, sort)
def test_Environment():
"""
Test the `ipalib.plugable.Environment` class.
"""
# This has to be the same as iter_cnt
control_cnt = 0
class prop_class:
def __init__(self, val):
self._val = val
def get_value(self):
return self._val
class iter_class(prop_class):
# Increment this for each time iter_class yields
iter_cnt = 0
def get_value(self):
for item in self._val:
self.__class__.iter_cnt += 1
yield item
# Tests for basic functionality
basic_tests = (
('a', 1),
('b', 'basic_foo'),
('c', ('basic_bar', 'basic_baz')),
)
# Tests with prop classes
prop_tests = (
('d', prop_class(2), 2),
('e', prop_class('prop_foo'), 'prop_foo'),
('f', prop_class(('prop_bar', 'prop_baz')), ('prop_bar', 'prop_baz')),
)
# Tests with iter classes
iter_tests = (
('g', iter_class((3, 4, 5)), (3, 4, 5)),
('h', iter_class(('iter_foo', 'iter_bar', 'iter_baz')),
('iter_foo', 'iter_bar', 'iter_baz')
),
)
# Set all the values
env = plugable.Environment()
for name, val in basic_tests:
env[name] = val
for name, val, dummy in prop_tests:
env[name] = val
for name, val, dummy in iter_tests:
env[name] = val
# Test if the values are correct
for name, val in basic_tests:
assert env[name] == val
for name, dummy, val in prop_tests:
assert env[name] == val
# Test if the get_value() function is called only when needed
for name, dummy, correct_values in iter_tests:
values_in_env = []
for val in env[name]:
control_cnt += 1
assert iter_class.iter_cnt == control_cnt
values_in_env.append(val)
assert tuple(values_in_env) == correct_values
# Test __setattr__()
env.spam = 'ham'
assert env.spam == 'ham'
# Test if we throw AttributeError exception when trying to overwrite
# existing value, or delete it
raises(AttributeError, setitem, env, 'a', 1)
raises(AttributeError, setattr, env, 'a', 1)
raises(AttributeError, delitem, env, 'a')
raises(AttributeError, delattr, env, 'a')
raises(AttributeError, plugable.Environment.update, env, dict(a=1000))
# This should be silently ignored
env.update(dict(a=1000), True)
assert env.a != 1000
def test_Registrar(): def test_Registrar():
""" """
Test the `ipalib.plugable.Registrar` class Test the `ipalib.plugable.Registrar` class