consistency is now simple validation

This commit is contained in:
Emmanuel Garette 2019-10-27 11:09:15 +01:00
parent e62268c46a
commit 5c3a133928
37 changed files with 1163 additions and 6135 deletions

View file

@ -5,11 +5,9 @@ from .autopath import do_autopath
do_autopath()
from .config import config_type, get_config, value_list, global_owner
from tiramisu.setting import owners
from tiramisu import ChoiceOption, StrOption, OptionDescription, Config
from tiramisu import ChoiceOption, StrOption, OptionDescription, Config, owners, Calculation, \
undefined, Params, ParamValue, ParamOption, list_sessions
from tiramisu.error import ConfigError
from tiramisu import undefined, Params, ParamValue, ParamOption
from tiramisu.storage import list_sessions
def teardown_function(function):
@ -58,7 +56,7 @@ def test_choiceoption(config_type):
def test_choiceoption_function(config_type):
choice = ChoiceOption('choice', '', values=return_list)
choice = ChoiceOption('choice', '', values=Calculation(return_list))
odesc = OptionDescription('od', '', [choice])
cfg = Config(odesc)
cfg.property.read_write()
@ -79,7 +77,7 @@ def test_choiceoption_function(config_type):
def test_choiceoption_function_error():
choice = ChoiceOption('choice', '', values=return_error)
choice = ChoiceOption('choice', '', values=Calculation(return_error))
odesc = OptionDescription('od', '', [choice])
cfg = Config(odesc)
cfg.property.read_write()
@ -87,7 +85,7 @@ def test_choiceoption_function_error():
def test_choiceoption_function_error_args():
choice = ChoiceOption('choice', '', values=return_error, values_params=Params((ParamValue('val1'),)))
choice = ChoiceOption('choice', '', values=Calculation(return_error, Params(ParamValue('val1'))))
odesc = OptionDescription('od', '', [choice])
cfg = Config(odesc)
cfg.property.read_write()
@ -95,7 +93,7 @@ def test_choiceoption_function_error_args():
def test_choiceoption_function_error_kwargs():
choice = ChoiceOption('choice', '', values=return_error, values_params=Params(kwargs={'kwargs': ParamValue('val1')}))
choice = ChoiceOption('choice', '', values=Calculation(return_error, Params(kwargs={'kwargs': ParamValue('val1')})))
odesc = OptionDescription('od', '', [choice])
cfg = Config(odesc)
cfg.property.read_write()
@ -103,7 +101,7 @@ def test_choiceoption_function_error_kwargs():
def test_choiceoption_calc_function(config_type):
choice = ChoiceOption('choice', "", values=return_calc_list, values_params=Params((ParamValue('val1'),)))
choice = ChoiceOption('choice', "", values=Calculation(return_calc_list, Params(ParamValue('val1'))))
odesc = OptionDescription('od', '', [choice])
cfg = Config(odesc)
cfg.property.read_write()
@ -125,8 +123,7 @@ def test_choiceoption_calc_opt_function(config_type):
str_ = StrOption('str', '', 'val1')
choice = ChoiceOption('choice',
"",
values=return_calc_list,
values_params=Params((ParamOption(str_),)))
values=Calculation(return_calc_list, Params(ParamOption(str_))))
odesc = OptionDescription('od', '', [str_, choice])
cfg = Config(odesc)
cfg.property.read_write()
@ -148,8 +145,7 @@ def test_choiceoption_calc_opt_function_propertyerror():
str_ = StrOption('str', '', 'val1', properties=('disabled',))
choice = ChoiceOption('choice',
"",
values=return_calc_list,
values_params=Params((ParamOption(str_),)))
values=Calculation(return_calc_list, Params(ParamOption(str_))))
odesc = OptionDescription('od', '', [str_, choice])
cfg = Config(odesc)
cfg.property.read_write()
@ -164,14 +160,12 @@ def test_choiceoption_calc_opt_multi_function():
choice = ChoiceOption('choice',
"",
default_multi='val2',
values=return_val,
values_params=Params((ParamOption(str_),)),
values=Calculation(return_val, Params(ParamOption(str_))),
multi=True)
ch2 = ChoiceOption('ch2',
"",
default=['val2'],
values=return_val,
values_params=Params((ParamOption(str_),)),
values=Calculation(return_val, Params(ParamOption(str_))),
multi=True)
odesc = OptionDescription('od', '', [str_, choice, ch2])
cfg = Config(odesc)
@ -203,14 +197,12 @@ def test_choiceoption_calc_opt_multi_function_kwargs(config_type):
choice = ChoiceOption('choice',
"",
default_multi='val2',
values=return_val,
values_params=Params(kwargs={'val': ParamOption(str_)}),
values=Calculation(return_val, Params(kwargs={'val': ParamOption(str_)})),
multi=True)
ch2 = ChoiceOption('ch2',
"",
default=['val2'],
values=return_val,
values_params=Params(kwargs={'val': ParamOption(str_)}),
values=Calculation(return_val, Params(kwargs={'val': ParamOption(str_)})),
multi=True)
odesc = OptionDescription('od', '', [str_, choice, ch2])
cfg = Config(odesc)
@ -237,21 +229,12 @@ def test_choiceoption_calc_opt_multi_function_kwargs(config_type):
raises(ValueError, "cfg.option('ch2').value.get()")
def test_choiceoption_calc_invalid():
str_ = StrOption('str', '', ['val1'], multi=True)
str_
raises(ValueError,
"choice = ChoiceOption('choice', '', default_multi='val2', values=[1, 2, 3], \
values_params=Params((ParamOption(str_),)), multi=True)")
def test_choiceoption_calc_not_list():
str_ = StrOption('str', '', 'val1')
choice = ChoiceOption('choice',
"",
default_multi='val2',
values=return_val,
values_params=Params((ParamOption(str_),)),
values=Calculation(return_val, Params(ParamOption(str_))),
multi=True)
odesc = OptionDescription('od', '', [str_, choice])
cfg = Config(odesc)

View file

@ -139,77 +139,11 @@ def test_deref_optiondescription_config():
assert w() is None
#def test_deref_groupconfig():
# if not IS_DEREFABLE:
# return
# i1 = IntOption('i1', '')
# od1 = OptionDescription('od1', '', [i1])
# od2 = OptionDescription('od2', '', [od1])
# conf1 = Config(od2, 'conf1')
# conf2 = Config(od2, 'conf2')
# meta = GroupConfig([conf1, conf2])
# w = weakref.ref(conf1)
# del(conf1)
# assert w() is not None
# del(meta)
# assert w() is None
#def test_deref_metaconfig():
# if not IS_DEREFABLE:
# return
# i1 = IntOption('i1', '')
# od1 = OptionDescription('od1', '', [i1])
# od2 = OptionDescription('od2', '', [od1])
# conf1 = Config(od2, 'conf1')
# conf2 = Config(od2, 'conf2')
# meta = MetaConfig([conf1, conf2])
# w = weakref.ref(conf1)
# del(conf1)
# assert w() is not None
# del(meta)
# assert w() is None
def test_deref_consistency():
if not IS_DEREFABLE:
return
a = IPOption('a', '')
b = NetmaskOption('b', '')
od = OptionDescription('od', '', [a, b])
b.impl_add_consistency('ip_netmask', a)
cfg = Config(od)
w = weakref.ref(a)
x = weakref.ref(b)
y = weakref.ref(od)
z = weakref.ref(cfg)
assert w() is not None
assert x() is not None
assert y() is not None
assert z() is not None
del(a)
del(b)
assert w() is not None
assert x() is not None
assert y() is not None
assert z() is not None
del(od)
assert w() is not None
assert x() is not None
assert y() is not None
assert z() is not None
del(cfg)
assert y() is None
assert z() is None
#assert w() is None
#assert x() is None
def test_deref_validator():
if not IS_DEREFABLE:
return
a = StrOption('a', '', default='yes')
b = StrOption('b', '', validator=funcname, validator_params=Params((ParamOption(a),)), default='val')
b = StrOption('b', '', validators=[Calculation(funcname, Params(ParamOption(a)))], default='val')
od = OptionDescription('root', '', [a, b])
cfg = Config(od)
w = weakref.ref(a)
@ -234,8 +168,6 @@ def test_deref_validator():
del(cfg)
assert y() is None
assert z() is None
#assert w() is None
#assert x() is None
def test_deref_callback():
@ -267,8 +199,6 @@ def test_deref_callback():
del(cfg)
assert y() is None
assert z() is None
#assert w() is None
#assert x() is None
def test_deref_symlink():
@ -298,8 +228,6 @@ def test_deref_symlink():
assert w() is not None
assert x() is not None
del(cfg)
#assert w() is None
#assert x() is None
assert y() is None
assert z() is None
@ -333,7 +261,5 @@ def test_deref_dyn():
assert w() is not None
assert x() is not None
del(cfg)
#assert w() is None
#assert x() is None
assert y() is None
assert z() is None

View file

@ -9,7 +9,7 @@ from tiramisu import BoolOption, StrOption, ChoiceOption, IPOption, \
UnicodeOption, PortOption, BroadcastOption, DomainnameOption, \
EmailOption, URLOption, UsernameOption, FilenameOption, SymLinkOption, \
OptionDescription, DynOptionDescription, SynDynOption, submulti, Leadership, \
Config, Params, ParamOption, ParamValue, Calculation, calc_value
Config, Params, ParamOption, ParamValue, ParamSuffix, ParamSelfOption, Calculation, calc_value
from tiramisu.error import PropertiesOptionError, ConfigError, ConflictError
from tiramisu.storage import list_sessions
@ -28,7 +28,7 @@ def return_dynval(value='val', suffix=None):
return value
def return_list2(suffix=None):
def return_list2(suffix):
return [str(suffix), 'val2']
@ -249,7 +249,7 @@ def test_callback_dyndescription():
def test_callback_list_dyndescription():
st = StrOption('st', '', Calculation(return_list2), multi=True)
st = StrOption('st', '', Calculation(return_list2, Params(ParamSuffix())), multi=True)
dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od])
@ -675,7 +675,7 @@ def test_requires_dyndescription2():
def test_validator_dyndescription():
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', validator=return_true, validator_params=Params((ParamValue('yes'),)), default='val')
st = StrOption('st', '', validators=[Calculation(return_true, Params((ParamSelfOption(), ParamValue('yes'))))], default='val')
dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod, val1])
od2 = OptionDescription('od', '', [od])
@ -735,108 +735,13 @@ def test_information_dyndescription_context():
assert api.information.get('testcfgod') == 'val3'
def test_consistency_dyndescription():
st1 = StrOption('st', '')
st2 = StrOption('st2', '')
dod = DynOptionDescription('dod', '', [st1, st2], suffixes=Calculation(return_list))
od1 = OptionDescription('od', '', [dod])
st1.impl_add_consistency('not_equal', st2)
od2 = OptionDescription('od', '', [od1])
api = Config(od2)
api.option('od.dodval1.stval1').value.set('yes')
raises(ValueError, "api.option('od.dodval1.st2val1').value.set('yes')")
api.option('od.dodval2.stval2').value.set('yes')
raises(ValueError, "api.option('od.dodval2.st2val2').value.set('yes')")
raises(ValueError, "api.option('od.dodval1.st2val1').value.set('yes')")
api.option('od.dodval2.stval2').value.reset()
raises(ValueError, "api.option('od.dodval1.st2val1').value.set('yes')")
api.option('od.dodval2.st2val2').value.set('yes')
raises(ValueError, "api.option('od.dodval2.stval2').value.set('yes')")
#
api.option('od.dodval1.stval1').value.reset()
api.option('od.dodval2.st2val2').value.reset()
api.option('od.dodval1.st2val1').value.set('yes')
raises(ValueError, "api.option('od.dodval1.stval1').value.set('yes')")
def test_consistency_dyndescription_default():
st = StrOption('st', '', 'yes')
st2 = StrOption('st2', '')
dod = DynOptionDescription('dod', '', [st, st2], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod])
st.impl_add_consistency('not_equal', st2)
od2 = OptionDescription('od', '', [od])
api = Config(od2)
raises(ValueError, "api.option('od.dodval1.st2val1').value.set('yes')")
raises(ValueError, "api.option('od.dodval2.st2val2').value.set('yes')")
def test_consistency_dyndescription_default_multi2():
st = StrOption('st', '', ['yes'], multi=True)
st2 = StrOption('st2', '', ['yes'], multi=True)
dod = DynOptionDescription('dod', '', [st, st2], suffixes=Calculation(return_list))
dod
# FIXME raises(ValueError, "st.impl_add_consistency('not_equal', st2)")
def test_consistency_only_one_dyndescription():
st = StrOption('st', '')
st
st2 = StrOption('st2', '')
dod = DynOptionDescription('dod', '', [st2], suffixes=Calculation(return_list))
raises(ConfigError, "st.impl_add_consistency('not_equal', st2)")
raises(ConfigError, "st2.impl_add_consistency('not_equal', st)")
def test_consistency_became_dyndescription():
st = StrOption('st', '')
st2 = StrOption('st2', '')
st2.impl_add_consistency('not_equal', st)
od = DynOptionDescription('dod', '', [st2], suffixes=Calculation(return_list))
od2 = OptionDescription('od', '', [od, st])
od2
raises(ConfigError, "c = Config(od2)")
def test_consistency_became_dyndescription2():
st = StrOption('st', '')
st2 = StrOption('st2', '')
st.impl_add_consistency('not_equal', st2)
od = DynOptionDescription('dod', '', [st2], suffixes=Calculation(return_list))
od2 = OptionDescription('od', '', [od, st])
od2
raises(ConfigError, "c = Config(od2)")
def test_consistency_external_dyndescription():
st = StrOption('st', '')
st1 = StrOption('st1', '')
st2 = StrOption('st2', '')
dod = DynOptionDescription('dod', '', [st1, st2], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod, st])
od
raises(ConfigError, "st.impl_add_consistency('not_equal', st2)")
def test_consistency_notsame_dyndescription():
st1 = StrOption('st1', '')
st2 = StrOption('st2', '')
dod = DynOptionDescription('dod', '', [st1, st2], suffixes=Calculation(return_list))
tst1 = StrOption('tst1', '')
tst2 = StrOption('tst2', '')
tdod = DynOptionDescription('tdod', '', [tst1, tst2], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod, tdod])
od
raises(ConfigError, "st1.impl_add_consistency('not_equal', tst1)")
def test_all_dyndescription():
st = StrOption('st', '')
ip = IPOption('ip', '')
network = NetworkOption('network', '')
netmask = NetmaskOption('netmask', '')
ch = ChoiceOption('ch', '', ('val1', 'val2', 'val3'))
ch1 = ChoiceOption('ch1', '', return_list)
ch1 = ChoiceOption('ch1', '', Calculation(return_list))
boo = BoolOption('boo', '')
intr = IntOption('intr', '')
floa = FloatOption('floa', '')
@ -925,34 +830,6 @@ def test_all_dyndescription():
assert api.option('dodval2.filenameval2').value.get() is None
def test_consistency_ip_netmask_dyndescription():
ipa = IPOption('a', '')
netb = NetmaskOption('b', '')
dod = DynOptionDescription('dod', '', [ipa, netb], suffixes=Calculation(return_list))
netb.impl_add_consistency('ip_netmask', ipa)
od1 = OptionDescription('od', '', [dod])
cfg = Config(od1)
cfg.option('dodval1.aval1').value.set('192.168.1.1')
cfg.option('dodval1.bval1').value.set('255.255.255.0')
cfg.option('dodval2.aval2').value.set('192.168.1.2')
cfg.option('dodval2.bval2').value.set('255.255.255.128')
cfg.option('dodval2.bval2').value.set('255.255.255.0')
raises(ValueError, "cfg.option('dodval2.bval2').value.set('255.255.255.255')")
def test_consistency_ip_in_network_dyndescription():
neta = NetworkOption('a', '')
netb = NetmaskOption('b', '')
ipc = IPOption('c', '')
dod = DynOptionDescription('dod', '', [neta, netb, ipc], suffixes=Calculation(return_list))
ipc.impl_add_consistency('in_network', neta, netb)
od1 = OptionDescription('od', '', [dod])
cfg = Config(od1)
cfg.option('dodval1.aval1').value.set('192.168.1.0')
cfg.option('dodval1.bval1').value.set('255.255.255.0')
cfg.option('dodval1.cval1').value.set('192.168.1.1')
def test_leadership_dyndescription():
st1 = StrOption('st1', "", multi=True)
st2 = StrOption('st2', "", multi=True)

View file

@ -178,15 +178,6 @@ def test_force_store_value():
compare(conf.value.exportation(), (('wantref', 'wantref2', 'wantref3'), (None, None, None), (False, False, (False,)), ('forced', 'forced', 'forced')))
def test_force_store_value_no_requirement():
booloption = BoolOption('bool', 'Test boolean option', default=True)
try:
BoolOption('wantref', 'Test requires', default=False,
requires=({'option': booloption, 'expected': True, 'action': 'force_store_value'},))
except ValueError:
pass
def test_force_store_value_leadership_follower():
b = IntOption('int', 'Test int option', multi=True)
c = StrOption('str', 'Test string option', multi=True, properties=('force_store_value',))

View file

