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

View file

@ -139,77 +139,11 @@ def test_deref_optiondescription_config():
assert w() is None 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(): def test_deref_validator():
if not IS_DEREFABLE: if not IS_DEREFABLE:
return return
a = StrOption('a', '', default='yes') 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]) od = OptionDescription('root', '', [a, b])
cfg = Config(od) cfg = Config(od)
w = weakref.ref(a) w = weakref.ref(a)
@ -234,8 +168,6 @@ def test_deref_validator():
del(cfg) del(cfg)
assert y() is None assert y() is None
assert z() is None assert z() is None
#assert w() is None
#assert x() is None
def test_deref_callback(): def test_deref_callback():
@ -267,8 +199,6 @@ def test_deref_callback():
del(cfg) del(cfg)
assert y() is None assert y() is None
assert z() is None assert z() is None
#assert w() is None
#assert x() is None
def test_deref_symlink(): def test_deref_symlink():
@ -298,8 +228,6 @@ def test_deref_symlink():
assert w() is not None assert w() is not None
assert x() is not None assert x() is not None
del(cfg) del(cfg)
#assert w() is None
#assert x() is None
assert y() is None assert y() is None
assert z() is None assert z() is None
@ -333,7 +261,5 @@ def test_deref_dyn():
assert w() is not None assert w() is not None
assert x() is not None assert x() is not None
del(cfg) del(cfg)
#assert w() is None
#assert x() is None
assert y() is None assert y() is None
assert z() is None assert z() is None

View file

@ -9,7 +9,7 @@ from tiramisu import BoolOption, StrOption, ChoiceOption, IPOption, \
UnicodeOption, PortOption, BroadcastOption, DomainnameOption, \ UnicodeOption, PortOption, BroadcastOption, DomainnameOption, \
EmailOption, URLOption, UsernameOption, FilenameOption, SymLinkOption, \ EmailOption, URLOption, UsernameOption, FilenameOption, SymLinkOption, \
OptionDescription, DynOptionDescription, SynDynOption, submulti, Leadership, \ 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.error import PropertiesOptionError, ConfigError, ConflictError
from tiramisu.storage import list_sessions from tiramisu.storage import list_sessions
@ -28,7 +28,7 @@ def return_dynval(value='val', suffix=None):
return value return value
def return_list2(suffix=None): def return_list2(suffix):
return [str(suffix), 'val2'] return [str(suffix), 'val2']
@ -249,7 +249,7 @@ def test_callback_dyndescription():
def test_callback_list_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)) dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod]) od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
@ -675,7 +675,7 @@ def test_requires_dyndescription2():
def test_validator_dyndescription(): def test_validator_dyndescription():
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True) 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)) dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod, val1]) od = OptionDescription('od', '', [dod, val1])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
@ -735,108 +735,13 @@ def test_information_dyndescription_context():
assert api.information.get('testcfgod') == 'val3' 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(): def test_all_dyndescription():
st = StrOption('st', '') st = StrOption('st', '')
ip = IPOption('ip', '') ip = IPOption('ip', '')
network = NetworkOption('network', '') network = NetworkOption('network', '')
netmask = NetmaskOption('netmask', '') netmask = NetmaskOption('netmask', '')
ch = ChoiceOption('ch', '', ('val1', 'val2', 'val3')) ch = ChoiceOption('ch', '', ('val1', 'val2', 'val3'))
ch1 = ChoiceOption('ch1', '', return_list) ch1 = ChoiceOption('ch1', '', Calculation(return_list))
boo = BoolOption('boo', '') boo = BoolOption('boo', '')
intr = IntOption('intr', '') intr = IntOption('intr', '')
floa = FloatOption('floa', '') floa = FloatOption('floa', '')
@ -925,34 +830,6 @@ def test_all_dyndescription():
assert api.option('dodval2.filenameval2').value.get() is None 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(): def test_leadership_dyndescription():
st1 = StrOption('st1', "", multi=True) st1 = StrOption('st1', "", multi=True)
st2 = StrOption('st2', "", 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'))) 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(): def test_force_store_value_leadership_follower():
b = IntOption('int', 'Test int option', multi=True) b = IntOption('int', 'Test int option', multi=True)
c = StrOption('str', 'Test string option', multi=True, properties=('force_store_value',)) 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(): 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',)) 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]) od = OptionDescription('root', '', [interface1])
cfg = Config(od) cfg = Config(od)
cfg.property.read_write() cfg.property.read_write()
@ -412,22 +412,6 @@ def test_groups_with_leader_reset_out_of_range(config_type):
cfg.send() 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(): def test_allowed_groups():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) 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) 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.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.set("192.168.230.145")
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.reset() 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) cfg = get_config(cfg_ori, config_type)
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
# FIXME # FIXME
@ -847,49 +832,7 @@ def test_follower_not_multi():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) 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") netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
raises(ValueError, "Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])") 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(): def test_follower_force_store_value():

View file

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

View file

