mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-24 16:10:02 -06:00
Reworking Environment, moved it to config.py
This commit is contained in:
parent
ae8370be44
commit
3a80297b04
@ -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))
|
||||
|
199
ipalib/config.py
199
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,
|
||||
|
||||
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)
|
||||
)
|
||||
for key, value in d.iteritems():
|
||||
if key in default:
|
||||
if isinstance(default[key], (LazyIter, LazyProp)):
|
||||
default[key].set_value(value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""
|
||||
Return the value corresponding to ``key``.
|
||||
"""
|
||||
val = self.__map[key]
|
||||
if hasattr(val, 'get_value'):
|
||||
return val.get_value()
|
||||
else:
|
||||
default[key] = convert_val(type(default[key]), value)
|
||||
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] = value
|
||||
raise AttributeError('cannot overwrite %s.%s' %
|
||||
(self.__class__.__name__, key)
|
||||
)
|
||||
else:
|
||||
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
|
||||
|
||||
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):
|
||||
|
||||
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._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 target_type == bool and isinstance(value, basestring):
|
||||
if self._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)
|
||||
else:
|
||||
raise raise_TypeError(value, bool, 'value')
|
||||
check_isinstance(value, self._type, 'value')
|
||||
return value
|
||||
|
||||
|
||||
class LazyProp(object):
|
||||
def __init__(self, func, value=None):
|
||||
assert isinstance(func, types.FunctionType)
|
||||
class LazyProp(EnvProp):
|
||||
def __init__(self, type_, func, default=None, multi_value=False):
|
||||
check_isinstance(func, types.FunctionType, 'func')
|
||||
self._func = func
|
||||
self._value = value
|
||||
|
||||
def set_value(self, value):
|
||||
self._value = value
|
||||
EnvProp.__init__(self, type_, default, multi_value)
|
||||
|
||||
def get_value(self):
|
||||
if self._value == None:
|
||||
return self._func()
|
||||
if self._get() != None:
|
||||
return self._get()
|
||||
else:
|
||||
return self._value
|
||||
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
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user