@ -330,9 +330,9 @@ def test_groups_with_leader_default_value_2(config_type):
def test_groups_with_leader_hidden_in_config():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True, properties=('hidden',))
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, properties=('hidden',))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0], properties=('hidden',))
od = OptionDescription('root', '', [interface1])
cfg = Config(od)
cfg.property.read_write()
@ -412,22 +412,6 @@ def test_groups_with_leader_reset_out_of_range(config_type):
cfg.send()
def test_groups_with_leader_hidden_in_config3():
#if leader is hidden, follower are hidden too
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True, properties=('hidden',))
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od = OptionDescription('root', '', [interface1])
cfg = Config(od)
cfg.property.read_write()
cfg.permissive.set(frozenset(['hidden']))
assert cfg.forcepermissive.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
cfg.forcepermissive.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.1'])
assert cfg.forcepermissive.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
raises(PropertiesOptionError, "cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()")
raises(PropertiesOptionError, "cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
def test_allowed_groups():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
@ -449,7 +433,8 @@ def test_values_with_leader_disabled_leader(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.230.145'])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set("192.168.230.145")
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.reset()
cfg_ori.option('ip_admin_eth0.ip_admin_eth0').property.add('disabled')
raises(LeadershipError, "cfg_ori.option('ip_admin_eth0.ip_admin_eth0').property.add('disabled')")
cfg_ori.option('ip_admin_eth0').property.add('disabled')
cfg = get_config(cfg_ori, config_type)
if config_type != 'tiramisu-api':
# FIXME
@ -847,49 +832,7 @@ def test_follower_not_multi():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
raises(ValueError, "Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])")
def test_follower_not_same():
ip_admin_eth0 = IPOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
interface0 = Leadership('interface0', '', [ip_admin_eth0, netmask_admin_eth0])
ip_admin_eth1 = IPOption('ip_admin_eth1', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
netmask_admin_eth1 = NetmaskOption('netmask_admin_eth1', "masque du sous-réseau", multi=True)
netmask_admin_eth1.impl_add_consistency('ip_netmask', ip_admin_eth0)
interface1 = Leadership('interface1', '', [ip_admin_eth1, netmask_admin_eth1])
od1 = OptionDescription('od', '', [interface0, interface1])
maconfig = OptionDescription('toto', '', [od1])
raises(ConfigError, "Config(maconfig)")
def test_follower_not_same_not_equal():
ip_admin_eth0 = IPOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
interface0 = Leadership('interface0', '', [ip_admin_eth0, netmask_admin_eth0])
ip_admin_eth1 = IPOption('ip_admin_eth1', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
netmask_admin_eth1 = NetmaskOption('netmask_admin_eth1', "masque du sous-réseau", multi=True)
netmask_admin_eth1.impl_add_consistency('not_equal', netmask_admin_eth0)
interface1 = Leadership('interface1', '', [ip_admin_eth1, netmask_admin_eth1])
od1 = OptionDescription('od', '', [interface0, interface1])
maconfig = OptionDescription('toto', '', [od1])
cfg = Config(maconfig)
cfg.property.read_write()
def test_follower_consistency():
network_admin_eth1 = NetworkOption('network_admin_eth1', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
netmask_admin_eth1 = NetmaskOption('netmask_admin_eth1', "masque du sous-réseau", multi=True)
netmask_admin_eth1.impl_add_consistency('network_netmask', network_admin_eth1)
interface1 = Leadership('interface1', '', [network_admin_eth1, netmask_admin_eth1])
od1 = OptionDescription('od', '', [interface1])
maconfig = OptionDescription('toto', '', [od1])
cfg = Config(maconfig)
cfg.property.read_write()
cfg.option('od.interface1.network_admin_eth1').value.set(['192.168.1.128', '192.168.2.0', '192.168.3.128'])
cfg.option('od.interface1.netmask_admin_eth1', 0).value.set('255.255.255.128')
cfg.option('od.interface1.netmask_admin_eth1', 1).value.set('255.255.255.0')
cfg.option('od.interface1.netmask_admin_eth1', 2).value.set('255.255.255.128')
cfg.option('od.interface1.network_admin_eth1').value.pop(0)
#
def test_follower_force_store_value():

View file

@ -5,7 +5,8 @@ do_autopath()
from tiramisu.setting import groups, owners
from tiramisu import IntOption, StrOption, NetworkOption, NetmaskOption, BoolOption, ChoiceOption, \
IPOption, OptionDescription, Leadership, Config, GroupConfig, MetaConfig, \
Calculation, Params, ParamOption, ParamValue, calc_value
Calculation, Params, ParamOption, ParamValue, calc_value, ParamSelfOption, \
valid_network_netmask, valid_not_equal
from tiramisu.error import ConfigError, ConflictError, PropertiesOptionError, LeadershipError, APIError
from tiramisu.storage import list_sessions
from .config import config_type, get_config
@ -719,9 +720,8 @@ def test_meta_force_default_and_dont_change():
def test_meta_properties_meta():
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", multi=True, properties=('disabled',))
netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(ip_admin_eth0), ParamSelfOption())))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0], properties=('disabled',))
od = OptionDescription('root', '', [interface1])
conf1 = Config(od, session_id='conf1')
conf2 = Config(od, session_id='conf2')
@ -734,8 +734,7 @@ def test_meta_properties_meta():
def test_meta_exception_meta():
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", Calculation(raise_exception), multi=True)
netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0)
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", Calculation(raise_exception), multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(ip_admin_eth0), ParamSelfOption())))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od = OptionDescription('root', '', [interface1])
conf1 = Config(od, session_id='conf1')
@ -753,8 +752,7 @@ def test_meta_properties_requires1():
kwargs={'condition': ParamOption(opt1, todict=True),
'expected': ParamValue(False)}))
od2 = OptionDescription('od2', "", [opt2], properties=(disabled_property,))
opt3 = BoolOption('opt3', '')
opt2.impl_add_consistency('not_equal', opt3)
opt3 = BoolOption('opt3', '', validators=[Calculation(valid_not_equal, Params((ParamOption(opt2), ParamSelfOption())))])
od = OptionDescription('root', '', [opt1, od2, opt3])
conf1 = Config(od, session_id='conf1')
conf1.property.read_write()
@ -775,8 +773,7 @@ def test_meta_properties_requires_mandatory():
'expected': ParamValue('yes'),
'default': ParamValue(None)}))
ip_eth0 = IPOption('ip_eth0', "ip", Calculation(return_condition, Params(kwargs={'val': ParamOption(ip_address), 'condition': ParamOption(eth0_method), 'expected': ParamValue('dhcp')})), properties=(mandatory_property,))
ip_gw = IPOption('ip_gw', 'gw')
ip_gw.impl_add_consistency('not_equal', ip_eth0)
ip_gw = IPOption('ip_gw', 'gw', validators=[Calculation(valid_not_equal, Params((ParamOption(ip_eth0), ParamSelfOption())))])
od = OptionDescription('root', '', [ip_gw, probes, eth0_method, ip_address, ip_eth0])
conf1 = Config(od, session_id='conf1')
conf1.property.read_write()

View file

@ -6,7 +6,8 @@ from py.test import raises
from tiramisu.setting import groups, owners
from tiramisu import IntOption, StrOption, NetworkOption, NetmaskOption, \
OptionDescription, Leadership, Config, GroupConfig, MixConfig, \
MetaConfig, Params, ParamOption, ParamValue, Calculation
MetaConfig, Params, ParamOption, ParamValue, ParamSelfOption, Calculation, \
valid_network_netmask
from tiramisu.error import ConfigError, ConflictError, PropertiesOptionError, LeadershipError, APIError
from tiramisu.storage import list_sessions
@ -589,9 +590,8 @@ def test_mix_force_default_and_dont_change():
def test_mix_properties_mix():
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", multi=True, properties=('disabled',))
netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(ip_admin_eth0), ParamSelfOption())))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0], properties=('disabled',))
od = OptionDescription('root', '', [interface1])
conf1 = Config(od, session_id='conf1')
conf2 = Config(od, session_id='conf2')
@ -604,8 +604,7 @@ def test_mix_properties_mix():
def test_mix_exception_mix():
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", Calculation(raise_exception), multi=True)
netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0)
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", Calculation(raise_exception), multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(ip_admin_eth0), ParamSelfOption())))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od = OptionDescription('root', '', [interface1])
conf1 = Config(od, session_id='conf1')

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ from .config import config_type, get_config
from py.test import raises
from tiramisu.setting import owners
from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.error import PropertiesOptionError, ConfigError, LeadershipError
from tiramisu import IntOption, FloatOption, StrOption, ChoiceOption, \
BoolOption, OptionDescription, Leadership, Config, undefined
from tiramisu.storage import list_sessions
@ -163,7 +163,7 @@ def test_force_default_on_freeze_leader_frozen():
descr = Leadership("dummy1", "", [dummy1, dummy2])
descr = OptionDescription("root", "", [descr])
cfg = Config(descr)
raises(ConfigError, "cfg.option('dummy1.dummy1').property.pop('frozen')")
raises(LeadershipError, "cfg.option('dummy1.dummy1').property.pop('frozen')")
def test_force_metaconfig_on_freeze_leader_frozen():
@ -172,7 +172,7 @@ def test_force_metaconfig_on_freeze_leader_frozen():
descr = Leadership("dummy1", "", [dummy1, dummy2])
descr = OptionDescription("root", "", [descr])
cfg = Config(descr)
raises(ConfigError, "cfg.option('dummy1.dummy1').property.pop('frozen')")
raises(LeadershipError, "cfg.option('dummy1.dummy1').property.pop('frozen')")
def test_force_default_on_freeze_follower(config_type):

View file

@ -10,9 +10,11 @@ from tiramisu.error import display_list, ConfigError
from tiramisu.setting import owners, groups
from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
StrOption, OptionDescription, Leadership, Config, undefined, \
Calculation, Params, ParamOption, ParamValue, ParamIndex, calc_value, calc_value_property_help
Calculation, Params, ParamOption, ParamValue, ParamIndex, \
calc_value, calc_value_property_help
from tiramisu.error import PropertiesOptionError
from tiramisu.storage import list_sessions
import warnings
def teardown_function(function):

View file