@ -6,7 +6,8 @@ from py.test import raises
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
from tiramisu import IntOption, StrOption, NetworkOption, NetmaskOption, \ from tiramisu import IntOption, StrOption, NetworkOption, NetmaskOption, \
OptionDescription, Leadership, Config, GroupConfig, MixConfig, \ 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.error import ConfigError, ConflictError, PropertiesOptionError, LeadershipError, APIError
from tiramisu.storage import list_sessions from tiramisu.storage import list_sessions
@ -589,9 +590,8 @@ def test_mix_force_default_and_dont_change():
def test_mix_properties_mix(): def test_mix_properties_mix():
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1']) 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 = NetmaskOption('netmask_admin_eth0', "mask", multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(ip_admin_eth0), ParamSelfOption())))])
netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0], properties=('disabled',))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od = OptionDescription('root', '', [interface1]) od = OptionDescription('root', '', [interface1])
conf1 = Config(od, session_id='conf1') conf1 = Config(od, session_id='conf1')
conf2 = Config(od, session_id='conf2') conf2 = Config(od, session_id='conf2')
@ -604,8 +604,7 @@ def test_mix_properties_mix():
def test_mix_exception_mix(): def test_mix_exception_mix():
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1']) 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 = NetmaskOption('netmask_admin_eth0', "mask", Calculation(raise_exception), multi=True, validators=[Calculation(valid_network_netmask, Params((ParamOption(ip_admin_eth0), ParamSelfOption())))])
netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od = OptionDescription('root', '', [interface1]) od = OptionDescription('root', '', [interface1])
conf1 = Config(od, session_id='conf1') 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 py.test import raises
from tiramisu.setting import owners 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, \ from tiramisu import IntOption, FloatOption, StrOption, ChoiceOption, \
BoolOption, OptionDescription, Leadership, Config, undefined BoolOption, OptionDescription, Leadership, Config, undefined
from tiramisu.storage import list_sessions from tiramisu.storage import list_sessions
@ -163,7 +163,7 @@ def test_force_default_on_freeze_leader_frozen():
descr = Leadership("dummy1", "", [dummy1, dummy2]) descr = Leadership("dummy1", "", [dummy1, dummy2])
descr = OptionDescription("root", "", [descr]) descr = OptionDescription("root", "", [descr])
cfg = Config(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(): 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 = Leadership("dummy1", "", [dummy1, dummy2])
descr = OptionDescription("root", "", [descr]) descr = OptionDescription("root", "", [descr])
cfg = Config(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): 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.setting import owners, groups
from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \ from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
StrOption, OptionDescription, Leadership, Config, undefined, \ 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.error import PropertiesOptionError
from tiramisu.storage import list_sessions from tiramisu.storage import list_sessions
import warnings
def teardown_function(function): def teardown_function(function):

View file

@ -5,9 +5,13 @@ from .config import config_type, get_config
import warnings import warnings
from py.test import raises 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.setting import groups
from tiramisu.error import ValueWarning, ConfigError from tiramisu.error import ValueWarning, ConfigError, PropertiesOptionError
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.storage import list_sessions 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): def value_empty(value, empty, values):
if not value == 'val' or empty is not False and not values == ['val']: if not value == 'val' or empty is not False and not values == ['val']:
raise ValueError('error') raise ValueError('error')
@ -103,9 +98,9 @@ def valid_from_config(value, config):
def test_validator(config_type): def test_validator(config_type):
opt1 = StrOption('opt1', '', validator=return_true, default='val') opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()))], default='val')
raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')") raises(ValueError, "StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()))], default='val')")
opt2 = StrOption('opt2', '', validator=return_false) opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()))])
root = OptionDescription('root', '', [opt1, opt2]) root = OptionDescription('root', '', [opt1, opt2])
cfg_ori = Config(root) cfg_ori = Config(root)
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
@ -138,9 +133,9 @@ def test_validator(config_type):
def test_validator_params(config_type): def test_validator_params(config_type):
opt1 = StrOption('opt1', '', validator=return_true, validator_params=Params(ParamValue('yes')), default='val') opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params((ParamSelfOption(), ParamValue('yes'))))], default='val')
raises(ValueError, "StrOption('opt2', '', validator=return_false, validator_params=Params(ParamValue('yes')), default='val')") raises(ValueError, "StrOption('opt2', '', validators=[Calculation(return_false, Params((ParamSelfOption(), ParamValue('yes'))))], default='val')")
opt2 = StrOption('opt2', '', validator=return_false, validator_params=Params(ParamValue('yes'))) opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params((ParamSelfOption(), ParamValue('yes'))))])
root = OptionDescription('root', '', [opt1, opt2]) root = OptionDescription('root', '', [opt1, opt2])
cfg_ori = Config(root) cfg_ori = Config(root)
cfg = get_config(cfg_ori, config_type) 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): 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]) root = OptionDescription('root', '', [opt1])
cfg = Config(root) cfg = Config(root)
cfg = get_config(cfg, config_type) 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): 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]) root = OptionDescription('root', '', [opt1])
cfg = Config(root) cfg = Config(root)
cfg = get_config(cfg, config_type) 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): 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) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1]) 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): 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) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1]) 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): def test_validator_params_value_values_follower(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) 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]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1]) root = OptionDescription('root', '', [interface1])
cfg = Config(root) 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): def test_validator_params_value_values_index_follower(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) 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]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1]) root = OptionDescription('root', '', [interface1])
cfg = Config(root) 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') 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): def test_validator_params_value_values_kwargs_empty(config_type):
v = BoolOption('v', '', default=False) v = BoolOption('v', '', default=False)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, default=["ip"]) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, default=["ip"])
netmask_admin_eth0 = StrOption('netmask_admin_eth0', netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau", "masque du sous-reseau",
multi=True, multi=True,
validator=value_empty, validators=[Calculation(value_empty, Params((ParamSelfOption(), ParamOption(v))))])
validator_params=Params(ParamOption(v)))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [v, interface1]) root = OptionDescription('root', '', [v, interface1])
cfg = Config(root) cfg = Config(root)
@ -252,8 +242,7 @@ def test_validator_params_value_values_kwargs(config_type):
netmask_admin_eth0 = StrOption('netmask_admin_eth0', netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau", "masque du sous-reseau",
multi=True, multi=True,
validator=value_values_auto, validators=[Calculation(value_values_auto, Params((ParamSelfOption(), ParamSelfOption(whole=True)), kwargs={'auto': ParamOption(v)}))])
validator_params=Params(kwargs={'auto': ParamOption(v)}))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [v, interface1]) root = OptionDescription('root', '', [v, interface1])
cfg = Config(root) cfg = Config(root)
@ -269,43 +258,7 @@ def test_validator_params_value_values_kwargs_values(config_type):
netmask_admin_eth0 = StrOption('netmask_admin_eth0', netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau", "masque du sous-reseau",
multi=True, multi=True,
validator=value_values_auto2, validators=[Calculation(value_values_auto2, Params(ParamSelfOption(), kwargs={'values': ParamOption(ip_admin_eth0)}))])
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)}))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1]) root = OptionDescription('root', '', [interface1])
cfg = Config(root) cfg = Config(root)
@ -318,7 +271,7 @@ def test_validator_params_value_values_kwargs_index(config_type):
def test_validator_params_context(): 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]) root = OptionDescription('root', '', [opt1])
cfg = Config(root) cfg = Config(root)
# cfg = get_config(cfg, config_type) # ParamContext not supported # cfg = get_config(cfg, config_type) # ParamContext not supported
@ -329,7 +282,7 @@ def test_validator_params_context():
def test_validator_params_context_value(): def test_validator_params_context_value():
opt1 = StrOption('opt1', '', 'yes') 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]) root = OptionDescription('root', '', [opt1, opt2])
cfg = Config(root) cfg = Config(root)
# cfg = get_config(cfg_ori, config_type) # ParamContext not supported # 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): def test_validator_params_key(config_type):
opt1 = StrOption('opt1', '', validator=return_true, validator_params=Params(kwargs={'param': ParamValue('yes')}), default='val') opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption(), kwargs={'param': ParamValue('yes')}))], default='val')
raises(ConfigError, "StrOption('opt2', '', validator=return_true, validator_params=Params(kwargs={'param_unknown': 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]) root = OptionDescription('root', '', [opt1])
cfg = Config(root) cfg = Config(root)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
@ -357,7 +310,7 @@ def test_validator_params_key(config_type):
def test_validator_params_option(config_type): def test_validator_params_option(config_type):
opt0 = StrOption('opt0', '', default='yes') 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]) r = OptionDescription('root', '', [opt0, opt1])
cfg_ori = Config(r) cfg_ori = Config(r)
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
@ -375,7 +328,7 @@ def test_validator_params_option(config_type):
def test_validator_multi(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]) root = OptionDescription('root', '', [opt1])
cfg_ori = Config(root) cfg_ori = Config(root)
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
@ -394,9 +347,9 @@ def test_validator_multi(config_type):
def test_validator_warning(config_type): def test_validator_warning(config_type):
opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True) opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()), warnings_only=True)], default='val')
opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True) opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()), warnings_only=True)])
opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, 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]) root = OptionDescription('root', '', [opt1, opt2, opt3])
cfg = Config(root) cfg = Config(root)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
@ -440,9 +393,9 @@ def test_validator_warning(config_type):
def test_validator_warning_disabled(config_type): def test_validator_warning_disabled(config_type):
opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True) opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()), warnings_only=True)], default='val')
opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True) opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()), warnings_only=True)])
opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, 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]) root = OptionDescription('root', '', [opt1, opt2, opt3])
cfg_ori = Config(root) cfg_ori = Config(root)
cfg_ori.property.pop('warnings') cfg_ori.property.pop('warnings')
@ -485,8 +438,8 @@ def test_validator_warning_disabled(config_type):
def test_validator_warning_leadership(config_type): def test_validator_warning_leadership(config_type):
display_name_ip = "ip reseau autorise" display_name_ip = "ip reseau autorise"
display_name_netmask = "masque du sous-reseau" 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) 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, validator=return_if_val, 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]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
assert interface1.impl_get_group_type() == groups.leadership assert interface1.impl_get_group_type() == groups.leadership
root = OptionDescription('root', '', [interface1]) root = OptionDescription('root', '', [interface1])
@ -543,8 +496,7 @@ def test_validator_follower_param(config_type):
netmask_admin_eth0 = StrOption('netmask_admin_eth0', netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau", "masque du sous-reseau",
multi=True, multi=True,
validator=return_true, validators=[Calculation(return_true, Params(ParamSelfOption(), kwargs={'param': ParamOption(ip_admin_eth0)}))])
validator_params=Params(kwargs={'param': ParamOption(ip_admin_eth0)}))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
root = OptionDescription('root', '', [interface1]) root = OptionDescription('root', '', [interface1])
cfg = Config(root) cfg = Config(root)
@ -560,9 +512,8 @@ def test_validator_dependencies():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise") ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise")
netmask_admin_eth0 = StrOption('netmask_admin_eth0', netmask_admin_eth0 = StrOption('netmask_admin_eth0',
"masque du sous-reseau", "masque du sous-reseau",
validator=return_true, validators=[Calculation(return_true, Params(ParamSelfOption(whole=False), kwargs={'param': ParamOption(ip_admin_eth0)}))])
validator_params=Params(kwargs={'param': ParamOption(ip_admin_eth0)})) opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption(whole=False)))])
opt2 = StrOption('opt2', '', validator=return_false)
root = OptionDescription('root', '', [ip_admin_eth0, netmask_admin_eth0, opt2]) root = OptionDescription('root', '', [ip_admin_eth0, netmask_admin_eth0, opt2])
cfg = Config(root) cfg = Config(root)
assert cfg.option('ip_admin_eth0').option.has_dependency() is False 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('ip_admin_eth0').option.has_dependency(False) is True
assert cfg.option('netmask_admin_eth0').option.has_dependency(False) is False assert cfg.option('netmask_admin_eth0').option.has_dependency(False) is False
assert cfg.option('opt2').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', '') p = URLOption('p', '')
q = FilenameOption('q', '') q = FilenameOption('q', '')
m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l, o, p, q]) m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l, o, p, q])
a._requires = (((a,),),) a._name = 'a'
b._requires = (((a,),),) b._name = 'b'
c._requires = (((a,),),) c._name = 'c'
d._requires = (((a,),),) d._name = 'd'
e._requires = (((a,),),) e._name = 'e'
g._requires = (((a,),),) g._name = 'g'
h._requires = (((a,),),) h._name = 'h'
i._requires = (((a,),),) i._name = 'i'
j._requires = (((a,),),) j._name = 'j'
k._requires = (((a,),),) k._name = 'k'
l._requires = (((a,),),) l._name = 'l'
m._requires = (((a,),),) m._name = 'm'
o._requires = (((a,),),) o._name = 'o'
p._requires = (((a,),),) p._name = 'p'
q._requires = (((a,),),) q._name = 'q'
Config(m) Config(m)
raises(AttributeError, "a._requires = 'a'") raises(AttributeError, "a._requires = 'a'")
raises(AttributeError, "b._requires = 'b'") raises(AttributeError, "b._requires = 'b'")

