diff options
-rw-r--r-- | ipalib/cli.py | 25 | ||||
-rw-r--r-- | ipalib/config.py | 211 | ||||
-rw-r--r-- | ipalib/plugable.py | 72 | ||||
-rwxr-xr-x | test_server | 3 | ||||
-rw-r--r-- | tests/test_ipalib/test_config.py | 106 | ||||
-rw-r--r-- | tests/test_ipalib/test_crud.py | 2 | ||||
-rw-r--r-- | tests/test_ipalib/test_frontend.py | 4 | ||||
-rw-r--r-- | tests/test_ipalib/test_plugable.py | 79 |
8 files changed, 278 insertions, 224 deletions
diff --git a/ipalib/cli.py b/ipalib/cli.py index 5dd2c44f2..07956e0a7 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -25,11 +25,12 @@ import re import sys import code import optparse + import frontend import errors import plugable import ipa_types -import config +from config import set_default_env, read_config def exit_error(error): sys.exit('ipa: ERROR: %s' % error) @@ -207,7 +208,6 @@ class CLI(object): self.__api = api self.__all_interactive = False self.__not_interactive = False - self.__config = None def __get_api(self): return self.__api @@ -256,9 +256,8 @@ class CLI(object): def run(self): self.finalize() - (args, env_dict) = self.parse_globals() - env_dict.update(config.read_config(self.__config)) - self.api.env.update(config.generate_env(env_dict)) + set_default_env(self.api.env) + args = self.parse_globals() if len(args) < 1: self.print_commands() print 'Usage: ipa [global-options] COMMAND' @@ -329,7 +328,6 @@ class CLI(object): return parser def parse_globals(self, argv=sys.argv[1:]): - env_dict = {} parser = optparse.OptionParser() parser.disable_interspersed_args() parser.add_option('-a', dest='interactive', action='store_true', @@ -348,20 +346,23 @@ class CLI(object): self.__all_interactive = True elif options.interactive == False: self.__not_interactive = True - if options.config_file: - self.__config = options.config_file + if options.verbose != None: + self.api.env.verbose = True if options.environment: + env_dict = {} for a in options.environment.split(','): a = a.split('=', 1) if len(a) < 2: parser.error('badly specified environment string,'\ 'use var1=val1[,var2=val2]..') env_dict[a[0].strip()] = a[1].strip() - if options.verbose != None: - env_dict.update(verbose=True) - - return (args, env_dict) + self.api.env.update(env_dict, True) + if options.config_file: + self.api.env.update(read_config(options.config_file), True) + else: + self.api.env.update(read_config(), True) + return args def get_usage(self, cmd): return ' '.join(self.get_usage_iter(cmd)) diff --git a/ipalib/config.py b/ipalib/config.py index 42bf7787b..7899d077f 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -21,74 +21,193 @@ from ConfigParser import SafeConfigParser, ParsingError import types import os +from errors import check_isinstance, raise_TypeError + DEFAULT_CONF='/etc/ipa/ipa.conf' -def generate_env(d={}): - default = dict( - container_accounts = 'cn=accounts', - basedn = 'dc=example,dc=com', - container_user = 'cn=users,cn=accounts', - container_group = 'cn=groups,cn=accounts', - container_service = 'cn=services,cn=accounts', - domain = LazyProp(get_domain), - interactive = True, - query_dns = True, - realm = LazyProp(get_realm), - server_context = True, - server = LazyIter(get_servers), - verbose = False, - ) - for key, value in d.iteritems(): - if key in default: - if isinstance(default[key], (LazyIter, LazyProp)): - default[key].set_value(value) + +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): + if hasattr(self.__map[key], 'set_value'): + self.__map[key].set_value(value) else: - default[key] = convert_val(type(default[key]), value) + raise AttributeError('cannot overwrite %s.%s' % + (self.__class__.__name__, key) + ) else: - default[key] = value + self.__map[key] = value - return default + 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 -# TODO: Add a validation function -def convert_val(target_type, value): - bool_true = ('true', 'yes', 'on') - bool_false = ('false', 'no', 'off') + 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 - if target_type == bool and isinstance(value, basestring): - if value.lower() in bool_true: - return True - elif value.lower() in bool_false: - return False - return target_type(value) + def get(self, name, default=None): + return self.__map.get(name, default) -class LazyProp(object): - def __init__(self, func, value=None): - assert isinstance(func, types.FunctionType) - self._func = func - self._value = value - def set_value(self, value): - self._value = value +def set_default_env(env): + assert isinstance(env, Environment) + + default = dict( + basedn = EnvProp(basestring, 'dc=example,dc=com'), + 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 EnvProp(object): + def __init__(self, type_, default, multi_value=False): + if multi_value: + if isinstance(default, tuple) and len(default): + check_isinstance(default[0], type_, allow_none=True) + self._type = type_ + self._default = default + self._value = None + self._multi_value = multi_value def get_value(self): - if self._value == None: - return self._func() + if self._get() != None: + return self._get() 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 + 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): + def __init__(self, type_, func, default=None): + LazyProp.__init__(self, type_, func, default, multi_value=True) + def get_value(self): - if self._value != None: - if type(self._value) == tuple: - for item in self._value: + val = self._get() + if val != None: + if type(val) == tuple: + for item in val: yield item else: - yield self._value + yield val 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 diff --git a/ipalib/plugable.py b/ipalib/plugable.py index 4a2658a74..98aa41720 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -29,6 +29,7 @@ import re import inspect import errors from errors import check_type, check_isinstance +from config import Environment class ReadOnly(object): @@ -692,77 +693,6 @@ class Registrar(DictProxy): 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): """ Dynamic API object through which `Plugin` instances are accessed. diff --git a/test_server b/test_server index d58d00af7..86df68156 100755 --- a/test_server +++ b/test_server @@ -146,9 +146,10 @@ XMLRPCServer.register_introspection_functions() api.finalize() # Initialize our environment +config.set_default_env(api.env) env_dict = config.read_config() env_dict['server_context'] = True -api.env.update(config.generate_env(env_dict)) +api.env.update(env_dict) # Get and register all the methods for cmd in api.Command: diff --git a/tests/test_ipalib/test_config.py b/tests/test_ipalib/test_config.py index 3fe44136a..df8302326 100644 --- a/tests/test_ipalib/test_config.py +++ b/tests/test_ipalib/test_config.py @@ -23,29 +23,111 @@ Test the `ipalib.config` module. 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 -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 - env = dict( + d = dict( query_dns = False, server = ('first', 'second'), realm = 'myrealm', # test right conversions server_context = 'off', ) - d = config.generate_env(env) - assert d['server_context'] == False - assert d['query_dns'] == False + env = config.Environment() + config.set_default_env(env) + 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) - iter = d['server'].get_value() + iter = env['server'] assert iter.next() == 'first' assert iter.next() == 'second' @@ -59,13 +141,13 @@ def test_LazyProp(): return 1 # Basic sanity testing with no initial value - prop = config.LazyProp(dummy) + prop = config.LazyProp(int, dummy) assert prop.get_value() == 1 prop.set_value(2) assert prop.get_value() == 2 # Basic sanity testing with initial value - prop = config.LazyProp(dummy, 3) + prop = config.LazyProp(int, dummy, 3) assert prop.get_value() == 3 prop.set_value(4) assert prop.get_value() == 4 @@ -81,14 +163,14 @@ def test_LazyIter(): yield 2 # Basic sanity testing with no initial value - prop = config.LazyIter(dummy) + prop = config.LazyIter(int, dummy) iter = prop.get_value() assert iter.next() == 1 assert iter.next() == 2 raises(StopIteration, iter.next) # Basic sanity testing with initial value - prop = config.LazyIter(dummy, 0) + prop = config.LazyIter(int, dummy, 0) iter = prop.get_value() assert iter.next() == 0 assert iter.next() == 1 diff --git a/tests/test_ipalib/test_crud.py b/tests/test_ipalib/test_crud.py index d7e6b51f3..9a207cce9 100644 --- a/tests/test_ipalib/test_crud.py +++ b/tests/test_ipalib/test_crud.py @@ -40,7 +40,7 @@ class CrudChecker(ClassChecker): frontend.Method, frontend.Property, ) - api.env.update(config.generate_env()) + config.set_default_env(api.env) class user(frontend.Object): takes_params = ( 'givenname', diff --git a/tests/test_ipalib/test_frontend.py b/tests/test_ipalib/test_frontend.py index 9ab0504b2..c57428867 100644 --- a/tests/test_ipalib/test_frontend.py +++ b/tests/test_ipalib/test_frontend.py @@ -894,7 +894,7 @@ class test_Object(ClassChecker): frontend.Method, frontend.Property, ) - api.env.update(config.generate_env()) + config.set_default_env(api.env) api.finalize() # Test with no primary keys: @@ -951,7 +951,7 @@ class test_Object(ClassChecker): frontend.Property, backend.Backend, ) - api.env.update(config.generate_env()) + config.set_default_env(api.env) class ldap(backend.Backend): whatever = 'It worked!' api.register(ldap) diff --git a/tests/test_ipalib/test_plugable.py b/tests/test_ipalib/test_plugable.py index dec893a9c..610117971 100644 --- a/tests/test_ipalib/test_plugable.py +++ b/tests/test_ipalib/test_plugable.py @@ -659,85 +659,6 @@ class test_NameSpace(ClassChecker): '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(): """ Test the `ipalib.plugable.Registrar` class |