@ -5,9 +5,13 @@ from .config import config_type, get_config
import warnings
from py.test import raises
from tiramisu import BoolOption, StrOption, OptionDescription, Leadership, Config, Params, ParamValue, ParamOption, ParamContext
from tiramisu import BoolOption, StrOption, IPOption, NetmaskOption, NetworkOption, BroadcastOption, \
IntOption, \
OptionDescription, Leadership, Config, Params, ParamValue, ParamOption, ParamContext, \
ParamSelfOption, ParamIndex, Calculation, valid_ip_netmask, valid_network_netmask, \
valid_in_network, valid_broadcast, valid_not_equal, undefined
from tiramisu.setting import groups
from tiramisu.error import ValueWarning, ConfigError
from tiramisu.error import ValueWarning, ConfigError, PropertiesOptionError
from tiramisu.i18n import _
from tiramisu.storage import list_sessions
@ -82,15 +86,6 @@ def value_values_auto2(value, values, auto=False):
def value_values_index2(value, values, index, auto=False):
if auto != False:
raise ValueError('auto should be False')
if not (value == 'val1' and values == ['val1'] and index == 'val' or
value == 'val1' and values == ['val1', None] and index == 'val' or
value == 'val2' and values == ['val1', 'val2'] and index == 'val'):
raise ValueError('error')
def value_empty(value, empty, values):
if not value == 'val' or empty is not False and not values == ['val']:
raise ValueError('error')
@ -103,9 +98,9 @@ def valid_from_config(value, config):
def test_validator(config_type):
opt1 = StrOption('opt1', '', validator=return_true, default='val')
raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')")
opt2 = StrOption('opt2', '', validator=return_false)
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()))], default='val')
raises(ValueError, "StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()))], default='val')")
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()))])
root = OptionDescription('root', '', [opt1, opt2])
cfg_ori = Config(root)
cfg = get_config(cfg_ori, config_type)
@ -138,9 +133,9 @@ def test_validator(config_type):
def test_validator_params(config_type):
opt1 = StrOption('opt1', '', validator=return_true, validator_params=Params(ParamValue('yes')), default='val')
raises(ValueError, "StrOption('opt2', '', validator=return_false, validator_params=Params(ParamValue('yes')), default='val')")
opt2 = StrOption('opt2', '', validator=return_false, validator_params=Params(ParamValue('yes')))
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params((ParamSelfOption(), ParamValue('yes'))))], default='val')
raises(ValueError, "StrOption('opt2', '', validators=[Calculation(return_false, Params((ParamSelfOption(), ParamValue('yes'))))], default='val')")
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params((ParamSelfOption(), ParamValue('yes'))))])
root = OptionDescription('root', '', [opt1, opt2])
cfg_ori = Config(root)
cfg = get_config(cfg_ori, config_type)
@ -157,7 +152,7 @@ def test_validator_params(config_type):
def test_validator_params_value_values(config_type):
opt1 = StrOption('opt1', '', validator=value_values, default=['val'], multi=True)
opt1 = StrOption('opt1', '', validators=[Calculation(value_values, Params((ParamSelfOption(whole=False), ParamSelfOption())))], default=['val'], multi=True)
root = OptionDescription('root', '', [opt1])
cfg = Config(root)
cfg = get_config(cfg, config_type)
@ -166,7 +161,7 @@ def test_validator_params_value_values(config_type):
def test_validator_params_value_values_index(config_type):
opt1 = StrOption('opt1', '', validator=value_values_index, default=['val'], multi=True)
opt1 = StrOption('opt1', '', validators=[Calculation(value_values_index, Params((ParamSelfOption(whole=False), ParamSelfOption(), ParamIndex())))], default=['val'], multi=True)
root = OptionDescription('root', '', [opt1])
cfg = Config(root)
cfg = get_config(cfg, config_type)
@ -175,7 +170,7 @@ def test_validator_params_value_values_index(config_type):
def test_validator_params_value_values_leader(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=value_values)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validators=[Calculation(value_values, Params((ParamSelfOption(whole=False), ParamSelfOption())))])
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
@ -186,7 +181,7 @@ def test_validator_params_value_values_leader(config_type):
def test_validator_params_value_values_index_leader(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=value_values_index)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validators=[Calculation(value_values_index, Params((ParamSelfOption(whole=False), ParamSelfOption(), ParamIndex())))])
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
@ -198,7 +193,7 @@ def test_validator_params_value_values_index_leader(config_type):
def test_validator_params_value_values_follower(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=value_values)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validators=[Calculation(value_values, Params((ParamSelfOption(), ParamSelfOption(whole=True))))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
cfg = Config(root)
@ -212,7 +207,7 @@ def test_validator_params_value_values_follower(config_type):
def test_validator_params_value_values_index_follower(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=value_values_index)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validators=[Calculation(value_values_index, Params((ParamSelfOption(), ParamSelfOption(whole=True), ParamIndex())))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
cfg = Config(root)
@ -224,18 +219,13 @@ def test_validator_params_value_values_index_follower(config_type):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val2')
def test_validator_params_value_values_notmulti():
raises(ConfigError, "opt1 = StrOption('opt1', '', validator=value_values, default='val')")
def test_validator_params_value_values_kwargs_empty(config_type):
v = BoolOption('v', '', default=False)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, default=["ip"])
netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau",
multi=True,
validator=value_empty,
validator_params=Params(ParamOption(v)))
validators=[Calculation(value_empty, Params((ParamSelfOption(), ParamOption(v))))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [v, interface1])
cfg = Config(root)
@ -252,8 +242,7 @@ def test_validator_params_value_values_kwargs(config_type):
netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau",
multi=True,
validator=value_values_auto,
validator_params=Params(kwargs={'auto': ParamOption(v)}))
validators=[Calculation(value_values_auto, Params((ParamSelfOption(), ParamSelfOption(whole=True)), kwargs={'auto': ParamOption(v)}))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [v, interface1])
cfg = Config(root)
@ -269,43 +258,7 @@ def test_validator_params_value_values_kwargs_values(config_type):
netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau",
multi=True,
validator=value_values_auto2,
validator_params=Params(kwargs={'values': ParamOption(ip_admin_eth0)}))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
cfg = Config(root)
cfg = get_config(cfg, config_type)
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val'])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1')
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val'])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val2')
def test_validator_params_value_values_kwargs2(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau",
multi=True,
validator=value_values_index2,
validator_params=Params(ParamValue(['val1']), {'index': ParamOption(ip_admin_eth0)}))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
cfg = Config(root)
cfg = get_config(cfg, config_type)
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val'])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1')
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val'])
def test_validator_params_value_values_kwargs_index(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau",
multi=True,
validator=value_values_index2,
validator_params=Params(kwargs={'index': ParamOption(ip_admin_eth0)}))
validators=[Calculation(value_values_auto2, Params(ParamSelfOption(), kwargs={'values': ParamOption(ip_admin_eth0)}))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
cfg = Config(root)
@ -318,7 +271,7 @@ def test_validator_params_value_values_kwargs_index(config_type):
def test_validator_params_context():
opt1 = StrOption('opt1', '', validator=is_context, validator_params=Params(ParamContext()), default='val')
opt1 = StrOption('opt1', '', validators=[Calculation(is_context, Params((ParamSelfOption(), ParamContext())))], default='val')
root = OptionDescription('root', '', [opt1])
cfg = Config(root)
# cfg = get_config(cfg, config_type) # ParamContext not supported
@ -329,7 +282,7 @@ def test_validator_params_context():
def test_validator_params_context_value():
opt1 = StrOption('opt1', '', 'yes')
opt2 = StrOption('opt2', '', validator=valid_from_config, validator_params=Params(ParamContext()), default='val')
opt2 = StrOption('opt2', '', validators=[Calculation(valid_from_config, Params((ParamSelfOption(), ParamContext())))], default='val')
root = OptionDescription('root', '', [opt1, opt2])
cfg = Config(root)
# cfg = get_config(cfg_ori, config_type) # ParamContext not supported
@ -347,8 +300,8 @@ def test_validator_params_context_value():
def test_validator_params_key(config_type):
opt1 = StrOption('opt1', '', validator=return_true, validator_params=Params(kwargs={'param': ParamValue('yes')}), default='val')
raises(ConfigError, "StrOption('opt2', '', validator=return_true, validator_params=Params(kwargs={'param_unknown': ParamValue('yes')}), default='val')")
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption(), kwargs={'param': ParamValue('yes')}))], default='val')
raises(ConfigError, "StrOption('opt2', '', validators=[Calculation(return_true, Params(ParamSelfOption(), kwargs={'param_unknown': ParamValue('yes')}))], default='val')")
root = OptionDescription('root', '', [opt1])
cfg = Config(root)
cfg = get_config(cfg, config_type)
@ -357,7 +310,7 @@ def test_validator_params_key(config_type):
def test_validator_params_option(config_type):
opt0 = StrOption('opt0', '', default='yes')
opt1 = StrOption('opt1', '', validator=return_true, validator_params=Params(ParamOption(opt0)), default='val')
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params((ParamSelfOption(), ParamOption(opt0))))], default='val')
r = OptionDescription('root', '', [opt0, opt1])
cfg_ori = Config(r)
cfg = get_config(cfg_ori, config_type)
@ -375,7 +328,7 @@ def test_validator_params_option(config_type):
def test_validator_multi(config_type):
opt1 = StrOption('opt1', '', validator=return_if_val, multi=True)
opt1 = StrOption('opt1', '', validators=[Calculation(return_if_val, Params(ParamSelfOption(whole=False)))], multi=True)
root = OptionDescription('root', '', [opt1])
cfg_ori = Config(root)
cfg = get_config(cfg_ori, config_type)
@ -394,9 +347,9 @@ def test_validator_multi(config_type):
def test_validator_warning(config_type):
opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True)
opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True)
opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, warnings_only=True)
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()), warnings_only=True)], default='val')
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()), warnings_only=True)])
opt3 = StrOption('opt3', '', validators=[Calculation(return_if_val, Params(ParamSelfOption(whole=False)), warnings_only=True)], multi=True)
root = OptionDescription('root', '', [opt1, opt2, opt3])
cfg = Config(root)
cfg = get_config(cfg, config_type)
@ -440,9 +393,9 @@ def test_validator_warning(config_type):
def test_validator_warning_disabled(config_type):
opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True)
opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True)
opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, warnings_only=True)
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()), warnings_only=True)], default='val')
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()), warnings_only=True)])
opt3 = StrOption('opt3', '', validators=[Calculation(return_if_val, Params(ParamSelfOption(whole=False)), warnings_only=True)], multi=True)
root = OptionDescription('root', '', [opt1, opt2, opt3])
cfg_ori = Config(root)
cfg_ori.property.pop('warnings')
@ -485,8 +438,8 @@ def test_validator_warning_disabled(config_type):
def test_validator_warning_leadership(config_type):
display_name_ip = "ip reseau autorise"
display_name_netmask = "masque du sous-reseau"
ip_admin_eth0 = StrOption('ip_admin_eth0', display_name_ip, multi=True, validator=return_false, warnings_only=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', display_name_netmask, multi=True, validator=return_if_val, warnings_only=True)
ip_admin_eth0 = StrOption('ip_admin_eth0', display_name_ip, multi=True, validators=[Calculation(return_false, Params(ParamSelfOption(whole=False)), warnings_only=True)])
netmask_admin_eth0 = StrOption('netmask_admin_eth0', display_name_netmask, multi=True, validators=[Calculation(return_if_val, Params(ParamSelfOption()), warnings_only=True)])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
assert interface1.impl_get_group_type() == groups.leadership
root = OptionDescription('root', '', [interface1])
@ -543,8 +496,7 @@ def test_validator_follower_param(config_type):
netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau",
multi=True,
validator=return_true,
validator_params=Params(kwargs={'param': ParamOption(ip_admin_eth0)}))
validators=[Calculation(return_true, Params(ParamSelfOption(), kwargs={'param': ParamOption(ip_admin_eth0)}))])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1])
cfg = Config(root)
@ -560,9 +512,8 @@ def test_validator_dependencies():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise")
netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau",
validator=return_true,
validator_params=Params(kwargs={'param': ParamOption(ip_admin_eth0)}))
opt2 = StrOption('opt2', '', validator=return_false)
validators=[Calculation(return_true, Params(ParamSelfOption(whole=False), kwargs={'param': ParamOption(ip_admin_eth0)}))])
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption(whole=False)))])
root = OptionDescription('root', '', [ip_admin_eth0, netmask_admin_eth0, opt2])
cfg = Config(root)
assert cfg.option('ip_admin_eth0').option.has_dependency() is False
@ -572,3 +523,611 @@ def test_validator_dependencies():
assert cfg.option('ip_admin_eth0').option.has_dependency(False) is True
assert cfg.option('netmask_admin_eth0').option.has_dependency(False) is False
assert cfg.option('opt2').option.has_dependency(False) is False
def test_validator_ip_netmask(config_type):
a = IPOption('a', '')
b = NetmaskOption('b', '', validators=[Calculation(valid_ip_netmask, Params((ParamOption(a, todict=True), ParamSelfOption())))])
od = OptionDescription('od', '', [a, b])
cfg_ori = Config(od)
cfg = cfg_ori
cfg = get_config(cfg_ori, config_type)
cfg.option('a').value.set('192.168.1.1')
cfg.option('b').value.set('255.255.255.0')
cfg.option('a').value.set('192.168.1.2')
cfg.option('b').value.set('255.255.255.128')
cfg.option('b').value.set('255.255.255.0')
cfg.option('a').value.set('192.168.1.0')
raises(ValueError, "cfg.option('b').value.get()")
cfg.option('a').value.set('192.168.1.255')
raises(ValueError, "cfg.option('b').value.get()")
cfg.option('a').value.reset()
cfg.option('b').value.reset()
cfg.option('a').value.set('192.168.1.255')
raises(ValueError, "cfg.option('b').value.set('255.255.255.0')")
#
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.add('demoting_error_warning')
cfg = get_config(cfg_ori, config_type)
with warnings.catch_warnings(record=True) as w:
cfg.option('b').value.set('255.255.255.0')
assert len(w) == 1
def test_validator_network_netmask(config_type):
a = NetworkOption('a', '')
b = NetmaskOption('b', '', validators=[Calculation(valid_network_netmask, Params((ParamOption(a, todict=True), ParamSelfOption())))])
od = OptionDescription('od', '', [a, b])
cfg_ori = Config(od)
cfg = get_config(cfg_ori, config_type)
cfg.option('a').value.set('192.168.1.1')
cfg.option('b').value.set('255.255.255.255')
cfg.option('b').value.reset()
cfg.option('a').value.set('192.168.1.0')
cfg.option('b').value.set('255.255.255.0')
cfg.option('a').value.set('192.168.1.1')
raises(ValueError, "cfg.option('b').value.get()")
#
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.add('demoting_error_warning')
cfg = get_config(cfg_ori, config_type)
with warnings.catch_warnings(record=True) as w:
cfg.option('a').value.set('192.168.1.1')
assert len(w) == 0
with warnings.catch_warnings(record=True) as w:
cfg.option('b').value.get()
assert len(w) == 1
def test_validator_ip_in_network(config_type):
a = NetworkOption('a', '')
b = NetmaskOption('b', '')
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))))])
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))), warnings_only=True)])
od = OptionDescription('od', '', [a, b, c, d])
warnings.simplefilter("always", ValueWarning)
cfg = Config(od)
cfg = get_config(cfg, config_type)
cfg.option('a').value.set('192.168.1.0')
cfg.option('b').value.set('255.255.255.0')
cfg.option('c').value.set('192.168.1.1')
raises(ValueError, "cfg.option('c').value.set('192.168.2.1')")
raises(ValueError, "cfg.option('c').value.set('192.168.1.0')")
raises(ValueError, "cfg.option('c').value.set('192.168.1.255')")
with warnings.catch_warnings(record=True) as w:
cfg.option('d').value.set('192.168.2.1')
assert len(w) == 1
def test_validator_ip_in_network_cidr(config_type):
a = NetworkOption('a', '', cidr=True)
c = IPOption('c', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
d = IPOption('d', '', validators=[Calculation(valid_in_network, Params((ParamSelfOption(), ParamOption(a, todict=True))), warnings_only=True)])
od = OptionDescription('od', '', [a, c, d])
warnings.simplefilter("always", ValueWarning)
cfg = Config(od)
cfg = get_config(cfg, config_type)
cfg.option('a').value.set('192.168.1.0/24')
cfg.option('c').value.set('192.168.1.1')
raises(ValueError, "cfg.option('c').value.set('192.168.2.1')")
raises(ValueError, "cfg.option('c').value.set('192.168.1.0')")
raises(ValueError, "cfg.option('c').value.set('192.168.1.255')")
with warnings.catch_warnings(record=True) as w:
cfg.option('d').value.set('192.168.2.1')
assert len(w) == 1
def test_validator_ip_netmask_multi(config_type):
a = IPOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_ip_netmask, Params((ParamOption(a, todict=True), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg_ori = Config(od2)
cfg = get_config(cfg_ori, config_type)
cfg.option('a.a').value.set(['192.168.1.1'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.a').value.set(['192.168.1.2'])
cfg.option('a.b', 0).value.set('255.255.255.128')
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.a').value.set(['192.168.1.0'])
raises(ValueError, "cfg.option('a.b', 0).value.get()")
#
cfg.option('a.a').value.set(['192.168.1.2'])
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.add('demoting_error_warning')
cfg = get_config(cfg_ori, config_type)
cfg.option('a.a').value.set(['192.168.1.0'])
with warnings.catch_warnings(record=True) as w:
cfg.option('a.b', 0).value.get()
assert len(w) == 1
def test_validator_network_netmask_multi(config_type):
a = NetworkOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od', '', [od])
cfg = Config(od2)
cfg = get_config(cfg, config_type)
cfg.option('a.a').value.set(['192.168.1.1'])
cfg.option('a.b', 0).value.set('255.255.255.255')
cfg.option('a.b', 0).value.reset()
cfg.option('a.a').value.set(['192.168.1.0'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.a').value.set(['192.168.1.1'])
raises(ValueError, "cfg.option('a.b', 0).value.get()")
def test_validator_network_netmask_multi_follower_default_multi(config_type):
a = NetworkOption('a', '', default_multi=u'192.168.1.0', multi=True, properties=('mandatory',))
b = NetmaskOption('b', '', default_multi=u'255.255.255.0', multi=True, properties=('mandatory',), validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg = Config(od2)
cfg.property.read_write()
cfg = get_config(cfg, config_type)
cfg.option('a.a').value.set([undefined])
assert cfg.option('a.a').value.get() == ['192.168.1.0']
assert cfg.option('a.b', 0).value.get() == '255.255.255.0'
def test_validator_network_netmask_multi_follower_default(config_type):
a = NetworkOption('a', '', multi=True, properties=('mandatory',))
b = NetmaskOption('b', '', default_multi=u'255.255.255.0', multi=True, properties=('mandatory',), validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg_ori = Config(od2)
cfg_ori.property.read_write()
cfg_ori.property.pop('cache')
cfg = get_config(cfg_ori, config_type)
assert cfg.option('a.a').value.get() == []
cfg.option('a.a').value.set(['192.168.1.0'])
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.read_only()
cfg = get_config(cfg_ori, config_type)
assert cfg.option('a.a').value.get() == [u'192.168.1.0']
assert cfg.option('a.b', 0).value.get() == u'255.255.255.0'
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.read_write()
cfg = get_config(cfg_ori, config_type)
cfg.option('a.a').value.set([u'192.168.1.0', u'192.168.1.1'])
raises(ValueError, "cfg.option('a.b', 0).value.set([u'192.168.1.0'])")
raises(ValueError, "cfg.option('a.b', 1).value.set([u'192.168.1.1'])")
cfg.option('a.a').value.set(['192.168.1.0', undefined])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.b', 1).value.set('255.255.255.255')
cfg.option('a.a').value.set([u'192.168.1.0', u'192.168.1.1'])
def return_netmask(*args, **kwargs):
return u'255.255.255.0'
def return_netmask2(leader):
if leader is not None:
if leader.endswith('2.1'):
return u'255.255.255.0'
if not leader.endswith('.0'):
return u'255.255.255.255'
return u'255.255.255.0'
def test_validator_network_netmask_multi_follower_callback(config_type):
a = NetworkOption('a', '', multi=True, properties=('mandatory',))
b = NetmaskOption('b', '', Calculation(return_netmask, Params(kwargs={'index': ParamIndex()})), multi=True, properties=('mandatory',), validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg_ori = Config(od2)
cfg_ori.property.read_write()
cfg_ori.property.pop('cache')
cfg = get_config(cfg_ori, config_type)
assert cfg.option('a.a').value.get() == []
cfg.option('a.a').value.set(['192.168.1.0'])
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.read_only()
cfg = get_config(cfg_ori, config_type)
assert cfg.option('a.a').value.get() == [u'192.168.1.0']
assert cfg.option('a.b', 0).value.get() == '255.255.255.0'
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.read_write()
cfg = get_config(cfg_ori, config_type)
cfg.option('a.a').value.set([u'192.168.1.0', u'192.168.1.1'])
cfg.option('a.b', 0).value.get()
raises(ValueError, "cfg.option('a.b', 1).value.get()")
cfg.option('a.a').value.set(['192.168.1.0', undefined])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.b', 1).value.set('255.255.255.255')
cfg.option('a.a').value.set(['192.168.1.0', '192.168.1.1'])
def test_validator_network_netmask_multi_follower_callback_value(config_type):
a = NetworkOption('a', '', multi=True, properties=('mandatory',))
b = NetmaskOption('b', '', Calculation(return_netmask2, Params(ParamOption(a))), multi=True, properties=('mandatory',), validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg = Config(od2)
cfg.property.read_write()
cfg.property.pop('cache')
cfg = get_config(cfg, config_type)
assert cfg.option('a.a').value.get() == []
cfg.option('a.a').value.set(['192.168.1.0'])
assert cfg.option('a.a').value.get() == ['192.168.1.0']
assert cfg.option('a.b', 0).value.get() == '255.255.255.0'
cfg.option('a.a').value.set(['192.168.1.0', '192.168.2.1'])
assert cfg.option('a.b', 0).value.get() == '255.255.255.0'
raises(ValueError, "cfg.option('a.b', 1).value.get()")
cfg.option('a.a').value.pop(1)
#
assert cfg.option('a.a').value.get() == [u'192.168.1.0']
assert cfg.option('a.b', 0).value.get() == '255.255.255.0'
cfg.option('a.a').value.set(['192.168.2.1'])
raises(ValueError, "cfg.option('a.b', 0).value.get()")
cfg.option('a.a').value.set(['192.168.1.0'])
#
assert cfg.option('a.a').value.get() == [u'192.168.1.0']
assert cfg.option('a.b', 0).value.get() == '255.255.255.0'
cfg.option('a.a').value.set(['192.168.1.0', '192.168.1.1'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.b', 1).value.set('255.255.255.255')
def test_validator_ip_netmask_multi_leader(config_type):
a = IPOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_ip_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg = Config(od2)
cfg = get_config(cfg, config_type)
cfg.option('a.a').value.set(['192.168.1.1'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.a').value.set(['192.168.1.2'])
cfg.option('a.b', 0).value.set('255.255.255.128')
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.a').value.set(['192.168.1.0'])
raises(ValueError, "cfg.option('a.b', 0).value.get()")
cfg.option('a.a').value.set(['192.168.1.128'])
raises(ValueError, "cfg.option('a.b', 0).value.set('255.255.255.128')")
cfg.option('a.a').value.set(['192.168.1.2', '192.168.1.3'])
def test_validator_network_netmask_multi_leader(config_type):
a = NetworkOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg = Config(od2)
cfg = get_config(cfg, config_type)
cfg.option('a.a').value.set(['192.168.1.1'])
cfg.option('a.b', 0).value.set('255.255.255.255')
cfg.option('a.b', 0).value.reset()
cfg.option('a.a').value.set(['192.168.1.0'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.a').value.set(['192.168.1.1'])
raises(ValueError, "cfg.option('a.b', 0).value.get()")
def test_validator_broadcast(config_type):
a = NetworkOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
c = BroadcastOption('c', '', multi=True, validators=[Calculation(valid_broadcast, Params((ParamOption(a), ParamOption(b), ParamSelfOption())))])
od = Leadership('a', '', [a, b, c])
od2 = OptionDescription('od2', '', [od])
cfg = Config(od2)
cfg = get_config(cfg, config_type)
#first, test network_netmask
cfg.option('a.a').value.set(['192.168.1.128'])
raises(ValueError, "cfg.option('a.a').value.set(['255.255.255.0'])")
#
cfg.option('a.a').value.set(['192.168.1.0'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.c', 0).value.set('192.168.1.255')
cfg.option('a.a').value.set(['192.168.1.1'])
raises(ValueError, "cfg.option('a.b', 0).value.get()")
raises(ValueError, "cfg.option('a.c', 0).value.get()")
#
cfg.option('a.a').value.set(['192.168.1.0', '192.168.2.128'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.b', 1).value.set('255.255.255.128')
cfg.option('a.c', 0).value.set('192.168.1.255')
cfg.option('a.c', 1).value.set('192.168.2.255')
raises(ValueError, "cfg.option('a.c', 1).value.set('192.168.2.128')")
cfg.option('a.c', 1).value.set('192.168.2.255')
def test_validator_broadcast_warnings(config_type):
warnings.simplefilter("always", ValueWarning)
a = NetworkOption('a', '', properties=('mandatory', 'disabled'))
b = NetmaskOption('b', '', properties=('mandatory', 'disabled'), validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())), warnings_only=True)])
od = OptionDescription('a', '', [a, b])
cfg_ori = Config(od)
cfg = get_config(cfg_ori, config_type)
with warnings.catch_warnings(record=True) as w:
cfg.option('a').value.set('192.168.1.4')
cfg.option('b').value.set('255.255.255.0')
assert len(w) == 1
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.read_write()
cfg = get_config(cfg_ori, config_type)
with warnings.catch_warnings(record=True) as w:
list(cfg.value.mandatory())
assert len(w) == 0
def test_validator_broadcast_default_1():
a = NetworkOption('a', '', '192.168.1.0')
b = NetmaskOption('b', '', '255.255.255.128')
c = BroadcastOption('c', '', '192.168.2.127', validators=[Calculation(valid_broadcast, Params((ParamOption(a), ParamOption(b), ParamSelfOption())))])
od = OptionDescription('a', '', [a, b, c])
cfg = Config(od)
raises(ValueError, "cfg.value.dict()")
def test_validator_broadcast_default_2():
a = NetworkOption('a', '', '192.168.1.0')
b = NetmaskOption('b', '', '255.255.255.128')
d = BroadcastOption('d', '', '192.168.1.127', validators=[Calculation(valid_broadcast, Params((ParamOption(a), ParamOption(b), ParamSelfOption())))])
od = OptionDescription('a', '', [a, b, d])
cfg = Config(od)
assert cfg.value.dict()
def test_validator_not_all(config_type):
a = NetworkOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
c = BroadcastOption('c', '', multi=True)
od = Leadership('a', '', [a, b, c])
od = OptionDescription('od2', '', [od])
cfg = Config(od)
cfg = get_config(cfg, config_type)
cfg.option('a.a').value.set(['192.168.1.0'])
cfg.option('a.b', 0).value.set('255.255.255.0')
cfg.option('a.c', 0).value.set('192.168.1.255')
def test_validator_network_netmask_mandatory(config_type):
a = NetworkOption('a', '', multi=True, properties=('mandatory',), default=[u'0.0.0.0'])
b = NetmaskOption('b', '', multi=True, properties=('mandatory',), default_multi=u'0.0.0.0', validators=[Calculation(valid_network_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('od2', '', [od])
cfg = Config(od2)
cfg.property.read_only()
cfg = get_config(cfg, config_type)
cfg.value.dict()
def test_validator_has_dependency():
a = IPOption('a', '')
b = NetmaskOption('b', '', validators=[Calculation(valid_ip_netmask, Params((ParamOption(a), ParamSelfOption())))])
od = OptionDescription('od', '', [a, b])
cfg = Config(od)
assert cfg.option('a').option.has_dependency() is False
assert cfg.option('b').option.has_dependency() is True
assert cfg.option('a').option.has_dependency(False) is True
assert cfg.option('b').option.has_dependency(False) is False
def test_validator_warnings_only_more_option(config_type):
a = IntOption('a', '')
b = IntOption('b', '')
d = IntOption('d', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True), ParamOption(b, todict=True))), warnings_only=True)])
od = OptionDescription('od', '', [a, b, d])
cfg = Config(od)
cfg = get_config(cfg, config_type)
cfg.option('a').value.set(1)
cfg.option('b').value.set(1)
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
cfg.option('d').value.get()
assert w == []
with warnings.catch_warnings(record=True) as w:
cfg.option('d').value.set(1)
assert w != []
assert len(w) == 1
def test_validator_error_prefix():
a = IntOption('a', '')
b = IntOption('b', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
od = OptionDescription('od', '', [a, b])
cfg = Config(od)
cfg.option('a').value.set(1)
try:
cfg.option('b').value.set(1)
except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('1', _('integer'), 'b') + ', ' + _('value is identical to {}').format('"a"')
try:
cfg.option('b').value.set(1)
except Exception as err:
err.prefix = ''
assert str(err) == _('value is identical to {}').format('"a"')
def test_validator_warnings_only_option(config_type):
a = IntOption('a', '')
b = IntOption('b', '', warnings_only=True, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
od = OptionDescription('od', '', [a, b])
cfg_ori = Config(od)
cfg = get_config(cfg_ori, config_type)
cfg.option('a').value.set(1)
raises(ValueError, "cfg.option('b').value.set(1)")
def test_validator_not_equal(config_type):
a = IntOption('a', '')
b = IntOption('b', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
od = OptionDescription('od', '', [a, b])
cfg_ori = Config(od)
cfg = get_config(cfg_ori, config_type)
assert cfg.option('a').value.get() is None
assert cfg.option('b').value.get() is None
cfg.option('a').value.set(1)
cfg.option('a').value.reset()
cfg.option('a').value.set(1)
raises(ValueError, "cfg.option('b').value.set(1)")
cfg.option('b').value.set(2)
#
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.add('demoting_error_warning')
cfg = get_config(cfg_ori, config_type)
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
cfg.option('b').value.set(1)
assert len(w) == 1
def test_validator_not_equal_leadership(config_type):
a = IntOption('a', '', multi=True)
b = IntOption('b', '', multi=True, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('b', '', [od])
cfg = Config(od2)
cfg = get_config(cfg, config_type)
assert cfg.option('a.a').value.get() == []
cfg.option('a.a').value.set([1])
cfg.option('a.a').value.reset()
cfg.option('a.a').value.set([1])
raises(ValueError, "cfg.option('a.b', 0).value.set(1)")
cfg.option('a.b', 0).value.set(2)
cfg.option('a.a').value.reset()
cfg.option('a.a').value.set([1])
cfg.value.dict()
def test_validator_not_equal_leadership_default():
a = IntOption('a', '', multi=True)
b = IntOption('b', '', multi=True, default_multi=1, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a))))])
od = Leadership('a', '', [a, b])
od2 = OptionDescription('a', '', [od])
cfg = Config(od2)
# FIXME cfg = get_config(cfg, config_type)
assert cfg.option('a.a').value.get() == []
cfg.option('a.a').value.set([1])
raises(ValueError, "cfg.option('a.b', 0).value.get()")
cfg.option('a.a').value.set([2])
cfg.option('a.a').value.reset()
cfg.option('a.a').value.set([2])
#
cfg.property.add('demoting_error_warning')
with warnings.catch_warnings(record=True) as w:
cfg.option('a.b', 0).value.set(2)
assert len(w) == 1
def test_validator_default_diff():
a = IntOption('a', '', 3)
b = IntOption('b', '', 1, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
od = OptionDescription('od', '', [a, b])
cfg = Config(od)
# FIXME cfg = get_config(cfg, config_type)
cfg.option('b').value.set(2)
cfg.option('a').value.set(1)
owner = cfg.owner.get()
assert cfg.option('b').owner.get() == owner
raises(ValueError, "cfg.option('b').value.reset()")
assert cfg.option('b').owner.get() == owner
#
cfg.property.add('demoting_error_warning')
with warnings.catch_warnings(record=True) as w:
cfg.option('b').value.reset()
assert len(w) == 1
def test_validator_permissive(config_type):
a = IntOption('a', '', 1, properties=('hidden',))
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
od = OptionDescription('od', '', [a, b])
cfg = Config(od)
cfg.property.read_write()
cfg.permissive.set(frozenset(['hidden']))
cfg = get_config(cfg, config_type)
raises(ValueError, "cfg.option('b').value.set(1)")
cfg.option('b').value.set(2)
def test_validator_disabled(config_type):
a = IntOption('a', '', 1, properties=('disabled',))
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True, raisepropertyerror=True))))])
od = OptionDescription('od', '', [a, b])
cfg = Config(od)
cfg.property.read_write()
cfg = get_config(cfg, config_type)
raises(PropertiesOptionError, "cfg.option('b').value.set(1)")
def test_consistency_disabled_transitive(config_type):
a = IntOption('a', '', 1, properties=('disabled',))
b = IntOption('b', '', 2, validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True, notraisepropertyerror=True))))])
od = OptionDescription('od', '', [a, b])
cfg = Config(od)
cfg.property.read_write()
cfg = get_config(cfg, config_type)
cfg.option('b').value.set(1)
def test_consistency_double_warnings(config_type):
a = IntOption('a', '', 1)
b = IntOption('b', '', 1)
c = IntOption('c', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))), warnings_only=True), Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(b, todict=True))), warnings_only=True)])
od = OptionDescription('od', '', [a, b, c])
warnings.simplefilter("always", ValueWarning)
od2 = OptionDescription('od2', '', [od])
cfg_ori = Config(od2)
cfg = get_config(cfg_ori, config_type)
with warnings.catch_warnings(record=True) as w:
cfg.option('od.c').value.set(1)
assert w != []
if config_type == 'tiramisu-api':
# in this case warnings is for '"a" and "b"'
assert len(w) == 1
else:
# in this cas one warnings is for "a" and the second for "b"
assert len(w) == 2
cfg.option('od.a').value.set(2)
with warnings.catch_warnings(record=True) as w:
cfg.option('od.c').value.get()
assert len(w) == 1
#
if config_type == 'tiramisu-api':
cfg.send()
cfg_ori.property.pop('warnings')
cfg = get_config(cfg_ori, config_type)
with warnings.catch_warnings(record=True) as w:
cfg.option('od.c').value.set(1)
assert w == []
def test_consistency_warnings_error(config_type):
a = IntOption('a', '', 1)
b = IntOption('b', '', 1)
c = IntOption('c', '', validators=[
Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))), warnings_only=True),
Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(b, todict=True))))
])
od = OptionDescription('od', '', [a, b, c])
warnings.simplefilter("always", ValueWarning)
cfg = Config(od)
cfg = get_config(cfg, config_type)
with warnings.catch_warnings(record=True) as w:
raises(ValueError, "cfg.option('c').value.set(1)")
assert w == []
def test_consistency_not_equal_has_dependency():
a = IntOption('a', '')
b = IntOption('b', '', )
b = IntOption('b', '', validators=[Calculation(valid_not_equal, Params((ParamSelfOption(), ParamOption(a, todict=True))))])
od = OptionDescription('od', '', [a, b])
cfg = Config(od)
assert cfg.option('a').option.has_dependency() is False
assert cfg.option('b').option.has_dependency() is True
assert cfg.option('a').option.has_dependency(False) is True
assert cfg.option('b').option.has_dependency(False) is False

