TiramisuOption.list() with follower

This commit is contained in:
egarette@silique.fr 2023-06-26 19:25:57 +02:00
parent ad31fc85bb
commit 3641eb39d8
6 changed files with 51 additions and 21 deletions

View file

@ -268,7 +268,7 @@ def test_cache_leadership():
assert set(cache.keys()) == set([None, 'ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0']) assert set(cache.keys()) == set([None, 'ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
assert set(cache['ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([0, None]) assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == {0}
# #
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.1']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.1'])
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
@ -286,7 +286,7 @@ def test_cache_leadership():
assert set(cache.keys()) == set([None, 'ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0']) assert set(cache.keys()) == set([None, 'ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
assert set(cache['ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([None, 0, 1]) assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([0, 1])
#DEL, insert, ... #DEL, insert, ...
# assert not list_sessions() # assert not list_sessions()
@ -398,19 +398,20 @@ def test_cache_leader_and_followers():
val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)} val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)}
compare(settings.get_cached(), {None: {None: (set(global_props), None)}, compare(settings.get_cached(), {None: {None: (set(global_props), None)},
'val1.val1': {None: (val1_val1_props, None)}, 'val1.val1': {None: (val1_val1_props, None)},
'val1.val2': val_val2_props}) })
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}}) compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.dict() cfg.value.dict()
#has value #has value
idx_val2 = 0 idx_val2 = 0
val_val2 = None val_val2 = None
val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)} val_val2_props = {idx_val2: (val1_val2_props, None)}
compare(settings.get_cached(), {None: {None: (global_props, None)}, compare(settings.get_cached(), {None: {None: (global_props, None)},
'val1': {None: (val1_props, None)}, 'val1': {None: (val1_props, None)},
'val1.val1': {None: (val1_val1_props, None)}, 'val1.val1': {None: (val1_val1_props, None)},
'val1.val2': val_val2_props}) 'val1.val2': val_val2_props})
compare(values.get_cached(), {'val1.val1': {None: ([None], None)}, compare(values.get_cached(), {'val1.val1': {None: ([None], None)},
'val1.val2': {idx_val2: (val_val2, None)}}) 'val1.val2': {idx_val2: (val_val2, None)},
})
cfg.option('val1.val1').value.set([undefined, undefined]) cfg.option('val1.val1').value.set([undefined, undefined])
cfg.value.dict() cfg.value.dict()
cfg.option('val1.val2', 1).value.set('oui') cfg.option('val1.val2', 1).value.set('oui')
@ -446,7 +447,7 @@ def test_cache_leader_callback():
cfg.option('val1.val1').value.set([undefined]) cfg.option('val1.val1').value.set([undefined])
compare(settings.get_cached(), {None: {None: (set(global_props), None)}, compare(settings.get_cached(), {None: {None: (set(global_props), None)},
'val1.val1': {None: (val1_val1_props, None)}, 'val1.val1': {None: (val1_val1_props, None)},
'val1.val2': {None: (val1_val2_props, None)}}) })
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}}) compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.dict() cfg.value.dict()

View file

@ -880,3 +880,17 @@ def test_pprint_not_todict():
def test_property_invalid_type(): def test_property_invalid_type():
with pytest.raises(ValueError): with pytest.raises(ValueError):
s3 = StrOption("string3", "", default=["string"], default_multi="string", multi=True, properties=(1,)) s3 = StrOption("string3", "", default=["string"], default_multi="string", multi=True, properties=(1,))
def test_settings_list_with_follower():
leader = StrOption("leader", "leader", default=['leader'], multi=True)
option = StrOption("str", "str", default_multi="dhcp", multi=True, properties=frozenset({'disabled'}))
ip = StrOption(name="ip",
doc="ip",
multi=True,
properties=frozenset({"basic", "mandatory", Calculation(calc_value, Params(ParamValue('disabled'), kwargs={'condition': ParamOption(option, notraisepropertyerror=True), 'expected': ParamValue("ipv4"), 'reverse_condition': ParamValue(True)}), calc_value_property_help)}),
)
descr = Leadership("root", "", [leader, option, ip])
cfg = Config(OptionDescription('root', 'root', [descr]))
cfg.property.read_write()
assert len(cfg.option('root').list('all')) == 2