View file

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

View file

@ -196,11 +196,6 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
option = self._option_bag.option option = self._option_bag.option
return option.impl_has_dependency(self_is_dep) 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): def isoptiondescription(self):
"""Test if option is an optiondescription""" """Test if option is an optiondescription"""
option = self._option_bag.option option = self._option_bag.option
@ -274,11 +269,6 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
ret = value ret = value
return ret 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): def callbacks(self):
"""Get callbacks for an option (not for optiondescription)""" """Get callbacks for an option (not for optiondescription)"""
option = self._option_bag.option option = self._option_bag.option

View file

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

View file

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

View file

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

View file

@ -14,13 +14,144 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Any, List, Optional from typing import Any, List, Optional
from operator import add, mul, sub, truediv from operator import add, mul, sub, truediv
from ipaddress import ip_address, ip_interface, ip_network
from .i18n import _ from .i18n import _
from .setting import undefined from .setting import undefined
from .error import display_list from .error import display_list
def tiramisu_copy(val): # pragma: no cover def valid_network_netmask(network: str,
return val 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: class CalcValue:

View file

@ -51,14 +51,8 @@ class Base:
__slots__ = ('_name', __slots__ = ('_name',
'_path', '_path',
'_informations', '_informations',
#calcul
'_subdyn', '_subdyn',
'_requires',
'_properties', '_properties',
'_calc_properties',
#
'_consistencies',
#other
'_has_dependency', '_has_dependency',
'_dependencies', '_dependencies',
'_has_calc_context', '_has_calc_context',
@ -68,19 +62,10 @@ class Base:
def __init__(self, def __init__(self,
name: str, name: str,
doc: str, doc: str,
requires=None,
properties=None, properties=None,
is_multi: bool=False) -> None: is_multi: bool=False) -> None:
if not valid_name(name): if not valid_name(name):
raise ValueError(_('"{0}" is an invalid name for an option').format(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: if properties is None:
properties = frozenset() properties = frozenset()
elif isinstance(properties, tuple): elif isinstance(properties, tuple):
@ -92,153 +77,18 @@ class Base:
assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},' assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},'
' must be a frozenset').format(type(properties), ' must be a frozenset').format(type(properties),
name) 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: for prop in properties:
if not isinstance(prop, str): if not isinstance(prop, str):
if not isinstance(prop, Calculation): if not isinstance(prop, Calculation):
raise ValueError(_('invalid property type {0} for {1}, must be a string or a Calculation').format(type(prop), name)) 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(prop.params.args, prop.params.kwargs.values()):
for param in chain(params.args, params.kwargs.values()):
if isinstance(param, ParamOption): if isinstance(param, ParamOption):
param.option._add_dependency(self) param.option._add_dependency(self)
_setattr = object.__setattr__
def _get_function_args(self, _setattr(self, '_name', name)
function: Callable) -> Tuple[Set[str], Set[str], bool, bool]: _setattr(self, '_informations', {'doc': doc})
args = set() if properties:
kwargs = set() _setattr(self, '_properties', properties)
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
def impl_has_dependency(self, def impl_has_dependency(self,
self_is_dep: bool=True) -> bool: self_is_dep: bool=True) -> bool:
@ -323,9 +173,6 @@ class Base:
def getsubdyn(self): def getsubdyn(self):
return self._subdyn() return self._subdyn()
def impl_getrequires(self):
return getattr(self, '_requires', STATIC_TUPLE)
def impl_get_callback(self): def impl_get_callback(self):
call = getattr(self, '_val_call', (None, None))[1] call = getattr(self, '_val_call', (None, None))[1]
if call is None: if call is None:
@ -373,7 +220,6 @@ class Base:
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
" read-only").format(self.__class__.__name__, " read-only").format(self.__class__.__name__,
self, self,
#self.impl_getname(),
key)) key))
self._informations[key] = value self._informations[key] = value
@ -445,181 +291,3 @@ class BaseOption(Base):
def impl_is_symlinkoption(self) -> bool: def impl_is_symlinkoption(self) -> bool:
return False 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) ip_address(value)
except ValueError: except ValueError:
raise 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 ..setting import undefined
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
from ..autolib import carry_out_calculation from ..autolib import carry_out_calculation, Calculation
from ..error import ConfigError, display_list from ..error import ConfigError, display_list
@ -41,83 +41,44 @@ class ChoiceOption(Option):
doc, doc,
values, values,
default=None, default=None,
values_params=None,
default_multi=None, default_multi=None,
requires=None,
multi=False, multi=False,
callback=None, validators=None,
callback_params=None,
validator=None,
validator_params=None,
properties=None, properties=None,
warnings_only=False): warnings_only=False):
""" """
:param values: is a list of values the option can possibly take :param values: is a list of values the option can possibly take
""" """
if isinstance(values, FunctionType): if not isinstance(values, (Calculation, tuple)):
values_params = self._build_calculator_params(values, raise TypeError(_('values must be a tuple or a calculation for {0}'
values_params, ).format(name))
'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))
self._choice_values = values self._choice_values = values
super(ChoiceOption, self).__init__(name, super(ChoiceOption, self).__init__(name,
doc, doc,
default=default, default=default,
default_multi=default_multi, default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi, multi=multi,
validator=validator, validators=validators,
validator_params=validator_params,
properties=properties, properties=properties,
warnings_only=warnings_only) 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, def impl_get_values(self,
option_bag, option_bag):
current_opt=undefined): if isinstance(self._choice_values, Calculation):
if current_opt is undefined: values = self._choice_values.execute(option_bag)
current_opt = self if values is not undefined and not isinstance(values, list):
values, values_params = self.get_callback() raise ConfigError(_('calculated values for {0} is not a list'
if values is not None: '').format(self.impl_getname()))
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()))
else: else:
values = self._choice_values values = self._choice_values
return values return values
def _validate(self, def _validate(self,
value, value,
option_bag, option_bag,
current_opt=undefined): current_opt=undefined):
values = self.impl_get_values(option_bag, values = self.impl_get_values(option_bag)
current_opt=current_opt)
if values is not undefined and value not in values: if values is not undefined and value not in values:
if len(values) == 1: if len(values) == 1:
raise ValueError(_('only "{0}" is allowed' raise ValueError(_('only "{0}" is allowed'

View file

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

View file

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

View file

@ -18,15 +18,14 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # 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 ..error import ConfigError
from ..setting import undefined, Undefined, OptionBag from ..setting import undefined, Undefined, OptionBag
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
from .stroption import StrOption from .stroption import StrOption
from .netmaskoption import NetmaskOption from ..function import valid_ip_netmask
from .networkoption import NetworkOption
class IPOption(StrOption): class IPOption(StrOption):
@ -40,12 +39,8 @@ class IPOption(StrOption):
doc, doc,
default=None, default=None,
default_multi=None, default_multi=None,
requires=None,
multi=False, multi=False,
callback=None, validators=None,
callback_params=None,
validator=None,
validator_params=None,
properties=None, properties=None,
private_only=False, private_only=False,
allow_reserved=False, allow_reserved=False,
@ -63,12 +58,8 @@ class IPOption(StrOption):
doc, doc,
default=default, default=default,
default_multi=default_multi, default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi, multi=multi,
validator=validator, validators=validators,
validator_params=validator_params,
properties=properties, properties=properties,
warnings_only=warnings_only, warnings_only=warnings_only,
extra=extra) extra=extra)
@ -98,9 +89,11 @@ class IPOption(StrOption):
if not cidr: if not cidr:
ip_address(value) ip_address(value)
else: else:
ip_interface(value) ip = ip_interface(value)
except ValueError: except ValueError:
raise ValueError() raise ValueError()
if cidr:
valid_ip_netmask(str(ip.ip), str(ip.netmask))
def _second_level_validation(self, def _second_level_validation(self,
value, value,
@ -118,68 +111,3 @@ class IPOption(StrOption):
else: else:
msg = _("must be private IP") msg = _("must be private IP")
raise ValueError(msg) 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 ..i18n import _
from ..setting import groups, undefined, OptionBag, Settings from ..setting import groups, undefined, OptionBag, Settings, ALLOWED_LEADER_PROPERTIES
from ..value import Values from ..value import Values
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .syndynoptiondescription import SynDynLeadership from .syndynoptiondescription import SynDynLeadership
from .baseoption import BaseOption from .baseoption import BaseOption
from .option import Option from .option import Option
from ..error import RequirementError from ..error import LeadershipError
from ..autolib import Calculation, ParamOption from ..autolib import Calculation, ParamOption
@ -43,12 +43,10 @@ class Leadership(OptionDescription):
name: str, name: str,
doc: str, doc: str,
children: List[BaseOption], children: List[BaseOption],
requires=None,
properties=None) -> None: properties=None) -> None:
super().__init__(name, super().__init__(name,
doc, doc,
children, children,
requires=requires,
properties=properties) properties=properties)
self._group_type = groups.leadership self._group_type = groups.leadership
followers = [] followers = []
@ -93,28 +91,9 @@ class Leadership(OptionDescription):
raise ValueError(_("callback of leader's option shall " raise ValueError(_("callback of leader's option shall "
"not refered to a follower's ones")) "not refered to a follower's ones"))
# leader should not have requires, only Leadership should have for prop in leader.impl_getproperties():
# so move requires to Leadership if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
# if Leadership has requires too, cannot manage this move so raises raise LeadershipError(_('leader cannot have "{}" property').format(prop))
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()))
def is_leader(self, def is_leader(self,
opt: Option) -> bool: opt: Option) -> bool:

View file

@ -49,58 +49,3 @@ class NetmaskOption(StrOption):
ip_network('0.0.0.0/{0}'.format(value)) ip_network('0.0.0.0/{0}'.format(value))
except ValueError: except ValueError:
raise 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, doc,
default=None, default=None,
default_multi=None, default_multi=None,
requires=None,
multi=False, multi=False,
callback=None, validators=None,
callback_params=None,
validator=None,
validator_params=None,
properties=None, properties=None,
warnings_only=False, warnings_only=False,
cidr=False): cidr=False):
@ -50,12 +46,8 @@ class NetworkOption(Option):
doc, doc,
default=default, default=default,
default_multi=default_multi, default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi, multi=multi,
validator=validator, validators=validators,
validator_params=validator_params,
properties=properties, properties=properties,
warnings_only=warnings_only, warnings_only=warnings_only,
extra=extra) extra=extra)

View file

@ -27,11 +27,11 @@ from itertools import chain
from .baseoption import BaseOption, submulti, STATIC_TUPLE from .baseoption import BaseOption, submulti, STATIC_TUPLE
from ..i18n import _ from ..i18n import _
from ..setting import undefined, OptionBag, Undefined 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, from ..error import (ConfigError, ValueWarning, ValueErrorWarning, PropertiesOptionError,
ValueOptionError, display_list) ValueOptionError, display_list)
from .syndynoption import SynDynOption from .syndynoption import SynDynOption
ALLOWED_CONST_LIST = ['_cons_not_equal'] #ALLOWED_CONST_LIST = ['_cons_not_equal']
class Option(BaseOption): class Option(BaseOption):
@ -43,14 +43,14 @@ class Option(BaseOption):
__slots__ = ('_extra', __slots__ = ('_extra',
'_warnings_only', '_warnings_only',
'_allow_empty_list', '_allow_empty_list',
#multi # multi
'_multi', '_multi',
'_unique', '_unique',
#value # value
'_default', '_default',
'_default_multi', '_default_multi',
#calcul #
'_val_call', '_validators',
# #
'_leadership', '_leadership',
'_choice_values', '_choice_values',
@ -62,13 +62,9 @@ class Option(BaseOption):
doc: str, doc: str,
default: Any=undefined, default: Any=undefined,
default_multi: Any=None, default_multi: Any=None,
requires: List[Dict]=None,
multi: bool=False, multi: bool=False,
unique: bool=undefined, unique: bool=undefined,
callback: Optional[Callable]=None, validators: Optional[List[Calculation]]=None,
callback_params: Optional[Params]=None,
validator: Optional[Callable]=None,
validator_params: Optional[Params]=None,
properties: Optional[List[str]]=None, properties: Optional[List[str]]=None,
warnings_only: bool=False, warnings_only: bool=False,
extra: Optional[Dict]=None, extra: Optional[Dict]=None,
@ -99,19 +95,23 @@ class Option(BaseOption):
default = [] default = []
super().__init__(name, super().__init__(name,
doc, doc,
requires=requires,
properties=properties, properties=properties,
is_multi=is_multi) is_multi=is_multi)
if validator is not None: if __debug__:
validator_params = self._build_calculator_params(validator, if validators is not None:
validator_params, if not isinstance(validators, list):
'validator', raise ValueError(_('validators must be a list of Calculation for "{}"').format(name))
add_value=True) for validator in validators:
if not validator_params: if not isinstance(validator, Calculation):
val_call = (validator,) raise ValueError(_('validators must be a Calculation for "{}"').format(name))
else: for param in chain(validator.params.args, validator.params.kwargs.values()):
val_call = (validator, validator_params) if isinstance(param, ParamContext):
self._val_call = (val_call, None) 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 != {}: if extra is not None and extra != {}:
_setattr(self, '_extra', extra) _setattr(self, '_extra', extra)
if unique != undefined and not isinstance(unique, bool): if unique != undefined and not isinstance(unique, bool):
@ -126,9 +126,14 @@ class Option(BaseOption):
def test_multi_value(value): def test_multi_value(value):
if isinstance(value, Calculation): if isinstance(value, Calculation):
return return
option_bag = OptionBag()
option_bag.set_option(self,
undefined,
None,
undefined)
try: try:
self._validate(value, self._validate(value,
undefined) option_bag)
except ValueError as err: except ValueError as err:
str_err = str(err) str_err = str(err)
if not str_err: if not str_err:
@ -168,9 +173,6 @@ class Option(BaseOption):
default = tuple(default) default = tuple(default)
_setattr(self, '_default', default) _setattr(self, '_default', default)
self._impl_set_callback(callback,
callback_params)
def value_dependencies(self, def value_dependencies(self,
value: Any) -> Any: value: Any) -> Any:
if isinstance(value, list): if isinstance(value, list):
@ -250,16 +252,6 @@ class Option(BaseOption):
#__________________________________________________________________________ #__________________________________________________________________________
# validator # 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, def impl_validate(self,
value: Any, value: Any,
option_bag: OptionBag, option_bag: OptionBag,
@ -272,13 +264,9 @@ class Option(BaseOption):
if check_error and config_bag is not undefined and \ if check_error and config_bag is not undefined and \
not 'validator' in config_bag.properties: not 'validator' in config_bag.properties:
# just to check propertieserror
self.valid_consistency(option_bag,
value,
check_error,
is_warnings_only)
return return
def _is_not_unique(value): def _is_not_unique(value):
# if set(value) has not same length than value # if set(value) has not same length than value
if check_error and self.impl_is_unique() and \ if check_error and self.impl_is_unique() and \
@ -291,26 +279,33 @@ class Option(BaseOption):
def calculation_validator(val, def calculation_validator(val,
_index): _index):
validator, validator_params = self.impl_get_validator() for validator in getattr(self, '_validators', []):
if validator is not None: calc_is_warnings_only = hasattr(validator, 'warnings_only') and validator.warnings_only
#inject value in calculation if ((check_error and not calc_is_warnings_only) or
if validator_params is None: (not check_error and calc_is_warnings_only)):
args = [] try:
kwargs = None kwargs = {'allow_raises': True}
else: if _index is not None and option_bag.index == _index:
args = list(validator_params.args) soption_bag = option_bag
kwargs = validator_params.kwargs else:
args.insert(0, ParamValue(val)) soption_bag = option_bag.copy()
validator_params_ = Params(tuple(args), kwargs) soption_bag.index = _index
# Raise ValueError if not valid kwargs['orig_value'] = value
carry_out_calculation(option_bag.ori_option,
callback=validator, validator.execute(soption_bag,
callback_params=validator_params_, leadership_must_have_index=True,
index=_index, **kwargs)
config_bag=option_bag.config_bag, except ValueError as err:
fromconsistency=option_bag.fromconsistency, if calc_is_warnings_only:
orig_value=value, warnings.warn_explicit(ValueWarning(val,
is_validator=True) self._display_name,
self,
'{0}'.format(err),
_index),
ValueWarning,
self.__class__.__name__, 0)
else:
raise err
def do_validation(_value, def do_validation(_value,
_index): _index):
@ -328,8 +323,6 @@ class Option(BaseOption):
if ((check_error and not is_warnings_only) or if ((check_error and not is_warnings_only) or
(not check_error and is_warnings_only)): (not check_error and is_warnings_only)):
try: try:
calculation_validator(_value,
_index)
self._second_level_validation(_value, self._second_level_validation(_value,
is_warnings_only) is_warnings_only)
except ValueError as err: except ValueError as err:
@ -343,6 +336,8 @@ class Option(BaseOption):
self.__class__.__name__, 0) self.__class__.__name__, 0)
else: else:
raise err raise err
calculation_validator(_value,
_index)
try: try:
val = value val = value
err_index = force_index err_index = force_index
@ -379,12 +374,6 @@ class Option(BaseOption):
for err_index, val in enumerate(value): for err_index, val in enumerate(value):
do_validation(val, do_validation(val,
err_index) 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: except ValueError as err:
if config_bag is undefined or \ if config_bag is undefined or \
'demoting_error_warning' not in config_bag.properties: 'demoting_error_warning' not in config_bag.properties:
@ -419,11 +408,6 @@ class Option(BaseOption):
warnings_only: bool) -> None: warnings_only: bool) -> None:
pass pass
#__________________________________________________________________________
# leadership
# def impl_is_leadership(self):
# return self.impl_get_leadership() is not None
def impl_is_leader(self): def impl_is_leader(self):
leadership = self.impl_get_leadership() leadership = self.impl_get_leadership()
if leadership is None: if leadership is None:
@ -442,327 +426,6 @@ class Option(BaseOption):
return leadership return leadership
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, def to_dynoption(self,
rootpath: str, rootpath: str,
suffix: str) -> SynDynOption: suffix: str) -> SynDynOption:

View file

@ -25,14 +25,12 @@ from typing import Optional, Iterator, Union, List
from ..i18n import _ from ..i18n import _
from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption from .baseoption import BaseOption
from .option import ALLOWED_CONST_LIST
from .syndynoptiondescription import SynDynOptionDescription, SynDynLeadership from .syndynoptiondescription import SynDynOptionDescription, SynDynLeadership
from ..error import ConfigError, ConflictError from ..error import ConfigError, ConflictError
class CacheOptionDescription(BaseOption): class CacheOptionDescription(BaseOption):
__slots__ = ('_cache_consistencies', __slots__ = ('_cache_force_store_values',)
'_cache_force_store_values')
def impl_already_build_caches(self) -> bool: def impl_already_build_caches(self) -> bool:
return self.impl_is_readonly() return self.impl_is_readonly()
@ -101,60 +99,10 @@ class CacheOptionDescription(BaseOption):
'"force_metaconfig_on_freeze" ' '"force_metaconfig_on_freeze" '
'property without "frozen"' 'property without "frozen"'
'').format(option.impl_get_display_name())) '').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 context is set to callback, must be reset each time a value change
if hasattr(option, '_has_calc_context'): if hasattr(option, '_has_calc_context'):
self._add_dependency(option) 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(): if option.impl_is_readonly():
raise ConflictError(_('duplicate option: {0}').format(option)) raise ConflictError(_('duplicate option: {0}').format(option))
if not self.impl_is_readonly() and display_name: if not self.impl_is_readonly() and display_name:
@ -162,15 +110,6 @@ class CacheOptionDescription(BaseOption):
option._path = subpath option._path = subpath
option._set_readonly() option._set_readonly()
if init: 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._cache_force_store_values = force_store_values
self._path = self._name self._path = self._name
self._set_readonly() self._set_readonly()
@ -275,7 +214,6 @@ class OptionDescription(OptionDescriptionWalk):
name: str, name: str,
doc: str, doc: str,
children: List[BaseOption], children: List[BaseOption],
requires=None,
properties=None) -> None: properties=None) -> None:
""" """
:param children: a list of options (including optiondescriptions) :param children: a list of options (including optiondescriptions)
@ -285,7 +223,6 @@ class OptionDescription(OptionDescriptionWalk):
'must be a list').format(name) 'must be a list').format(name)
super().__init__(name, super().__init__(name,
doc=doc, doc=doc,
requires=requires,
properties=properties) properties=properties)
child_names = [] child_names = []
if __debug__: if __debug__:

View file

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

View file

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

View file

@ -64,24 +64,11 @@ class SynDynOption:
def impl_getpath(self) -> str: def impl_getpath(self) -> str:
return self.rootpath + '.' + self.impl_getname() 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: def impl_is_dynsymlinkoption(self) -> bool:
return True return True
def impl_get_leadership(self): def impl_get_leadership(self):
return self.opt.impl_get_leadership().to_dynoption(self.rootpath, leadership = self.opt.impl_get_leadership()
self.suffix) 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 # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ____________________________________________________________ # ____________________________________________________________
from .error import (RequirementError, PropertiesOptionError, from .error import PropertiesOptionError, ConstError, ConfigError, LeadershipError, display_list
ConstError, ConfigError, display_list)
from .i18n import _ from .i18n import _
@ -109,7 +108,12 @@ FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value'])
FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze', FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze',
'force_metaconfig_on_freeze', 'force_metaconfig_on_freeze',
'force_store_value']) 'force_store_value'])
ALLOWED_LEADER_PROPERTIES = frozenset(['empty',
'force_store_value',
'mandatory',
'force_default_on_freeze',
'force_metaconfig_on_freeze',
'frozen'])
static_set = frozenset() static_set = frozenset()
@ -123,12 +127,10 @@ class OptionBag:
'properties', # properties of current option 'properties', # properties of current option
'properties_setted', 'properties_setted',
'apply_requires', # apply requires or not for this option 'apply_requires', # apply requires or not for this option
'fromconsistency' # history for consistency
) )
def __init__(self): def __init__(self):
self.option = None self.option = None
self.fromconsistency = []
def set_option(self, def set_option(self,
option, option,
@ -235,9 +237,6 @@ class ConfigBag:
return return
raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover
# def __setattr__(self, key, value):
# super().__setattr__(key, value)
def copy(self): def copy(self):
kwargs = {} kwargs = {}
for key in self.__slots__: for key in self.__slots__:
@ -447,24 +446,19 @@ class Settings(object):
if isinstance(prop, str): if isinstance(prop, str):
props.add(prop) props.add(prop)
elif apply_requires: elif apply_requires:
new_props = prop.execute(option_bag, new_prop = prop.execute(option_bag,
leadership_must_have_index=True) leadership_must_have_index=True)
if not new_props: if not new_prop:
continue continue
elif not isinstance(new_props, str): elif not isinstance(new_prop, str):
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_props), raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_prop),
option_bag.option.impl_getname(), option_bag.option.impl_getname(),
prop.function.__name__)) prop.function.__name__))
props.add(new_props) if not option.impl_is_optiondescription() and option.impl_is_leader() and new_prop not in ALLOWED_LEADER_PROPERTIES:
# else: raise LeadershipError(_('leader cannot have "{}" property').format(new_prop))
# props.update(new_props) props.add(new_prop)
if apply_requires:
props |= self.apply_requires(option_bag,
False,
search_properties=search_properties)
props -= self.getpermissives(option, props -= self.getpermissives(option,
path) path)
#if apply_requires and config_bag.properties == config_bag.true_properties:
if apply_requires and not config_bag.is_unrestraint: if apply_requires and not config_bag.is_unrestraint:
cache.setcache(path, cache.setcache(path,
index, index,
@ -508,195 +502,6 @@ class Settings(object):
path = opt.impl_getpath() path = opt.impl_getpath()
return self._pp_.getpermissives(path) 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 # set methods
def set_context_properties(self, def set_context_properties(self,
@ -715,23 +520,21 @@ class Settings(object):
(never save properties if same has option properties) (never save properties if same has option properties)
""" """
opt = option_bag.option 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(): if opt.impl_is_symlinkoption():
raise TypeError(_("can't assign property to the symlinkoption \"{}\"" raise TypeError(_("can't assign property to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name())) "").format(opt.impl_get_display_name()))
if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \ if not opt.impl_is_optiondescription() and opt.impl_is_leader():
'frozen' not in properties and \ not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
opt.impl_is_leader(): if not_allowed_properties:
raise ConfigError(_('a leader ({0}) cannot have ' if len(not_allowed_properties) == 1:
'"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"' raise LeadershipError(_('leader cannot have "{}" property').format(list(not_allowed_properties)[0]))
'').format(opt.impl_get_display_name())) 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, self._p_.setproperties(path,
properties) properties)
# values too because of follower values could have a PropertiesOptionError has value # 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)) raise ValueError(_('context is not supported from now for {}').format(path))
if isinstance(callback_param, ParamOption): if isinstance(callback_param, ParamOption):
has_option = True 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) self.tiramisu_web.set_remotable(callback_param.option.impl_getpath(), form)
if not has_option and form.get(path, {}).get('remote', False) == False: if not has_option and form.get(path, {}).get('remote', False) == False:
if 'expire' in childapi.option.properties(): if 'expire' in childapi.option.properties():
@ -79,14 +79,15 @@ class Callbacks(object):
form.setdefault(path, {})['clearable'] = True form.setdefault(path, {})['clearable'] = True
def manage_callbacks(self, form): def manage_callbacks(self, form):
for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks: pass
if callback_params is not None: #for callback, callback_params, path, childapi, schema, force_store_value in self.callbacks:
for callback_param in chain(callback_params.args, callback_params.kwargs.values()): # if callback_params is not None:
if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy': # for callback_param in chain(callback_params.args, callback_params.kwargs.values()):
opt_path = callback_param.option.impl_getpath() # if isinstance(callback_param, ParamOption) and callback.__name__ == 'tiramisu_copy':
if form.get(opt_path, {}).get('remote') is not True: # opt_path = callback_param.option.impl_getpath()
form.setdefault(opt_path, {}) # if form.get(opt_path, {}).get('remote') is not True:
form[opt_path].setdefault('copy', []).append(path) # form.setdefault(opt_path, {})
# form[opt_path].setdefault('copy', []).append(path)
def process(self, def process(self,
form): form):

View file

@ -17,7 +17,7 @@
# ____________________________________________________________ # ____________________________________________________________
import weakref import weakref
from typing import Optional, Any, Callable 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 .setting import owners, undefined, forbidden_owners, OptionBag, ConfigBag
from .autolib import Calculation, carry_out_calculation, Params from .autolib import Calculation, carry_out_calculation, Params
from .i18n import _ from .i18n import _
@ -78,7 +78,7 @@ class Values(object):
# store value in cache # store value in cache
properties = option_bag.config_bag.properties properties = option_bag.config_bag.properties
validator = 'validator' in properties and 'demoting_error_warning' not in 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, cache.setcache(option_bag.path,
option_bag.index, option_bag.index,
value, value,
@ -258,8 +258,7 @@ class Values(object):
callback=callback, callback=callback,
callback_params=callback_params, callback_params=callback_params,
index=option_bag.index, index=option_bag.index,
config_bag=option_bag.config_bag, config_bag=option_bag.config_bag)
fromconsistency=option_bag.fromconsistency)
def isempty(self, def isempty(self,
opt, opt,
value, value,
@ -289,24 +288,8 @@ class Values(object):
context = option_bag.config_bag.context context = option_bag.config_bag.context
owner = self.get_context_owner() owner = self.get_context_owner()
if 'validator' in option_bag.config_bag.properties: if 'validator' in option_bag.config_bag.properties:
if option_bag.index is not None or option_bag.option.has_consistencies(context): self.setvalue_validation(value,
# set value to a fake config when option has dependency option_bag)
# 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(option_bag, self._setvalue(option_bag,
value, value,
@ -622,7 +605,7 @@ class Values(object):
except PropertiesOptionError as err: except PropertiesOptionError as err:
if err.proptype == ['mandatory']: if err.proptype == ['mandatory']:
yield path yield path
except (RequirementError, ConfigError): except ConfigError:
pass pass
def mandatory_warnings(self, def mandatory_warnings(self,