File diff suppressed because it is too large Load diff

View file

@ -88,21 +88,21 @@ def test_slots_option_readonly():
p = URLOption('p', '')
q = FilenameOption('q', '')
m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l, o, p, q])
a._requires = (((a,),),)
b._requires = (((a,),),)
c._requires = (((a,),),)
d._requires = (((a,),),)
e._requires = (((a,),),)
g._requires = (((a,),),)
h._requires = (((a,),),)
i._requires = (((a,),),)
j._requires = (((a,),),)
k._requires = (((a,),),)
l._requires = (((a,),),)
m._requires = (((a,),),)
o._requires = (((a,),),)
p._requires = (((a,),),)
q._requires = (((a,),),)
a._name = 'a'
b._name = 'b'
c._name = 'c'
d._name = 'd'
e._name = 'e'
g._name = 'g'
h._name = 'h'
i._name = 'i'
j._name = 'j'
k._name = 'k'
l._name = 'l'
m._name = 'm'
o._name = 'o'
p._name = 'p'
q._name = 'q'
Config(m)
raises(AttributeError, "a._requires = 'a'")
raises(AttributeError, "b._requires = 'b'")

View file

@ -14,8 +14,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Configuration management library written in python
"""
from .function import tiramisu_copy, calc_value, calc_value_property_help
from .autolib import Calculation, Params, ParamOption, ParamSelfOption, ParamValue, ParamIndex, ParamContext
from .function import calc_value, calc_value_property_help, valid_ip_netmask, \
valid_network_netmask, valid_in_network, valid_broadcast, \
valid_not_equal
from .autolib import Calculation, Params, ParamOption, ParamSelfOption, ParamValue, \
ParamIndex, ParamContext, ParamSuffix
from .option import *
from .error import APIError
from .api import Config, MetaConfig, GroupConfig, MixConfig
@ -32,6 +35,7 @@ allfuncs = ['Calculation',
'ParamValue',
'ParamIndex',
'ParamContext',
'ParamSuffix',
'MetaConfig',
'MixConfig',
'GroupConfig',
@ -44,9 +48,13 @@ allfuncs = ['Calculation',
'Storage',
'list_sessions',
'delete_session',
'tiramisu_copy',
'calc_value',
'calc_value_property_help']
'calc_value_property_help',
'valid_ip_netmask',
'valid_network_netmask',
'valid_in_network',
'valid_broadcast',
]
allfuncs.extend(all_options)
del(all_options)
__all__ = tuple(allfuncs)

View file

@ -196,11 +196,6 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
option = self._option_bag.option
return option.impl_has_dependency(self_is_dep)
def requires(self):
"""Get requires for an option"""
option = self._option_bag.option
return option.impl_getrequires()
def isoptiondescription(self):
"""Test if option is an optiondescription"""
option = self._option_bag.option
@ -274,11 +269,6 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
ret = value
return ret
def consistencies(self):
"""Get consistencies for an option (not for optiondescription)"""
option = self._option_bag.option
return option.get_consistencies()
def callbacks(self):
"""Get callbacks for an option (not for optiondescription)"""
option = self._option_bag.option

View file

@ -84,10 +84,14 @@ class ParamOption(Param):
class ParamSelfOption(Param):
__slots__ = ('todict',)
__slots__ = ('todict', 'whole')
def __init__(self,
todict: bool=False) -> None:
todict: bool=False,
whole: bool=undefined) -> None:
"""whole: send all value for a multi, not only indexed value"""
self.todict = todict
if whole is not undefined:
self.whole = whole
class ParamValue(Param):
@ -104,15 +108,21 @@ class ParamIndex(Param):
__slots__ = tuple()
class ParamSuffix(Param):
__slots__ = tuple()
class Calculation:
__slots__ = ('function',
'params',
'help_function',
'has_index')
'has_index',
'warnings_only')
def __init__(self,
function: Callable,
params: Params=Params(),
help_function: Optional[Callable]=None):
help_function: Optional[Callable]=None,
warnings_only: bool=False):
assert isinstance(function, Callable), _('first argument ({0}) must be a function').format(function)
if help_function:
assert isinstance(help_function, Callable), _('help_function ({0}) must be a function').format(help_function)
@ -127,19 +137,22 @@ class Calculation:
break
else:
self.has_index = False
if warnings_only is True:
self.warnings_only = warnings_only
def execute(self,
option_bag: OptionBag,
leadership_must_have_index: bool=False) -> Any:
if leadership_must_have_index and not self.has_index:
leadership_must_have_index = False
leadership_must_have_index: bool=False,
orig_value: Any=undefined,
allow_raises=False) -> Any:
return carry_out_calculation(option_bag.option,
callback=self.function,
callback_params=self.params,
index=option_bag.index,
config_bag=option_bag.config_bag,
fromconsistency=option_bag.fromconsistency,
leadership_must_have_index=leadership_must_have_index)
leadership_must_have_index=leadership_must_have_index,
orig_value=orig_value,
allow_raises=allow_raises)
def help(self,
option_bag: OptionBag,
@ -147,14 +160,11 @@ class Calculation:
if not self.help_function:
return self.execute(option_bag,
leadership_must_have_index=leadership_must_have_index)
if leadership_must_have_index and not self.has_index:
leadership_must_have_index = False
return carry_out_calculation(option_bag.option,
callback=self.help_function,
callback_params=self.params,
index=option_bag.index,
config_bag=option_bag.config_bag,
fromconsistency=option_bag.fromconsistency,
leadership_must_have_index=leadership_must_have_index)
@ -167,49 +177,58 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
index: Optional[int],
orig_value,
config_bag: ConfigBag,
fromconsistency: List,
leadership_must_have_index: bool) -> Any:
"""replace Param by true value"""
if isinstance(callbk, ParamValue):
return callbk.value
if isinstance(callbk, ParamIndex):
return index
if config_bag is undefined:
return undefined
if isinstance(callbk, ParamContext):
# Not an option, set full context
return config_bag.context.duplicate(force_values=get_default_values_storages(),
force_settings=get_default_settings_storages())
if isinstance(callbk, ParamSelfOption):
opt = option
else:
# it's ParamOption
opt = callbk.option
if opt.issubdyn():
opt = opt.to_dynoption(option.rootpath,
option.impl_getsuffix())
path = opt.impl_getpath()
is_follower = opt.impl_is_follower()
if leadership_must_have_index and opt.impl_get_leadership() and index is None:
raise Break()
if index is not None and opt.impl_get_leadership() and \
opt.impl_get_leadership().in_same_group(option):
if opt == option:
index_ = None
with_index = False
elif is_follower:
index_ = index
with_index = False
else:
index_ = None
with_index = True
else:
index_ = None
with_index = False
if opt == option and orig_value is not undefined and \
(not is_follower or index is None):
value = orig_value
else:
def calc_index(callbk, index, same_leadership):
if index is not None:
if hasattr(callbk, 'whole'):
whole = callbk.whole
else:
# if value is same_leadership, follower are isolate by default
# otherwise option is a whole option
whole = not same_leadership
if not whole:
return index
return None
def calc_self(callbk, option, index, value, config_bag):
# index must be apply only if follower
is_follower = option.impl_is_follower()
apply_index = calc_index(callbk, index, is_follower)
if value is undefined or (apply_index is None and is_follower):
if config_bag is undefined:
return undefined
path = option.impl_getpath()
option_bag = get_option_bag(config_bag, option, path, apply_index)
option_bag.config_bag.unrestraint()
option_bag.config_bag.remove_validation()
# if we are in properties calculation, cannot calculated properties
option_bag.properties = config_bag.context.cfgimpl_get_settings().getproperties(option_bag,
apply_requires=False)
new_value = get_value(callbk, option_bag, path)
if apply_index is None and is_follower:
new_value[index] = value
value = new_value
elif apply_index is not None and not is_follower:
value = value[apply_index]
return value
def get_value(callbk, option_bag, path):
try:
# get value
value = config_bag.context.getattr(path,
option_bag)
except PropertiesOptionError as err:
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
if callbk.notraisepropertyerror or callbk.raisepropertyerror:
raise err
raise ConfigError(_('unable to carry out a calculation for "{}"'
', {}').format(option.impl_get_display_name(), err), err)
except ValueError as err:
raise ValueError(_('the option "{0}" is used in a calculation but is invalid ({1})').format(option_bag.option.impl_get_display_name(), err))
return value
def get_option_bag(config_bag, opt, path, index_):
# don't validate if option is option that we tried to validate
config_bag = config_bag.copy()
config_bag.properties = config_bag.true_properties - {'warnings'}
@ -220,29 +239,64 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
path,
index_,
config_bag)
if fromconsistency:
option_bag.fromconsistency = fromconsistency.copy()
if opt == option:
option_bag.config_bag.unrestraint()
option_bag.config_bag.remove_validation()
# if we are in properties calculation, cannot calculated properties
option_bag.properties = config_bag.context.cfgimpl_get_settings().getproperties(option_bag,
apply_requires=False)
try:
# get value
value = config_bag.context.getattr(path,
option_bag)
if with_index:
value = value[index]
except PropertiesOptionError as err:
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
if callbk.notraisepropertyerror or callbk.raisepropertyerror:
raise err
raise ConfigError(_('unable to carry out a calculation for "{}"'
', {}').format(option.impl_get_display_name(), err), err)
return option_bag
if isinstance(callbk, ParamValue):
return callbk.value
if isinstance(callbk, ParamIndex):
return index
if isinstance(callbk, ParamSuffix):
if not option.issubdyn():
raise ConfigError('option "{}" is not in a dynoptiondescription'.format(option.impl_get_display_name()))
return option.impl_getsuffix()
if isinstance(callbk, ParamContext):
if config_bag is undefined:
return undefined
# Not an option, set full context
return config_bag.context.duplicate(force_values=get_default_values_storages(),
force_settings=get_default_settings_storages())
if isinstance(callbk, ParamSelfOption):
if leadership_must_have_index and option.impl_get_leadership() and index is None:
raise Break()
value = calc_self(callbk, option, index, orig_value, config_bag)
if not callbk.todict:
return value
return {'name': option.impl_get_display_name(),
'value': value}
# it's ParamOption
callbk_option = callbk.option
if callbk_option.issubdyn():
callbk_option = callbk_option.to_dynoption(option.rootpath,
option.impl_getsuffix())
if leadership_must_have_index and callbk_option.impl_get_leadership() and index is None:
raise Break()
if config_bag is undefined:
return undefined
if index is not None and callbk_option.impl_get_leadership() and \
callbk_option.impl_get_leadership().in_same_group(option):
if not callbk_option.impl_is_follower():
# leader
index_ = None
with_index = True
else:
# follower
index_ = index
with_index = False
else:
index_ = None
with_index = False
path = callbk_option.impl_getpath()
option_bag = get_option_bag(config_bag, callbk_option, path, index_)
value = get_value(callbk, option_bag, path)
if with_index:
value = value[index]
if not callbk.todict:
return value
return {'name': opt.impl_get_display_name(),
return {'name': callbk_option.impl_get_display_name(),
'value': value}
@ -251,10 +305,9 @@ def carry_out_calculation(option,
callback_params: Optional[Params],
index: Optional[int],
config_bag: Optional[ConfigBag],
fromconsistency: List,
orig_value=undefined,
leadership_must_have_index: bool=False,
is_validator: int=False):
allow_raises: int=False):
"""a function that carries out a calculation for an option's value
:param option: the option
@ -265,109 +318,16 @@ def carry_out_calculation(option,
:type callback_params: dict
:param index: if an option is multi, only calculates the nth value
:type index: int
:param is_validator: to know if carry_out_calculation is used to validate a value
:param allow_raises: to know if carry_out_calculation is used to validate a value
The callback_params is a dict. Key is used to build args (if key is '')
and kwargs (otherwise). Values are tuple of:
- values
- tuple with option and boolean's force_permissive (True when don't raise
if PropertiesOptionError)
Values could have multiple values only when key is ''.
* if no callback_params:
=> calculate(<function func at 0x2092320>, [], {})
* if callback_params={'': ('yes',)}
=> calculate(<function func at 0x2092320>, ['yes'], {})
* if callback_params={'value': ('yes',)}
=> calculate(<function func at 0x165b320>, [], {'value': 'yes'})
* if callback_params={'': ('yes', 'no')}
=> calculate('yes', 'no')
* if callback_params={'value': ('yes', 'no')}
=> ValueError()
* if callback_params={'': (['yes', 'no'],)}
=> calculate(<function func at 0x176b320>, ['yes', 'no'], {})
* if callback_params={'value': ('yes', 'no')}
=> raises ValueError()
* if callback_params={'': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(<function func at 0x1cea320>, [11], {})
- a multi option and not leadership
opt1 == [1, 2, 3]
=> calculate(<function func at 0x223c320>, [[1, 2, 3]], {})
- option is leader or follower of opt1:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x223c320>, [1], {})
=> calculate(<function func at 0x223c320>, [2], {})
=> calculate(<function func at 0x223c320>, [3], {})
- opt is a leader or follower but not related to option:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x11b0320>, [[1, 2, 3]], {})
* if callback_params={'value': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(<function func at 0x17ff320>, [], {'value': 11})
- a multi option:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x1262320>, [], {'value': [1, 2, 3]})
* if callback_params={'': ((opt1, False), (opt2, False))}
- two single options
opt1 = 11
opt2 = 12
=> calculate(<function func at 0x217a320>, [11, 12], {})
- a multi option with a simple option
opt1 == [1, 2, 3]
opt2 == 12
=> calculate(<function func at 0x2153320>, [[1, 2, 3], 12], {})
- a multi option with an other multi option but with same length
opt1 == [1, 2, 3]
opt2 == [11, 12, 13]
=> calculate(<function func at 0x1981320>, [[1, 2, 3], [11, 12, 13]], {})
- a multi option with an other multi option but with different length
opt1 == [1, 2, 3]
opt2 == [11, 12]
=> calculate(<function func at 0x2384320>, [[1, 2, 3], [11, 12]], {})
- a multi option without value with a simple option
opt1 == []
opt2 == 11
=> calculate(<function func at 0xb65320>, [[], 12], {})
* if callback_params={'value': ((opt1, False), (opt2, False))}
=> raises ValueError()
If index is not undefined, return a value, otherwise return:
* a list if one parameters have multi option
* a value otherwise
If calculate return list, this list is extend to return value.
"""
Values could have multiple values only when key is ''."""
args = []
kwargs = {}
# if callback_params has a callback, launch several time calculate()
if option.issubdyn():
#FIXME why here? should be a ParamSuffix !
kwargs['suffix'] = option.impl_getsuffix()
if callback_params:
for callbk in callback_params.args:
try:
@ -376,7 +336,6 @@ def carry_out_calculation(option,
index,
orig_value,
config_bag,
fromconsistency,
leadership_must_have_index)
if value is undefined:
return undefined
@ -395,7 +354,6 @@ def carry_out_calculation(option,
index,
orig_value,
config_bag,
fromconsistency,
leadership_must_have_index)
if value is undefined:
return undefined
@ -409,7 +367,7 @@ def carry_out_calculation(option,
continue
ret = calculate(option,
callback,
is_validator,
allow_raises,
args,
kwargs)
if isinstance(ret, list) and not option.impl_is_dynoptiondescription() and \
@ -434,7 +392,7 @@ def carry_out_calculation(option,
def calculate(option,
callback: Callable,
is_validator: bool,
allow_raises: bool,
args,
kwargs):
"""wrapper that launches the 'callback'
@ -447,7 +405,7 @@ def calculate(option,
try:
return callback(*args, **kwargs)
except ValueError as err:
if is_validator:
if allow_raises:
raise err
error = err
except Exception as err:

View file

@ -48,8 +48,7 @@ class SubConfig(object):
descr,
context,
config_bag,
subpath=None,
fromconsistency=None):
subpath=None):
""" Configuration option management class
:param descr: describes the configuration schema
@ -82,8 +81,6 @@ class SubConfig(object):
full_leaderpath,
None,
cconfig_bag)
if fromconsistency:
moption_bag.fromconsistency = fromconsistency
value = self.getattr(leaderpath,
moption_bag)
self._impl_length = len(value)
@ -179,8 +176,7 @@ class SubConfig(object):
def cfgimpl_get_home_by_path(self,
path,
config_bag,
fromconsistency=None):
config_bag):
""":returns: tuple (config, name)"""
path = path.split('.')
for step in path[:-1]:
@ -193,8 +189,6 @@ class SubConfig(object):
subpath,
None,
config_bag)
if fromconsistency is not None:
option_bag.fromconsistency = fromconsistency
self = self.get_subconfig(option_bag)
assert isinstance(self, SubConfig), _('unknown option {}').format(path[-1])
return self, path[-1]
@ -253,17 +247,11 @@ class SubConfig(object):
def get_subconfig(self,
option_bag):
if option_bag.fromconsistency:
fromconsistency = option_bag.fromconsistency.copy()
else:
fromconsistency = None
self.cfgimpl_get_settings().validate_properties(option_bag)
return SubConfig(option_bag.option,
self._impl_context,
option_bag.config_bag,
option_bag.path,
fromconsistency)
option_bag.path)
def getattr(self,
name,
@ -276,13 +264,8 @@ class SubConfig(object):
"""
config_bag = option_bag.config_bag
if '.' in name:
if option_bag.fromconsistency:
fromconsistency = option_bag.fromconsistency.copy()
else:
fromconsistency = None
self, name = self.cfgimpl_get_home_by_path(name,
config_bag,
fromconsistency)
config_bag)
option = option_bag.option
if option.impl_is_symlinkoption():
@ -309,8 +292,7 @@ class SubConfig(object):
length,
option_bag.index))
if option.impl_is_follower() and option_bag.index is None:
needs_re_verify_follower_properties = option_bag.option.impl_getrequires() or \
self.cfgimpl_get_settings().has_properties_index(option_bag)
needs_re_verify_follower_properties = self.cfgimpl_get_settings().has_properties_index(option_bag)
value = []
for idx in range(length):
soption_bag = OptionBag()
@ -318,7 +300,6 @@ class SubConfig(object):
option_bag.path,
idx,
config_bag)
soption_bag.fromconsistency = option_bag.fromconsistency.copy()
try:
value.append(self.getattr(name,
soption_bag,
@ -860,7 +841,6 @@ class KernelGroupConfig(_CommonConfig):
err.proptype,
err._settings,
err._opt_type,
err._requires,
err._name,
err._orig_opt))
except (ValueError, LeadershipError, AttributeError) as err:
@ -1069,7 +1049,6 @@ class KernelMixConfig(KernelGroupConfig):
err.proptype,
err._settings,
err._opt_type,
err._requires,
err._name,
err._orig_opt))
except (ValueError, LeadershipError, AttributeError) as err:

View file

@ -58,12 +58,10 @@ class PropertiesOptionError(AttributeError):
proptype,
settings,
opt_type=None,
requires=None,
name=None,
orig_opt=None):
if opt_type:
self._opt_type = opt_type
self._requires = requires
self._name = name
self._orig_opt = orig_opt
else:
@ -71,7 +69,6 @@ class PropertiesOptionError(AttributeError):
self._opt_type = 'optiondescription'
else:
self._opt_type = 'option'
self._requires = option_bag.option.impl_getrequires()
self._name = option_bag.option.impl_get_display_name()
self._orig_opt = None
self._option_bag = option_bag
@ -84,30 +81,21 @@ class PropertiesOptionError(AttributeError):
self._orig_opt = opt
def __str__(self):
#this part is a bit slow, so only execute when display
# this part is a bit slow, so only execute when display
if self.msg is not None:
return self.msg
if self._settings is None:
return 'error'
req = self._settings.apply_requires(self._option_bag,
True)
if req != {}:
only_one = len(req) == 1
msg = []
for action, msg_ in req.items():
msg.append('"{0}" ({1})'.format(action, display_list(msg_, add_quote=False)))
msg = display_list(msg, add_quote=False)
else:
properties = list(self._settings.calc_raises_properties(self._option_bag,
apply_requires=False))
for property_ in self._settings.get_calculated_properties(self._option_bag):
properties.append(property_.help(self._option_bag))
properties = list(self._settings.calc_raises_properties(self._option_bag,
apply_requires=False))
for property_ in self._settings.get_calculated_properties(self._option_bag):
properties.append(property_.help(self._option_bag))
if not properties:
# if proptype == ['mandatory']
properties = self.proptype
only_one = len(properties) == 1
msg = display_list(properties, add_quote=True)
if not properties:
# if proptype == ['mandatory']
properties = self.proptype
only_one = len(properties) == 1
msg = display_list(properties, add_quote=True)
if only_one:
prop_msg = _('property')
else:
@ -125,7 +113,7 @@ class PropertiesOptionError(AttributeError):
self._name,
prop_msg,
msg))
del self._requires, self._opt_type, self._name
del self._opt_type, self._name
del self._settings, self._orig_opt
return self.msg
@ -150,13 +138,6 @@ class ConflictError(Exception):
#____________________________________________________________
# miscellaneous exceptions
class RequirementError(Exception):
"""a recursive loop occurs in the requirements tree
requires
"""
pass
class LeadershipError(Exception):
"problem with a leadership's value length"
pass

View file

@ -14,13 +14,144 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Any, List, Optional
from operator import add, mul, sub, truediv
from ipaddress import ip_address, ip_interface, ip_network
from .i18n import _
from .setting import undefined
from .error import display_list
def tiramisu_copy(val): # pragma: no cover
return val
def valid_network_netmask(network: str,
netmask: str):
"""FIXME
"""
if isinstance(network, dict):
network_value = network['value']
network_display_name = '({})'.format(network['name'])
else:
network_value = network
network_display_name = ''
if None in [network_value, netmask]:
return
try:
ip_network('{0}/{1}'.format(network_value, netmask))
except ValueError:
raise ValueError(_('network "{0}" {1}does not match with this netmask').format(network_value,
network_display_name))
def valid_ip_netmask(ip: str,
netmask: str):
if isinstance(ip, dict):
ip_value = ip['value']
ip_display_name = '({})'.format(ip['name'])
else:
ip_value = ip
ip_display_name = ''
if None in [ip_value, netmask]:
return
ip_netmask = ip_interface('{0}/{1}'.format(ip_value, netmask))
if ip_netmask.ip == ip_netmask.network.network_address:
raise ValueError(_('IP \"{0}\" {1}with this netmask is in fact a network address').format(ip_value, ip_display_name))
elif ip_netmask.ip == ip_netmask.network.broadcast_address:
raise ValueError(_('IP \"{0}\" {1}with this netmask is in fact a broacast address').format(ip_value, ip_display_name))
# FIXME CIDR ?
def valid_broadcast(network: 'NetworkOption',
netmask: 'NetmaskOption',
broadcast: 'BroadcastOption'):
if isinstance(network, dict):
network_value = network['value']
network_display_name = ' ({})'.format(network['name'])
else:
network_value = network
network_display_name = ''
if isinstance(netmask, dict):
netmask_value = netmask['value']
netmask_display_name = ' ({})'.format(netmask['name'])
else:
netmask_value = netmask
netmask_display_name = ''
if ip_network('{0}/{1}'.format(network, netmask)).broadcast_address != ip_address(broadcast):
raise ValueError(_('broadcast invalid with network {0}{1} and netmask {2}{3}'
'').format(network_value,
network_display_name,
netmask_value,
netmask_display_name))
def valid_in_network(ip,
network,
netmask=None):
if isinstance(network, dict):
network_value = network['value']
network_display_name = ' ({})'.format(network['name'])
else:
network_value = network
network_display_name = ''
if netmask is None:
network_obj = ip_network('{0}'.format(network_value))
else:
if isinstance(netmask, dict):
netmask_value = netmask['value']
netmask_display_name = ' ({})'.format(netmask['name'])
else:
netmask_value = netmask
netmask_display_name = ''
network_obj = ip_network('{0}/{1}'.format(network_value,
netmask_value))
if ip_interface(ip) not in network_obj:
if netmask is None:
msg = _('this IP is not in network {0}{1}').format(network_value,
network_display_name)
else:
msg = _('this IP is not in network {0}{1} with netmask {2}{3}').format(network_value,
network_display_name,
netmask_value,
netmask_display_name)
raise ValueError(msg)
# test if ip is not network/broadcast IP
ip_netmask = ip_interface('{0}/{1}'.format(ip, network_obj.netmask))
if ip_netmask.ip == ip_netmask.network.network_address:
if netmask is None:
msg = _('this IP with the network {0}{1} is in fact a network address').format(network_value,
network_display_name)
else:
msg = _('this IP with the netmask {0}{1} is in fact a network address').format(netmask_value,
netmask_display_name)
raise ValueError(msg)
elif ip_netmask.ip == ip_netmask.network.broadcast_address:
if netmask is None:
msg = _('this IP with the network {0}{1} is in fact a broadcast address').format(network_value,
network_display_name)
else:
msg = _('this IP with the netmask {0}{1} is in fact a broadcast address').format(netmask_value,
netmask_display_name)
raise ValueError(msg)
def valid_not_equal(*values):
equal = set()
for idx, val in enumerate(values[1:]):
if isinstance(val, dict):
if 'propertyerror' in val:
continue
tval = val['value']
else:
tval = val
if values[0] == tval is not None:
if isinstance(val, dict):
if equal is True:
equal = set()
equal.add(val['name'])
elif not equal:
equal = True
if equal:
if equal is not True:
msg = _('value is identical to {}').format(display_list(list(equal), add_quote=True))
else:
msg = _('value is identical')
raise ValueError(msg)
class CalcValue:

View file

@ -51,14 +51,8 @@ class Base:
__slots__ = ('_name',
'_path',
'_informations',
#calcul
'_subdyn',
'_requires',
'_properties',
'_calc_properties',
#
'_consistencies',
#other
'_has_dependency',
'_dependencies',
'_has_calc_context',
@ -68,19 +62,10 @@ class Base:
def __init__(self,
name: str,
doc: str,
requires=None,
properties=None,
is_multi: bool=False) -> None:
if not valid_name(name):
raise ValueError(_('"{0}" is an invalid name for an option').format(name))
if requires is not None:
calc_properties, requires = validate_requires_arg(self,
is_multi,
requires,
name)
else:
calc_properties = frozenset()
requires = undefined
if properties is None:
properties = frozenset()
elif isinstance(properties, tuple):
@ -92,153 +77,18 @@ class Base:
assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},'
' must be a frozenset').format(type(properties),
name)
self.validate_properties(name,
calc_properties,
properties)
_setattr = object.__setattr__
_setattr(self, '_name', name)
_setattr(self, '_informations', {'doc': doc})
if calc_properties is not undefined:
_setattr(self, '_calc_properties', calc_properties)
if requires is not undefined:
_setattr(self, '_requires', requires)
if properties:
_setattr(self, '_properties', properties)
def validate_properties(self,
name: str,
calc_properties: FrozenSet[str],
properties: FrozenSet[str]) -> None:
set_forbidden_properties = calc_properties & properties
if set_forbidden_properties != frozenset():
raise ValueError(_('conflict: properties already set in requirement {0} for {1}'
'').format(display_list(set_forbidden_properties, add_quote=True),
name))
assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},'
' must be a frozenset').format(type(properties),
name)
for prop in properties:
if not isinstance(prop, str):
if not isinstance(prop, Calculation):
raise ValueError(_('invalid property type {0} for {1}, must be a string or a Calculation').format(type(prop), name))
params = prop.params
for param in chain(params.args, params.kwargs.values()):
for param in chain(prop.params.args, prop.params.kwargs.values()):
if isinstance(param, ParamOption):
param.option._add_dependency(self)
def _get_function_args(self,
function: Callable) -> Tuple[Set[str], Set[str], bool, bool]:
args = set()
kwargs = set()
positional = False
keyword = False
for param in signature(function).parameters.values():
if param.kind == param.VAR_POSITIONAL:
positional = True
elif param.kind == param.VAR_KEYWORD:
keyword = True
elif param.default is param.empty:
args.add(param.name)
else:
kwargs.add(param.name)
return args, kwargs, positional, keyword
def _get_parameters_args(self,
calculator_params: Optional[Params],
add_value: bool) -> Tuple[Set[str], Set[str]]:
args = set()
kwargs = set()
# add value as first argument
if add_value:
args.add('value')
if self.impl_is_dynoptiondescription():
kwargs.add('suffix')
if calculator_params:
for idx in range(len(calculator_params.args)):
# construct an appropriate name
args.add('param{}'.format(idx))
for param in calculator_params.kwargs:
kwargs.add(param)
return args, kwargs
def _build_calculator_params(self,
calculator: Callable,
calculator_params: Optional[Params],
type_: str,
add_value: bool=False) -> Union[None, Params]:
"""
:add_value: add value as first argument for validator
"""
assert isinstance(calculator, Callable), _('{0} must be a function').format(type_)
if calculator_params is not None:
assert isinstance(calculator_params, Params), _('{0}_params must be a params'
'').format(type_)
for param in chain(calculator_params.args, calculator_params.kwargs.values()):
if isinstance(param, ParamContext):
self._has_calc_context = True
elif isinstance(param, ParamOption):
param.option._add_dependency(self)
if type_ == 'validator':
self._has_dependency = True
is_multi = self.impl_is_optiondescription() or self.impl_is_multi()
func_args, func_kwargs, func_positional, func_keyword = self._get_function_args(calculator)
calculator_args, calculator_kwargs = self._get_parameters_args(calculator_params, add_value)
# remove knowned kwargs
common_kwargs = func_kwargs & calculator_kwargs
func_kwargs -= common_kwargs
calculator_kwargs -= common_kwargs
# remove knowned calculator's kwargs in func's args
common = func_args & calculator_kwargs
func_args -= common
calculator_kwargs -= common
# remove unknown calculator's args in func's args
for idx in range(min(len(calculator_args), len(func_args))):
func_args.pop()
calculator_args.pop()
# remove unknown calculator's args in func's kwargs
if is_multi:
func_kwargs_left = func_kwargs - {'index', 'self'}
else:
func_kwargs_left = func_kwargs
func_kwargs_pop = set()
for idx in range(min(len(calculator_args), len(func_kwargs_left))):
func_kwargs_pop.add(func_kwargs_left.pop())
calculator_args.pop()
func_kwargs -= func_kwargs_pop
# func_positional or keyword is True, so assume all args or kwargs are satisfy
if func_positional:
calculator_args = set()
if func_keyword:
calculator_kwargs = set()
if calculator_args or calculator_kwargs:
# there is more args/kwargs than expected!
raise ConfigError(_('cannot find those arguments "{}" in function "{}" for "{}"'
'').format(display_list(list(calculator_args | calculator_kwargs)),
calculator.__name__,
self.impl_get_display_name()))
has_index = False
if is_multi and func_args and not self.impl_is_dynoptiondescription():
if calculator_params is None:
calculator_params = Params()
params = list(calculator_params.args)
if add_value:
# only for validator
params.append(ParamOption(self))
func_args.pop()
if func_args:
has_index = True
params.append(ParamIndex())
func_args.pop()
calculator_params.args = tuple(params)
if func_args:
raise ConfigError(_('missing those arguments "{}" in function "{}" for "{}"'
'').format(display_list(list(func_args)),
calculator.__name__,
self.impl_get_display_name()))
if not self.impl_is_dynoptiondescription() and is_multi and \
not has_index and 'index' in func_kwargs:
calculator_params.kwargs['index'] = ParamIndex()
return calculator_params
_setattr = object.__setattr__
_setattr(self, '_name', name)
_setattr(self, '_informations', {'doc': doc})
if properties:
_setattr(self, '_properties', properties)
def impl_has_dependency(self,
self_is_dep: bool=True) -> bool:
@ -323,9 +173,6 @@ class Base:
def getsubdyn(self):
return self._subdyn()
def impl_getrequires(self):
return getattr(self, '_requires', STATIC_TUPLE)
def impl_get_callback(self):
call = getattr(self, '_val_call', (None, None))[1]
if call is None:
@ -373,7 +220,6 @@ class Base:
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
" read-only").format(self.__class__.__name__,
self,
#self.impl_getname(),
key))
self._informations[key] = value
@ -445,181 +291,3 @@ class BaseOption(Base):
def impl_is_symlinkoption(self) -> bool:
return False
def validate_requires_arg(new_option: BaseOption,
multi: bool,
requires: List[dict],
name: str) -> Tuple[FrozenSet, Tuple]:
"""check malformed requirements
and tranform dict to internal tuple
:param requires: have a look at the
:meth:`tiramisu.setting.Settings.apply_requires` method to
know more about
the description of the requires dictionary
"""
def get_option(require):
if 'option' in require:
option = require['option']
if option == 'self':
option = new_option
if __debug__:
if not isinstance(option, BaseOption):
raise ValueError(_('malformed requirements '
'must be an option in option {0}').format(name))
if not multi and option.impl_is_multi():
raise ValueError(_('malformed requirements '
'multi option must not set '
'as requires of non multi option {0}').format(name))
option._add_dependency(new_option)
else:
callback = require['callback']
callback_params = new_option._build_calculator_params(callback,
require.get('callback_params'),
'callback')
option = (callback, callback_params)
return option
def _set_expected(action,
inverse,
transitive,
same_action,
option,
expected,
operator):
if inverse not in ret_requires[action]:
ret_requires[action][inverse] = ([(option, [expected])], action, inverse, transitive, same_action, operator)
else:
for exp in ret_requires[action][inverse][0]:
if exp[0] == option:
exp[1].append(expected)
break
else:
ret_requires[action][inverse][0].append((option, [expected]))
def set_expected(require,
ret_requires):
expected = require['expected']
inverse = get_inverse(require)
transitive = get_transitive(require)
same_action = get_sameaction(require)
operator = get_operator(require)
if isinstance(expected, list):
for exp in expected:
if __debug__ and set(exp.keys()) != {'option', 'value'}:
raise ValueError(_('malformed requirements expected must have '
'option and value for option {0}').format(name))
option = get_option(exp)
if __debug__:
try:
option._validate(exp['value'], undefined)
except ValueError as err:
raise ValueError(_('malformed requirements expected value '
'must be valid for option {0}'
': {1}').format(name, err))
_set_expected(action,
inverse,
transitive,
same_action,
option,
exp['value'],
operator)
else:
option = get_option(require)
if __debug__ and not isinstance(option, tuple) and expected is not None:
try:
option._validate(expected, undefined)
except ValueError as err:
raise ValueError(_('malformed requirements expected value '
'must be valid for option {0}'
': {1}').format(name, err))
_set_expected(action,
inverse,
transitive,
same_action,
option,
expected,
operator)
def get_action(require):
action = require['action']
if action == 'force_store_value':
raise ValueError(_("malformed requirements for option: {0}"
" action cannot be force_store_value"
).format(name))
return action
def get_inverse(require):
inverse = require.get('inverse', False)
if inverse not in [True, False]:
raise ValueError(_('malformed requirements for option: {0}'
' inverse must be boolean'))
return inverse
def get_transitive(require):
transitive = require.get('transitive', True)
if transitive not in [True, False]:
raise ValueError(_('malformed requirements for option: {0}'
' transitive must be boolean'))
return transitive
def get_sameaction(require):
same_action = require.get('same_action', True)
if same_action not in [True, False]:
raise ValueError(_('malformed requirements for option: {0}'
' same_action must be boolean'))
return same_action
def get_operator(require):
operator = require.get('operator', 'or')
if operator not in ['and', 'or']:
raise ValueError(_('malformed requirements for option: "{0}"'
' operator must be "or" or "and"').format(operator))
return operator
ret_requires = {}
config_action = set()
# start parsing all requires given by user (has dict)
# transforme it to a tuple
for require in requires:
if __debug__:
if not isinstance(require, dict):
raise ValueError(_("malformed requirements type for option:"
" {0}, must be a dict").format(name))
valid_keys = ('option', 'expected', 'action', 'inverse', 'transitive',
'same_action', 'operator', 'callback', 'callback_params')
unknown_keys = frozenset(require.keys()) - frozenset(valid_keys)
if unknown_keys != frozenset():
raise ValueError(_('malformed requirements for option: {0}'
' unknown keys {1}, must only '
'{2}').format(name,
unknown_keys,
valid_keys))
# {'expected': ..., 'option': ..., 'action': ...}
# {'expected': [{'option': ..., 'value': ...}, ...}], 'action': ...}
# {'expected': ..., 'callback': ..., 'action': ...}
if not 'expected' in require or not 'action' in require or \
not (isinstance(require['expected'], list) or \
'option' in require or \
'callback' in require):
raise ValueError(_("malformed requirements for option: {0}"
" require must have option, expected and"
" action keys").format(name))
action = get_action(require)
config_action.add(action)
if action not in ret_requires:
ret_requires[action] = {}
set_expected(require, ret_requires)
# transform dict to tuple
ret = []
for requires in ret_requires.values():
ret_action = []
for require in requires.values():
ret_action.append((tuple(require[0]), require[1],
require[2], require[3], require[4], require[5]))
ret.append(tuple(ret_action))
return frozenset(config_action), tuple(ret)

View file

@ -46,22 +46,3 @@ class BroadcastOption(Option):
ip_address(value)
except ValueError:
raise ValueError()
def _cons_broadcast(self,
current_opt,
opts,
vals,
warnings_only,
context):
if len(vals) != 3:
raise ConfigError(_('invalid broadcast consistency, a network and a netmask are needed'))
if None in vals:
return
broadcast, network, netmask = vals
if ip_network('{0}/{1}'.format(network, netmask)).broadcast_address != ip_address(broadcast):
raise ValueError(_('broadcast "{4}" invalid with network {0}/{1} ("{2}"/"{3}")'
'').format(network,
netmask,
opts[1].impl_get_display_name(),
opts[2].impl_get_display_name(),
broadcast))

View file

@ -23,7 +23,7 @@ from types import FunctionType
from ..setting import undefined
from ..i18n import _
from .option import Option
from ..autolib import carry_out_calculation
from ..autolib import carry_out_calculation, Calculation
from ..error import ConfigError, display_list
@ -41,83 +41,44 @@ class ChoiceOption(Option):
doc,
values,
default=None,
values_params=None,
default_multi=None,
requires=None,
multi=False,
callback=None,
callback_params=None,
validator=None,
validator_params=None,
validators=None,
properties=None,
warnings_only=False):
"""
:param values: is a list of values the option can possibly take
"""
if isinstance(values, FunctionType):
values_params = self._build_calculator_params(values,
values_params,
'values')
if values_params:
self._choice_values_params = values_params
else:
if values_params is not None:
raise ValueError(_('values is not a function, so values_params must be None'))
if not isinstance(values, tuple):
raise TypeError(_('values must be a tuple or a function for {0}'
).format(name))
if not isinstance(values, (Calculation, tuple)):
raise TypeError(_('values must be a tuple or a calculation for {0}'
).format(name))
self._choice_values = values
super(ChoiceOption, self).__init__(name,
doc,
default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_params=validator_params,
validators=validators,
properties=properties,
warnings_only=warnings_only)
def get_callback(self):
values = self._choice_values
if isinstance(values, FunctionType):
return (values, getattr(self, '_choice_values_params', {}))
else:
return (None, None)
def impl_get_values(self,
option_bag,
current_opt=undefined):
if current_opt is undefined:
current_opt = self
values, values_params = self.get_callback()
if values is not None:
if option_bag is undefined:
values = undefined
else:
values = carry_out_calculation(current_opt,
callback=values,
callback_params=values_params,
index=None,
config_bag=option_bag.config_bag,
fromconsistency=[])
if values is not undefined and not isinstance(values, list):
raise ConfigError(_('calculated values for {0} is not a list'
'').format(self.impl_getname()))
option_bag):
if isinstance(self._choice_values, Calculation):
values = self._choice_values.execute(option_bag)
if values is not undefined and not isinstance(values, list):
raise ConfigError(_('calculated values for {0} is not a list'
'').format(self.impl_getname()))
else:
values = self._choice_values
return values
def _validate(self,
value,
option_bag,
current_opt=undefined):
values = self.impl_get_values(option_bag,
current_opt=current_opt)
values = self.impl_get_values(option_bag)
if values is not undefined and value not in values:
if len(values) == 1:
raise ValueError(_('only "{0}" is allowed'

View file

@ -42,12 +42,8 @@ class DomainnameOption(IPOption):
doc,
default=None,
default_multi=None,
requires=None,
multi: bool=False,
callback=None,
callback_params=None,
validator=None,
validator_params=None,
validators=None,
properties=None,
allow_ip: bool=False,
cidr: bool=False,
@ -94,12 +90,8 @@ class DomainnameOption(IPOption):
doc,
default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_params=validator_params,
validators=validators,
properties=properties,
warnings_only=warnings_only,
cidr=cidr,

View file

@ -41,13 +41,11 @@ class DynOptionDescription(OptionDescription):
doc: str,
children: List[BaseOption],
suffixes: Calculation,
requires=None,
properties=None) -> None:
super().__init__(name,
doc,
children,
requires,
properties)
# check children + set relation to this dynoptiondescription
for child in children:

View file

@ -18,15 +18,14 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
from ipaddress import ip_address, ip_interface, ip_network
from ipaddress import ip_address, ip_interface
from ..error import ConfigError
from ..setting import undefined, Undefined, OptionBag
from ..i18n import _
from .option import Option
from .stroption import StrOption
from .netmaskoption import NetmaskOption
from .networkoption import NetworkOption
from ..function import valid_ip_netmask
class IPOption(StrOption):
@ -40,12 +39,8 @@ class IPOption(StrOption):
doc,
default=None,
default_multi=None,
requires=None,
multi=False,
callback=None,
callback_params=None,
validator=None,
validator_params=None,
validators=None,
properties=None,
private_only=False,
allow_reserved=False,
@ -63,12 +58,8 @@ class IPOption(StrOption):
doc,
default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_params=validator_params,
validators=validators,
properties=properties,
warnings_only=warnings_only,
extra=extra)
@ -98,9 +89,11 @@ class IPOption(StrOption):
if not cidr:
ip_address(value)
else:
ip_interface(value)
ip = ip_interface(value)
except ValueError:
raise ValueError()
if cidr:
valid_ip_netmask(str(ip.ip), str(ip.netmask))
def _second_level_validation(self,
value,
@ -118,68 +111,3 @@ class IPOption(StrOption):
else:
msg = _("must be private IP")
raise ValueError(msg)
if '/' in value:
net = NetmaskOption(self.impl_getname(),
self.impl_get_display_name(),
str(ip.netmask))
net._cons_ip_netmask(self,
(net, self),
(str(ip.netmask), str(ip.ip)),
warnings_only,
None,
True)
def _cons_in_network(self,
current_opt,
opts,
vals,
warnings_only,
context):
if len(opts) == 2 and isinstance(opts[0], IPOption) and \
opts[0].impl_get_extra('_cidr') == False and \
isinstance(opts[1], NetworkOption) and \
opts[1].impl_get_extra('_cidr') == True:
if None in vals:
return
ip, network = vals
network_obj = ip_network(network)
if ip_interface(ip) not in network_obj:
msg = _('IP not in network "{0}" ("{1}")')
raise ValueError(msg.format(network,
opts[1].impl_get_display_name()))
# test if ip is not network/broadcast IP
netmask = NetmaskOption(self.impl_getname(),
self.impl_get_display_name(),
str(network_obj.netmask))
netmask._cons_ip_netmask(self,
(netmask, self),
(str(network_obj.netmask), str(ip)),
warnings_only,
None,
True)
else:
if len(vals) != 3 and context is undefined:
raise ConfigError(_('ip_network needs an IP, a network and a netmask'))
if len(vals) != 3 or None in vals:
return
ip, network, netmask = vals
if ip_interface(ip) not in ip_network('{0}/{1}'.format(network,
netmask)):
if current_opt == opts[0]:
msg = _('IP not in network "{2}"/"{4}" ("{3}"/"{5}")')
elif current_opt == opts[1]:
msg = _('the network doest not match with IP "{0}" ("{1}") and network "{4}" ("{5}")')
else:
msg = _('the netmask does not match with IP "{0}" ("{1}") and broadcast "{2}" ("{3}")')
raise ValueError(msg.format(ip,
opts[0].impl_get_display_name(),
network,
opts[1].impl_get_display_name(),
netmask,
opts[2].impl_get_display_name()))
# test if ip is not network/broadcast IP
opts[2]._cons_ip_netmask(current_opt,
(opts[2], opts[0]),
(netmask, ip),
warnings_only,
context)

View file

@ -25,13 +25,13 @@ from typing import List, Iterator, Optional, Any
from ..i18n import _
from ..setting import groups, undefined, OptionBag, Settings
from ..setting import groups, undefined, OptionBag, Settings, ALLOWED_LEADER_PROPERTIES
from ..value import Values
from .optiondescription import OptionDescription
from .syndynoptiondescription import SynDynLeadership
from .baseoption import BaseOption
from .option import Option
from ..error import RequirementError
from ..error import LeadershipError
from ..autolib import Calculation, ParamOption
@ -43,12 +43,10 @@ class Leadership(OptionDescription):
name: str,
doc: str,
children: List[BaseOption],
requires=None,
properties=None) -> None:
super().__init__(name,
doc,
children,
requires=requires,
properties=properties)
self._group_type = groups.leadership
followers = []
@ -93,28 +91,9 @@ class Leadership(OptionDescription):
raise ValueError(_("callback of leader's option shall "
"not refered to a follower's ones"))
# leader should not have requires, only Leadership should have
# so move requires to Leadership
# if Leadership has requires too, cannot manage this move so raises
leader_requires = getattr(leader, '_requires', None)
if leader_requires:
if __debug__ and self.impl_getrequires():
raise RequirementError(_('leader {} have requirement, but Leadership {} too'
'').format(leader.impl_getname(),
self.impl_getname()))
leader_calproperties = getattr(leader, '_requires', None)
if leader_calproperties:
setattr(self, '_requires', leader_requires)
delattr(leader, '_requires')
if __debug__:
for requires_ in getattr(self, '_requires', ()):
for require in requires_:
for require_opt, values in require[0]:
if not isinstance(require_opt, tuple) and require_opt.impl_is_multi() and require_opt.impl_get_leadership():
raise ValueError(_('malformed requirements option "{0}" '
'must not be in follower for "{1}"').format(
require_opt.impl_getname(),
self.impl_getname()))
for prop in leader.impl_getproperties():
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
raise LeadershipError(_('leader cannot have "{}" property').format(prop))
def is_leader(self,
opt: Option) -> bool:

View file

@ -49,58 +49,3 @@ class NetmaskOption(StrOption):
ip_network('0.0.0.0/{0}'.format(value))
except ValueError:
raise ValueError()
def _cons_network_netmask(self,
current_opt: Option,
opts: List[Option],
vals: List[str],
warnings_only: bool,
context: 'Config'):
if context is undefined and len(vals) != 2:
raise ConfigError(_('network_netmask needs a network and a netmask'))
if None in vals or len(vals) != 2:
return
val_netmask, val_network = vals
opt_netmask, opt_network = opts
try:
ip_network('{0}/{1}'.format(val_network, val_netmask))
except ValueError:
if current_opt == opt_network:
raise ValueError(_('the netmask "{0}" ("{1}") does not match').format(val_netmask,
opt_netmask.impl_get_display_name()))
else:
raise ValueError(_('the network "{0}" ("{1}") does not match').format(val_network,
opt_network.impl_get_display_name()))
def _cons_ip_netmask(self,
current_opt: Option,
opts: List[Option],
vals: List[str],
warnings_only: bool,
context: 'config',
_cidr: bool=False):
if context is undefined and len(vals) != 2:
raise ConfigError(_('ip_netmask needs an IP and a netmask'))
if None in vals or len(vals) != 2:
return
val_netmask, val_ip = vals
opt_netmask, opt_ip = opts
ip = ip_interface('{0}/{1}'.format(val_ip, val_netmask))
if not _cidr and current_opt == opt_ip:
if ip.ip == ip.network.network_address:
raise ValueError( _('this is a network with netmask "{0}" ("{1}")'
'').format(val_netmask,
opt_netmask.impl_get_display_name()))
elif ip.ip == ip.network.broadcast_address:
raise ValueError(_('this is a broadcast with netmask "{0}" ("{1}")'
'').format(val_netmask,
opt_netmask.impl_get_display_name()))
else:
if ip.ip == ip.network.network_address:
raise ValueError(_('IP "{0}" ("{1}") is the network'
'').format(val_ip,
opt_ip.impl_get_display_name()))
elif ip.ip == ip.network.broadcast_address:
raise ValueError(_('IP "{0}" ("{1}") is the broadcast'
'').format(val_ip,
opt_ip.impl_get_display_name()))

View file

@ -36,12 +36,8 @@ class NetworkOption(Option):
doc,
default=None,
default_multi=None,
requires=None,
multi=False,
callback=None,
callback_params=None,
validator=None,
validator_params=None,
validators=None,
properties=None,
warnings_only=False,
cidr=False):
@ -50,12 +46,8 @@ class NetworkOption(Option):
doc,
default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_params=validator_params,
validators=validators,
properties=properties,
warnings_only=warnings_only,
extra=extra)

View file

@ -27,11 +27,11 @@ from itertools import chain
from .baseoption import BaseOption, submulti, STATIC_TUPLE
from ..i18n import _
from ..setting import undefined, OptionBag, Undefined
from ..autolib import Calculation, carry_out_calculation, Params, ParamValue, ParamContext, ParamOption
from ..autolib import Calculation, Params, ParamValue, ParamContext, ParamOption
from ..error import (ConfigError, ValueWarning, ValueErrorWarning, PropertiesOptionError,
ValueOptionError, display_list)
from .syndynoption import SynDynOption
ALLOWED_CONST_LIST = ['_cons_not_equal']
#ALLOWED_CONST_LIST = ['_cons_not_equal']
class Option(BaseOption):
@ -43,14 +43,14 @@ class Option(BaseOption):
__slots__ = ('_extra',
'_warnings_only',
'_allow_empty_list',
#multi
# multi
'_multi',
'_unique',
#value
# value
'_default',
'_default_multi',
#calcul
'_val_call',
#
'_validators',
#
'_leadership',
'_choice_values',
@ -62,13 +62,9 @@ class Option(BaseOption):
doc: str,
default: Any=undefined,
default_multi: Any=None,
requires: List[Dict]=None,
multi: bool=False,
unique: bool=undefined,
callback: Optional[Callable]=None,
callback_params: Optional[Params]=None,
validator: Optional[Callable]=None,
validator_params: Optional[Params]=None,
validators: Optional[List[Calculation]]=None,
properties: Optional[List[str]]=None,
warnings_only: bool=False,
extra: Optional[Dict]=None,
@ -99,19 +95,23 @@ class Option(BaseOption):
default = []
super().__init__(name,
doc,
requires=requires,
properties=properties,
is_multi=is_multi)
if validator is not None:
validator_params = self._build_calculator_params(validator,
validator_params,
'validator',
add_value=True)
if not validator_params:
val_call = (validator,)
else:
val_call = (validator, validator_params)
self._val_call = (val_call, None)
if __debug__:
if validators is not None:
if not isinstance(validators, list):
raise ValueError(_('validators must be a list of Calculation for "{}"').format(name))
for validator in validators:
if not isinstance(validator, Calculation):
raise ValueError(_('validators must be a Calculation for "{}"').format(name))
for param in chain(validator.params.args, validator.params.kwargs.values()):
if isinstance(param, ParamContext):
self._has_calc_context = True
elif isinstance(param, ParamOption):
param.option._add_dependency(self)
self._has_dependency = True
self._validators = tuple(validators)
if extra is not None and extra != {}:
_setattr(self, '_extra', extra)
if unique != undefined and not isinstance(unique, bool):
@ -126,9 +126,14 @@ class Option(BaseOption):
def test_multi_value(value):
if isinstance(value, Calculation):
return
option_bag = OptionBag()
option_bag.set_option(self,
undefined,
None,
undefined)
try:
self._validate(value,
undefined)
option_bag)
except ValueError as err:
str_err = str(err)
if not str_err:
@ -168,9 +173,6 @@ class Option(BaseOption):
default = tuple(default)
_setattr(self, '_default', default)
self._impl_set_callback(callback,
callback_params)
def value_dependencies(self,
value: Any) -> Any:
if isinstance(value, list):
@ -250,16 +252,6 @@ class Option(BaseOption):
#__________________________________________________________________________
# validator
def impl_get_validator(self) -> Tuple[Callable, Params]:
val = getattr(self, '_val_call', (None,))[0]
if val is None:
ret_val = (None, None)
elif len(val) == 1:
ret_val = (val[0], None)
else:
ret_val = val
return ret_val
def impl_validate(self,
value: Any,
option_bag: OptionBag,
@ -272,13 +264,9 @@ class Option(BaseOption):
if check_error and config_bag is not undefined and \
not 'validator' in config_bag.properties:
# just to check propertieserror
self.valid_consistency(option_bag,
value,
check_error,
is_warnings_only)
return
def _is_not_unique(value):
# if set(value) has not same length than value
if check_error and self.impl_is_unique() and \
@ -291,26 +279,33 @@ class Option(BaseOption):
def calculation_validator(val,
_index):
validator, validator_params = self.impl_get_validator()
if validator is not None:
#inject value in calculation
if validator_params is None:
args = []
kwargs = None
else:
args = list(validator_params.args)
kwargs = validator_params.kwargs
args.insert(0, ParamValue(val))
validator_params_ = Params(tuple(args), kwargs)
# Raise ValueError if not valid
carry_out_calculation(option_bag.ori_option,
callback=validator,
callback_params=validator_params_,
index=_index,
config_bag=option_bag.config_bag,
fromconsistency=option_bag.fromconsistency,
orig_value=value,
is_validator=True)
for validator in getattr(self, '_validators', []):
calc_is_warnings_only = hasattr(validator, 'warnings_only') and validator.warnings_only
if ((check_error and not calc_is_warnings_only) or
(not check_error and calc_is_warnings_only)):
try:
kwargs = {'allow_raises': True}
if _index is not None and option_bag.index == _index:
soption_bag = option_bag
else:
soption_bag = option_bag.copy()
soption_bag.index = _index
kwargs['orig_value'] = value
validator.execute(soption_bag,
leadership_must_have_index=True,
**kwargs)
except ValueError as err:
if calc_is_warnings_only:
warnings.warn_explicit(ValueWarning(val,
self._display_name,
self,
'{0}'.format(err),
_index),
ValueWarning,
self.__class__.__name__, 0)
else:
raise err
def do_validation(_value,
_index):
@ -328,8 +323,6 @@ class Option(BaseOption):
if ((check_error and not is_warnings_only) or
(not check_error and is_warnings_only)):
try:
calculation_validator(_value,
_index)
self._second_level_validation(_value,
is_warnings_only)
except ValueError as err:
@ -343,6 +336,8 @@ class Option(BaseOption):
self.__class__.__name__, 0)
else:
raise err
calculation_validator(_value,
_index)
try:
val = value
err_index = force_index
@ -379,12 +374,6 @@ class Option(BaseOption):
for err_index, val in enumerate(value):
do_validation(val,
err_index)
if (not is_warnings_only or not check_error) and not isinstance(value, Calculation):
self.valid_consistency(option_bag,
value,
check_error,
is_warnings_only)
except ValueError as err:
if config_bag is undefined or \
'demoting_error_warning' not in config_bag.properties:
@ -419,11 +408,6 @@ class Option(BaseOption):
warnings_only: bool) -> None:
pass
#__________________________________________________________________________
# leadership
# def impl_is_leadership(self):
# return self.impl_get_leadership() is not None
def impl_is_leader(self):
leadership = self.impl_get_leadership()
if leadership is None:
@ -442,327 +426,6 @@ class Option(BaseOption):
return leadership
return leadership()
#____________________________________________________________
# consistencies
def impl_add_consistency(self,
func: str,
*other_opts,
**params) -> None:
"""Add consistency means that value will be validate with other_opts
option's values.
:param func: function's name
:type func: `str`
:param other_opts: options used to validate value
:type other_opts: `list` of `tiramisu.option.Option`
:param params: extra params (warnings_only and transitive are allowed)
"""
if self.impl_is_readonly():
raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is"
" read-only").format(
self.__class__.__name__,
self.impl_getname()))
self._valid_consistencies(other_opts,
func=func)
func = '_cons_{0}'.format(func)
if func not in dir(self):
raise ConfigError(_('consistency {0} not available for this option').format(func))
options = [weakref.ref(self)]
for option in other_opts:
options.append(weakref.ref(option))
all_cons_opts = tuple(options)
unknown_params = set(params.keys()) - set(['warnings_only', 'transitive'])
if unknown_params != set():
raise ValueError(_('unknown parameter {0} in consistency').format(unknown_params))
self._add_consistency(func,
all_cons_opts,
params)
#validate default value when add consistency
option_bag = OptionBag()
option_bag.set_option(self,
undefined,
None,
undefined)
default = self.impl_getdefault()
if isinstance(default, tuple):
default = list(default)
self.impl_validate(default,
option_bag)
self.impl_validate(default,
option_bag,
check_error=False)
if func != '_cons_not_equal':
#consistency could generate warnings or errors
self._has_dependency = True
for wopt in all_cons_opts:
opt = wopt()
if func in ALLOWED_CONST_LIST:
if getattr(opt, '_unique', undefined) == undefined:
opt._unique = True
if opt != self:
self._add_dependency(opt)
opt._add_dependency(self)
def _add_consistency(self,
func: str,
all_cons_opts: List[BaseOption],
params: Dict) -> None:
cons = (-1, func, all_cons_opts, params)
consistencies = getattr(self, '_consistencies', None)
if consistencies is None:
self._consistencies = [cons]
else:
consistencies.append(cons)
def get_consistencies(self):
return getattr(self, '_consistencies', STATIC_TUPLE)
def has_consistencies(self, context) -> bool:
descr = context.cfgimpl_get_description()
if getattr(descr, '_cache_consistencies', None) is None:
return False
return self in descr._cache_consistencies
def valid_consistency(self,
option_bag: OptionBag,
value: Any,
check_error: bool,
option_warnings_only: bool) -> None:
if option_bag.config_bag is not undefined:
descr = option_bag.config_bag.context.cfgimpl_get_description()
# no consistency found at all
if getattr(descr, '_cache_consistencies', None) is None:
return
# get consistencies for this option
consistencies = descr._cache_consistencies.get(option_bag.option)
else:
# is no context, get consistencies in option
consistencies = option_bag.option.get_consistencies()
if consistencies:
if option_bag.config_bag is undefined:
coption_bag = option_bag.copy()
else:
cconfig_bag = option_bag.config_bag.copy()
cconfig_bag.remove_warnings()
cconfig_bag.set_permissive()
coption_bag = option_bag.copy()
coption_bag.config_bag = cconfig_bag
if not option_bag.fromconsistency:
fromconsistency_is_empty = True
option_bag.fromconsistency = [cons_id for cons_id, f, a, p in consistencies]
else:
fromconsistency_is_empty = False
for cons_id, func, all_cons_opts, params in consistencies:
if not fromconsistency_is_empty and cons_id in option_bag.fromconsistency:
continue
warnings_only = option_warnings_only or params.get('warnings_only', False)
if (warnings_only and not check_error) or (not warnings_only and check_error):
transitive = params.get('transitive', True)
#all_cons_opts[0] is the option where func is set
if option_bag.ori_option.impl_is_dynsymlinkoption():
opts = []
for opt in all_cons_opts:
opts.append(opt().to_dynoption(option_bag.ori_option.rootpath,
option_bag.ori_option.suffix))
wopt = opts[0]
else:
opts = all_cons_opts
wopt = opts[0]()
wopt.launch_consistency(self,
func,
cons_id,
coption_bag,
value,
opts,
warnings_only,
transitive)
if fromconsistency_is_empty:
option_bag.fromconsistency = []
def _valid_consistencies(self,
other_opts: List[BaseOption],
init: bool=True,
func: Optional[str]=None) -> None:
if self.issubdyn():
dynod = self.getsubdyn()
else:
dynod = None
if self.impl_is_submulti():
raise ConfigError(_('cannot add consistency with submulti option'))
is_multi = self.impl_is_multi()
for opt in other_opts:
if isinstance(opt, weakref.ReferenceType):
opt = opt()
assert not opt.impl_is_submulti(), _('cannot add consistency with submulti option')
assert isinstance(opt, Option), _('consistency must be set with an option, not {}').format(opt)
if opt.issubdyn():
if dynod is None:
raise ConfigError(_('almost one option in consistency is '
'in a dynoptiondescription but not all'))
subod = opt.getsubdyn()
if dynod != subod:
raise ConfigError(_('option in consistency must be in same'
' dynoptiondescription'))
dynod = subod
elif dynod is not None:
raise ConfigError(_('almost one option in consistency is in a '
'dynoptiondescription but not all'))
if self is opt:
raise ConfigError(_('cannot add consistency with itself'))
if is_multi != opt.impl_is_multi():
raise ConfigError(_('every options in consistency must be '
'multi or none'))
# FIXME
if init and func != 'not_equal':
opt._has_dependency = True
def launch_consistency(self,
current_opt: BaseOption,
func: Callable,
cons_id: int,
option_bag: OptionBag,
value: Any,
opts: List[BaseOption],
warnings_only: bool,
transitive: bool) -> None:
"""Launch consistency now
"""
all_cons_vals = []
all_cons_opts = []
length = None
for opt in opts:
if isinstance(opt, weakref.ReferenceType):
opt = opt()
try:
opt_value = self.get_consistency_value(option_bag,
opt,
cons_id,
value,
func)
except PropertiesOptionError as err:
if transitive:
err.set_orig_opt(option_bag.option)
raise err
else:
if opt.impl_is_multi() and option_bag.index is None and \
func not in ALLOWED_CONST_LIST:
len_value = len(opt_value)
if length is not None and length != len_value:
if option_bag.config_bag is undefined:
return
raise ValueError(_('unexpected length of "{}" in constency "{}", '
'should be "{}"').format(len(opt_value),
opt.impl_get_display_name(),
length)) # pragma: no cover
length = len_value
if isinstance(opt_value, list) and func in ALLOWED_CONST_LIST:
for value_ in opt_value:
all_cons_vals.append(value_)
all_cons_opts.append(opt)
else:
all_cons_vals.append(opt_value)
all_cons_opts.append(opt)
if option_bag.config_bag is not undefined and \
not 'validator' in option_bag.config_bag.properties:
return
all_values = []
if length is None:
all_values = [all_cons_vals]
elif length:
all_values = zip(*all_cons_vals)
try:
context = option_bag.config_bag if option_bag.config_bag is undefined else option_bag.config_bag.context
for values in all_values:
getattr(self, func)(current_opt,
all_cons_opts,
values,
warnings_only,
context)
except ValueError as err:
if warnings_only:
warnings.warn_explicit(ValueWarning(value,
self._display_name,
current_opt,
"{}".format(err),
option_bag.index),
ValueWarning,
self.__class__.__name__, 0)
else:
raise err
def get_consistency_value(self,
option_bag: OptionBag,
current_option: BaseOption,
cons_id: int,
value: Any,
func: str) -> Any:
if option_bag.ori_option == current_option:
# orig_option is current option
# we have already value, so use it
return value
if option_bag.config_bag is undefined:
#if no context get default value
return current_option.impl_getdefault()
if func in ALLOWED_CONST_LIST:
index = None
index_ = None
elif current_option.impl_is_leader():
index = option_bag.index
index_ = None
else:
index = option_bag.index
index_ = index
#otherwise calculate value
path = current_option.impl_getpath()
coption_bag = OptionBag()
coption_bag.set_option(current_option,
path,
index_,
option_bag.config_bag)
fromconsistency = option_bag.fromconsistency.copy()
fromconsistency.append(cons_id)
coption_bag.fromconsistency = fromconsistency
current_value = option_bag.config_bag.context.getattr(path,
coption_bag)
if index_ is None and index is not None:
#if self is a follower and current_option is a leader and func not in ALLOWED_CONST_LIST
#return only the value of the leader for isolate follower
current_value = current_value[index]
return current_value
def _cons_not_equal(self,
current_opt: BaseOption,
opts: List[BaseOption],
vals: List[Any],
warnings_only: bool,
context) -> None:
equal = []
is_current = False
for idx_inf, val_inf in enumerate(vals):
for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
if val_inf == val_sup is not None:
for opt_ in [opts[idx_inf], opts[idx_inf + idx_sup + 1]]:
if opt_ == current_opt:
is_current = True
elif opt_ not in equal:
equal.append(opt_)
if equal:
if is_current:
if warnings_only:
msg = _('should be different from the value of {}')
else:
msg = _('must be different from the value of {}')
else:
if warnings_only:
msg = _('value for {} should be different')
else:
msg = _('value for {} must be different')
equal_name = []
for opt in equal:
equal_name.append(opt.impl_get_display_name())
raise ValueError(msg.format(display_list(list(equal_name), add_quote=True)))
def to_dynoption(self,
rootpath: str,
suffix: str) -> SynDynOption:

View file

@ -25,14 +25,12 @@ from typing import Optional, Iterator, Union, List
from ..i18n import _
from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption
from .option import ALLOWED_CONST_LIST
from .syndynoptiondescription import SynDynOptionDescription, SynDynLeadership
from ..error import ConfigError, ConflictError
class CacheOptionDescription(BaseOption):
__slots__ = ('_cache_consistencies',
'_cache_force_store_values')
__slots__ = ('_cache_force_store_values',)
def impl_already_build_caches(self) -> bool:
return self.impl_is_readonly()
@ -101,60 +99,10 @@ class CacheOptionDescription(BaseOption):
'"force_metaconfig_on_freeze" '
'property without "frozen"'
'').format(option.impl_get_display_name()))
for cons_id, func, all_cons_opts, params in option.get_consistencies():
option._valid_consistencies(all_cons_opts[1:], init=False)
if func not in ALLOWED_CONST_LIST and is_multi:
if __debug__ and not option.impl_get_leadership():
raise ConfigError(_('malformed consistency option "{0}" '
'must be in same leadership').format(
option.impl_getname()))
leadership = option.impl_get_leadership()
for weak_opt in all_cons_opts:
opt = weak_opt()
if __debug__ and func not in ALLOWED_CONST_LIST and is_multi:
if not opt.impl_get_leadership():
raise ConfigError(_('malformed consistency option "{0}" '
'must not be a multi for "{1}"').format(
option.impl_getname(), opt.impl_getname()))
elif leadership != opt.impl_get_leadership():
raise ConfigError(_('malformed consistency option "{0}" '
'must be in same leadership as "{1}"').format(
option.impl_getname(), opt.impl_getname()))
_consistencies.setdefault(weak_opt,
[]).append((_consistencies_id,
func,
all_cons_opts,
params))
_consistencies_id += 1
# if context is set to callback, must be reset each time a value change
if hasattr(option, '_has_calc_context'):
self._add_dependency(option)
if __debug__:
is_follower = None
if is_multi:
all_requires = option.impl_getrequires()
for requires in all_requires:
for require in requires:
#if option in require is a multi:
# * option in require must be a leader or a follower
# * current option must be a follower (and only a follower)
# * option in require and current option must be in same leadership
for require_opt, values in require[0]:
if not isinstance(require_opt, tuple) and require_opt.impl_is_multi():
if is_follower is None:
is_follower = option.impl_is_follower()
if is_follower:
leadership = option.impl_get_leadership()
if is_follower and require_opt.impl_get_leadership():
if leadership != require_opt.impl_get_leadership():
raise ValueError(_('malformed requirements option "{0}" '
'must be in same leadership for "{1}"').format(
require_opt.impl_getname(), option.impl_getname()))
else:
raise ValueError(_('malformed requirements option "{0}" '
'must not be a multi for "{1}"').format(
require_opt.impl_getname(), option.impl_getname()))
if option.impl_is_readonly():
raise ConflictError(_('duplicate option: {0}').format(option))
if not self.impl_is_readonly() and display_name:
@ -162,15 +110,6 @@ class CacheOptionDescription(BaseOption):
option._path = subpath
option._set_readonly()
if init:
if _consistencies != {}:
self._cache_consistencies = {}
for weak_opt, cons in _consistencies.items():
opt = weak_opt()
if __debug__ and opt not in cache_option:
raise ConfigError(_('consistency with option {0} '
'which is not in Config').format(
opt.impl_getname()))
self._cache_consistencies[opt] = tuple(cons)
self._cache_force_store_values = force_store_values
self._path = self._name
self._set_readonly()
@ -275,7 +214,6 @@ class OptionDescription(OptionDescriptionWalk):
name: str,
doc: str,
children: List[BaseOption],
requires=None,
properties=None) -> None:
"""
:param children: a list of options (including optiondescriptions)
@ -285,7 +223,6 @@ class OptionDescription(OptionDescriptionWalk):
'must be a list').format(name)
super().__init__(name,
doc=doc,
requires=requires,
properties=properties)
child_names = []
if __debug__:

View file

@ -48,12 +48,8 @@ class PortOption(StrOption):
doc,
default=None,
default_multi=None,
requires=None,
multi=False,
callback=None,
callback_params=None,
validator=None,
validator_params=None,
validators=None,
properties=None,
allow_range=False,
allow_zero=False,
@ -89,12 +85,8 @@ class PortOption(StrOption):
doc,
default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_params=validator_params,
validators=validators,
properties=properties,
warnings_only=warnings_only,
extra=extra)

View file

@ -58,6 +58,3 @@ class SymLinkOption(BaseOption):
def impl_getopt(self) -> BaseOption:
return self._opt
def get_consistencies(self) -> tuple:
return ()

View file

@ -64,24 +64,11 @@ class SynDynOption:
def impl_getpath(self) -> str:
return self.rootpath + '.' + self.impl_getname()
def impl_validate(self,
value: Any,
option_bag: OptionBag,
check_error: bool=True) -> None:
soption_bag = OptionBag()
soption_bag.set_option(self.opt,
self.impl_getpath(),
option_bag.index,
option_bag.config_bag)
soption_bag.ori_option = option_bag.option
soption_bag.fromconsistency = option_bag.fromconsistency.copy()
self.opt.impl_validate(value,
soption_bag,
check_error=check_error)
def impl_is_dynsymlinkoption(self) -> bool:
return True
def impl_get_leadership(self):
return self.opt.impl_get_leadership().to_dynoption(self.rootpath,
self.suffix)
leadership = self.opt.impl_get_leadership()
if leadership:
return leadership.to_dynoption(self.rootpath,
self.suffix)