View file

@ -89,12 +89,15 @@ class TiramisuHelp:
class CommonTiramisu(TiramisuHelp): class CommonTiramisu(TiramisuHelp):
_validate_properties = True _validate_properties = True
def _get_options_bag(self) -> OptionBag: def _get_options_bag(self,
follower_not_apply_requires: bool,
) -> OptionBag:
try: try:
options_bag = self._config_bag.context.get_sub_option_bag(self._config_bag, options_bag = self._config_bag.context.get_sub_option_bag(self._config_bag,
self._path, self._path,
self._index, self._index,
self._validate_properties, self._validate_properties,
follower_not_apply_requires=follower_not_apply_requires,
) )
except AssertionError as err: except AssertionError as err:
raise ConfigError(str(err)) raise ConfigError(str(err))
@ -122,7 +125,7 @@ def option_type(typ):
)] )]
kwargs['is_group'] = True kwargs['is_group'] = True
return func(self, options_bag, *args[1:], **kwargs) return func(self, options_bag, *args[1:], **kwargs)
options_bag = self._get_options_bag() options_bag = self._get_options_bag('with_index' not in types)
option = options_bag[-1].option option = options_bag[-1].option
if option.impl_is_optiondescription() and 'optiondescription' in types or \ if option.impl_is_optiondescription() and 'optiondescription' in types or \
not option.impl_is_optiondescription() and ( not option.impl_is_optiondescription() and (
@ -135,12 +138,13 @@ def option_type(typ):
if not option.impl_is_optiondescription() and \ if not option.impl_is_optiondescription() and \
not option.impl_is_symlinkoption() and \ not option.impl_is_symlinkoption() and \
option.impl_is_follower(): option.impl_is_follower():
# default is "without_index"
if 'with_index' not in types and 'with_or_without_index' not in types and \ if 'with_index' not in types and 'with_or_without_index' not in types and \
self._index is not None: self._index is not None:
msg = _('please do not specify index ' msg = _('please do not specify index '
f'({self.__class__.__name__}.{func.__name__})') f'({self.__class__.__name__}.{func.__name__})')
raise ConfigError(_(msg)) raise ConfigError(_(msg))
if 'with_index' in types and self._index is None: if self._index is None and 'with_index' in types:
msg = _('please specify index with a follower option ' msg = _('please specify index with a follower option '
f'({self.__class__.__name__}.{func.__name__})') f'({self.__class__.__name__}.{func.__name__})')
raise ConfigError(msg) raise ConfigError(msg)
@ -251,7 +255,7 @@ class _TiramisuOptionOptionDescription:
option_bag = options_bag[-1] option_bag = options_bag[-1]
return option_bag.path return option_bag.path
@option_type(['optiondescription', 'option', 'symlink']) @option_type(['optiondescription', 'option', 'symlink', 'with_or_without_index'])
def has_dependency(self, def has_dependency(self,
options_bag: List[OptionBag], options_bag: List[OptionBag],
self_is_dep=True, self_is_dep=True,
@ -355,7 +359,7 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
return 'optiondescription' return 'optiondescription'
return option_bag.option.get_type() return option_bag.option.get_type()
@option_type('option') @option_type(['option', 'with_or_without_index'])
def pattern(self, options_bag: List[OptionBag]) -> str: def pattern(self, options_bag: List[OptionBag]) -> str:
"""Get the option pattern""" """Get the option pattern"""
option_bag = options_bag[-1] option_bag = options_bag[-1]

View file

@ -539,6 +539,8 @@ def carry_out_calculation(option,
- 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 not option.impl_is_optiondescription() and option.impl_is_follower() and index is None:
raise Exception('follower must have index in carry_out_calculation!')
def fake_items(iterator): def fake_items(iterator):
return ((None, i) for i in iterator) return ((None, i) for i in iterator)
args = [] args = []

View file

@ -342,6 +342,7 @@ class _SubConfig:
opt.impl_getpath(), opt.impl_getpath(),
None, None,
True, True,
follower_not_apply_requires=flatten_leadership,
)[-1], )[-1],
types=types, types=types,
recursive=recursive, recursive=recursive,
@ -721,6 +722,7 @@ class _CommonConfig(_SubConfig):
validate_properties: bool, validate_properties: bool,
leadership_length: int=None, leadership_length: int=None,
properties=undefined, properties=undefined,
follower_not_apply_requires: bool=False,
) -> List[OptionBag]: ) -> List[OptionBag]:
"""Get the suboption for path and the name of the option """Get the suboption for path and the name of the option
:returns: tuple (config, name)""" :returns: tuple (config, name)"""
@ -734,12 +736,12 @@ class _CommonConfig(_SubConfig):
option_bag = bag option_bag = bag
if option_bag.option != option_bag.config_bag.context.get_description(): if option_bag.option != option_bag.config_bag.context.get_description():
path = path[len(option_bag.path) + 1:] path = path[len(option_bag.path) + 1:]
path = path.split('.') split_path = path.split('.')
last_idx = len(path) - 1 last_idx = len(split_path) - 1
suboption = option_bag.option suboption = option_bag.option
options_bag = [] options_bag = []
sub_option_bag = option_bag sub_option_bag = option_bag
for idx, step in enumerate(path): for idx, step in enumerate(split_path):
if not suboption.impl_is_optiondescription(): if not suboption.impl_is_optiondescription():
raise TypeError(f'{suboption.impl_getpath()} is not an optiondescription') raise TypeError(f'{suboption.impl_getpath()} is not an optiondescription')
@ -753,9 +755,9 @@ class _CommonConfig(_SubConfig):
) )
if idx == last_idx: if idx == last_idx:
option_index = index option_index = index
else: apply_requires = not follower_not_apply_requires or \
option_index = None option.impl_is_optiondescription() or \
if idx == last_idx: not option.impl_is_follower()
if option_index is not None: if option_index is not None:
if option.impl_is_optiondescription() or \ if option.impl_is_optiondescription() or \
option.impl_is_symlinkoption() or \ option.impl_is_symlinkoption() or \
@ -771,11 +773,14 @@ class _CommonConfig(_SubConfig):
f'"{option.impl_get_display_name()}"')) f'"{option.impl_get_display_name()}"'))
option_properties = properties option_properties = properties
else: else:
option_index = None
apply_requires = True
option_properties = undefined option_properties = undefined
sub_option_bag = OptionBag(option, sub_option_bag = OptionBag(option,
option_index, option_index,
option_bag.config_bag, option_bag.config_bag,
properties=option_properties, properties=option_properties,
apply_requires=apply_requires,
) )
if validate_properties: if validate_properties:
self.get_settings().validate_properties(sub_option_bag) self.get_settings().validate_properties(sub_option_bag)

View file

@ -165,16 +165,20 @@ class Leadership(OptionDescription):
dyn = self dyn = self
values = config_bag.context.get_values() values = config_bag.context.get_values()
for idx, follower in enumerate(dyn.get_children(config_bag)): for idx, follower in enumerate(dyn.get_children(config_bag)):
if not idx:
# it's a master
apply_requires = True
indexes = [None]
else:
apply_requires = False
indexes = range(len(value))
foption_bag = OptionBag(follower, foption_bag = OptionBag(follower,
None, None,
config_bag, config_bag,
apply_requires=apply_requires,
) )
if 'force_store_value' not in foption_bag.properties: if 'force_store_value' not in foption_bag.properties:
continue continue
if idx == 0:
indexes = [None]
else:
indexes = range(len(value))
for index in indexes: for index in indexes:
foption_bag_index = OptionBag(follower, foption_bag_index = OptionBag(follower,
index, index,