View file

@ -15,8 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ____________________________________________________________
from .error import (RequirementError, PropertiesOptionError,
ConstError, ConfigError, display_list)
from .error import PropertiesOptionError, ConstError, ConfigError, LeadershipError, display_list
from .i18n import _
@ -109,7 +108,12 @@ FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value'])
FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze',
'force_metaconfig_on_freeze',
'force_store_value'])
ALLOWED_LEADER_PROPERTIES = frozenset(['empty',
'force_store_value',
'mandatory',
'force_default_on_freeze',
'force_metaconfig_on_freeze',
'frozen'])
static_set = frozenset()
@ -123,12 +127,10 @@ class OptionBag:
'properties', # properties of current option
'properties_setted',
'apply_requires', # apply requires or not for this option
'fromconsistency' # history for consistency
)
def __init__(self):
self.option = None
self.fromconsistency = []
def set_option(self,
option,
@ -235,9 +237,6 @@ class ConfigBag:
return
raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover
# def __setattr__(self, key, value):
# super().__setattr__(key, value)
def copy(self):
kwargs = {}
for key in self.__slots__:
@ -447,24 +446,19 @@ class Settings(object):
if isinstance(prop, str):
props.add(prop)
elif apply_requires:
new_props = prop.execute(option_bag,
leadership_must_have_index=True)
if not new_props:
new_prop = prop.execute(option_bag,
leadership_must_have_index=True)
if not new_prop:
continue
elif not isinstance(new_props, str):
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_props),
elif not isinstance(new_prop, str):
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_prop),
option_bag.option.impl_getname(),
prop.function.__name__))
props.add(new_props)
# else:
# props.update(new_props)
if apply_requires:
props |= self.apply_requires(option_bag,
False,
search_properties=search_properties)
if not option.impl_is_optiondescription() and option.impl_is_leader() and new_prop not in ALLOWED_LEADER_PROPERTIES:
raise LeadershipError(_('leader cannot have "{}" property').format(new_prop))
props.add(new_prop)
props -= self.getpermissives(option,
path)
#if apply_requires and config_bag.properties == config_bag.true_properties:
if apply_requires and not config_bag.is_unrestraint:
cache.setcache(path,
index,
@ -508,195 +502,6 @@ class Settings(object):
path = opt.impl_getpath()
return self._pp_.getpermissives(path)
def apply_requires(self,
option_bag,
readable,
search_properties=None):
"""carries out the jit (just in time) requirements between options
a requirement is a tuple of this form that comes from the option's
requirements validation::
(option, expected, action, inverse, transitive, same_action)
let's have a look at all the tuple's items:
- **option** is the target option's
- **expected** is the target option's value that is going to trigger
an action
- **action** is the (property) action to be accomplished if the target
option happens to have the expected value
- if **inverse** is `True` and if the target option's value does not
apply, then the property action must be removed from the option's
properties list (wich means that the property is inverted)
- **transitive**: but what happens if the target option cannot be
accessed ? We don't kown the target option's value. Actually if some
property in the target option is not present in the permissive, the
target option's value cannot be accessed. In this case, the
**action** have to be applied to the option. (the **action** property
is then added to the option).
- **same_action**: actually, if **same_action** is `True`, the
transitivity is not accomplished. The transitivity is accomplished
only if the target option **has the same property** that the demanded
action. If the target option's value is not accessible because of
another reason, because of a property of another type, then an
exception :exc:`~error.RequirementError` is raised.
And at last, if no target option matches the expected values, the
action will not add to the option's properties list.
:param opt: the option on wich the requirement occurs
:type opt: `option.Option()`
:param path: the option's path in the config
:type path: str
"""
current_requires = option_bag.option.impl_getrequires()
# filters the callbacks
if readable:
calc_properties = {}
else:
calc_properties = set()
if not current_requires:
return calc_properties
context = option_bag.config_bag.context
all_properties = None
for requires in current_requires:
for require in requires:
exps, action, inverse, transitive, same_action, operator = require
#if search_properties and action not in search_properties:
# continue
breaked = False
for option, expected in exps:
if not isinstance(option, tuple):
if option.issubdyn():
option = option.to_dynoption(option_bag.option.rootpath,
option_bag.option.impl_getsuffix())
reqpath = option.impl_getpath()
if __debug__ and reqpath.startswith(option_bag.path + '.'):
# FIXME too later!
raise RequirementError(_("malformed requirements "
"imbrication detected for option:"
" '{0}' with requirement on: "
"'{1}'").format(option_bag.path, reqpath))
idx = None
is_indexed = False
if option.impl_is_follower():
idx = option_bag.index
if idx is None:
continue
elif option.impl_is_leader() and option_bag.index is None:
continue
elif option.impl_is_multi() and option_bag.index is not None:
is_indexed = True
config_bag = option_bag.config_bag.copy()
soption_bag = OptionBag()
soption_bag.set_option(option,
reqpath,
idx,
config_bag)
if option_bag.option == option:
soption_bag.config_bag.unrestraint()
soption_bag.config_bag.remove_validation()
soption_bag.apply_requires = False
else:
soption_bag.config_bag.properties = soption_bag.config_bag.true_properties
soption_bag.config_bag.set_permissive()
else:
if not option_bag.option.impl_is_optiondescription() and option_bag.option.impl_is_follower():
idx = option_bag.index
if idx is None:
continue
is_indexed = False
try:
if not isinstance(option, tuple):
value = context.getattr(reqpath,
soption_bag)
else:
value = context.cfgimpl_get_values().carry_out_calculation(option_bag,
option[0],
option[1])
except (PropertiesOptionError, ConfigError) as err:
if isinstance(err, ConfigError):
if not isinstance(err.ori_err, PropertiesOptionError):
raise err
err = err.ori_err
properties = err.proptype
# if not transitive, properties must be verify in current requires
# otherwise if same_action, property must be in properties
# otherwise add property in returned properties (if operator is 'and')
if not transitive:
if all_properties is None:
all_properties = []
for requires_ in current_requires:
for require_ in requires_:
all_properties.append(require_[1])
if not set(properties) - set(all_properties):
continue
if same_action and action not in properties:
if len(properties) == 1:
prop_msg = _('property')
else:
prop_msg = _('properties')
err = RequirementError(_('cannot access to option "{0}" because '
'required option "{1}" has {2} {3}'
'').format(option_bag.option.impl_get_display_name(),
option.impl_get_display_name(),
prop_msg,
display_list(list(properties), add_quote=True)))
err.proptype = properties
raise err
# transitive action, add action
if operator != 'and':
if readable:
for msg in self.apply_requires(err._option_bag,
True).values():
calc_properties.setdefault(action, []).extend(msg)
else:
calc_properties.add(action)
breaked = True
break
else:
if is_indexed:
value = value[option_bag.index]
if (not inverse and value in expected or
inverse and value not in expected):
if operator != 'and':
if readable:
display_value = display_list(expected, 'or', add_quote=True)
if isinstance(option, tuple):
if not inverse:
msg = _('the calculated value is {0}').format(display_value)
else:
msg = _('the calculated value is not {0}').format(display_value)
else:
name = option.impl_get_display_name()
if not inverse:
msg = _('the value of "{0}" is {1}').format(name, display_value)
else:
msg = _('the value of "{0}" is not {1}').format(name, display_value)
calc_properties.setdefault(action, []).append(msg)
else:
calc_properties.add(action)
breaked = True
break
elif operator == 'and':
break
else:
if operator == 'and':
calc_properties.add(action)
continue
if breaked:
break
return calc_properties
#____________________________________________________________
# set methods
def set_context_properties(self,
@ -715,23 +520,21 @@ class Settings(object):
(never save properties if same has option properties)
"""
opt = option_bag.option
if opt.impl_getrequires() is not None:
not_allowed_props = properties & \
getattr(opt, '_calc_properties', static_set)
if not_allowed_props:
raise ValueError(_('cannot set property {} for option "{}" this property is '
'calculated').format(display_list(list(not_allowed_props),
add_quote=True),
opt.impl_get_display_name()))
if opt.impl_is_symlinkoption():
raise TypeError(_("can't assign property to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name()))
if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \
'frozen' not in properties and \
opt.impl_is_leader():
raise ConfigError(_('a leader ({0}) cannot have '
'"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
'').format(opt.impl_get_display_name()))
if not opt.impl_is_optiondescription() and opt.impl_is_leader():
not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
if not_allowed_properties:
if len(not_allowed_properties) == 1:
raise LeadershipError(_('leader cannot have "{}" property').format(list(not_allowed_properties)[0]))
else:
raise LeadershipError(_('leader cannot have {} properties').format(display_list(list(not_allowed_properties), add_quote=True)))
if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \
'frozen' not in properties:
raise LeadershipError(_('a leader ({0}) cannot have '
'"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
'').format(opt.impl_get_display_name()))
self._p_.setproperties(path,
properties)
# values too because of follower values could have a PropertiesOptionError has value

View file

@ -67,7 +67,7 @@ class Callbacks(object):
raise ValueError(_('context is not supported from now for {}').format(path))
if isinstance(callback_param, ParamOption):
has_option = True
if callback.__name__ != 'tiramisu_copy' or 'expire' in childapi.option.properties():
if 'expire' in childapi.option.properties():
self.tiramisu_web.set_remotable(callback_param.option.impl_getpath(), form)
if not has_option and form.get(path, {}).get('remote', False) == False:
if 'expire' in childapi.option.properties():
@ -79,14 +79,15 @@ class Callbacks(object):
form.setdefault(path, {})['clearable'] = True
def manage_callbacks(self, form):
for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
if callback_params is not None:
for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy':
opt_path = callback_param.option.impl_getpath()
if form.get(opt_path, {}).get('remote') is not True:
form.setdefault(opt_path, {})
form[opt_path].setdefault('copy', []).append(path)
pass
#for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
# if callback_params is not None:
# for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
# if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy':
# opt_path = callback_param.option.impl_getpath()
# if form.get(opt_path, {}).get('remote') is not True:
# form.setdefault(opt_path, {})
# form[opt_path].setdefault('copy', []).append(path)
def process(self,
form):

View file

@ -17,7 +17,7 @@
# ____________________________________________________________
import weakref
from typing import Optional, Any, Callable
from .error import ConfigError, PropertiesOptionError, RequirementError
from .error import ConfigError, PropertiesOptionError
from .setting import owners, undefined, forbidden_owners, OptionBag, ConfigBag
from .autolib import Calculation, carry_out_calculation, Params
from .i18n import _
@ -78,7 +78,7 @@ class Values(object):
# store value in cache
properties = option_bag.config_bag.properties
validator = 'validator' in properties and 'demoting_error_warning' not in properties
if not option_bag.fromconsistency and (not is_cached or validator):
if not is_cached or validator:
cache.setcache(option_bag.path,
option_bag.index,
value,
@ -258,8 +258,7 @@ class Values(object):
callback=callback,
callback_params=callback_params,
index=option_bag.index,
config_bag=option_bag.config_bag,
fromconsistency=option_bag.fromconsistency)
config_bag=option_bag.config_bag)
def isempty(self,
opt,
value,
@ -289,24 +288,8 @@ class Values(object):
context = option_bag.config_bag.context
owner = self.get_context_owner()
if 'validator' in option_bag.config_bag.properties:
if option_bag.index is not None or option_bag.option.has_consistencies(context):
# set value to a fake config when option has dependency
# validation will be complet in this case (consistency, ...)
tested_context = context._gen_fake_values()
config_bag = option_bag.config_bag.copy()
config_bag.unrestraint()
config_bag.context = tested_context
soption_bag = option_bag.copy()
soption_bag.config_bag = config_bag
tested_context.cfgimpl_get_values().setvalue(value,
soption_bag,
True)
soption_bag.config_bag.properties = option_bag.config_bag.properties
tested_context.getattr(soption_bag.path,
soption_bag)
else:
self.setvalue_validation(value,
option_bag)
self.setvalue_validation(value,
option_bag)
self._setvalue(option_bag,
value,
@ -622,7 +605,7 @@ class Values(object):
except PropertiesOptionError as err:
if err.proptype == ['mandatory']:
yield path
except (RequirementError, ConfigError):
except ConfigError:
pass
def mandatory_warnings(self,