From 482dfec7f2811d872747bd33e1b0db9df4298ba0 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Fri, 27 Sep 2013 23:26:10 +0200 Subject: [PATCH] consistancies can have more than one option add _cons_broadcast --- test/test_option_consistency.py | 21 +++- test/test_state.py | 3 +- tiramisu/option.py | 191 ++++++++++++++++++-------------- 3 files changed, 130 insertions(+), 85 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 5cf53cd..1c23dac 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -4,7 +4,7 @@ from py.test import raises from tiramisu.setting import owners, groups from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ - SymLinkOption, OptionDescription + BroadcastOption, SymLinkOption, OptionDescription def test_consistency_not_equal(): @@ -159,3 +159,22 @@ def test_consistency_network_netmask_multi_master(): c.a = ['192.168.1.0'] c.b = ['255.255.255.0'] raises(ValueError, "c.a = ['192.168.1.1']") + + +def test_consistency_broadcast(): + a = NetworkOption('a', '', multi=True) + b = NetmaskOption('b', '', multi=True) + c = BroadcastOption('c', '', multi=True) + od = OptionDescription('a', '', [a, b, c]) + od.impl_set_group_type(groups.master) + b.impl_add_consistency('network_netmask', a) + c.impl_add_consistency('broadcast', a, b) + c = Config(od) + c.a = ['192.168.1.0'] + c.b = ['255.255.255.0'] + c.c = ['192.168.1.255'] + raises(ValueError, "c.a = ['192.168.1.1']") + c.a = ['192.168.1.0', '192.168.2.128'] + c.b = ['255.255.255.0', '255.255.255.128'] + c.c = ['192.168.1.255', '192.168.2.255'] + raises(ValueError, "c.c[1] = '192.168.2.128'") diff --git a/test/test_state.py b/test/test_state.py index 8587b4a..4430bd9 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -72,7 +72,8 @@ def _diff_opt(opt1, opt2): if isinstance(val1, list): for index, consistency in enumerate(val1): assert consistency[0] == val2[index][0] - assert consistency[1]._name == val2[index][1]._name + for idx, opt in enumerate(consistency[1]): + assert opt._name == val2[index][1][idx]._name elif attr == '_callback': assert val1[0] == val2[0] if val1[1] is not None: diff --git a/tiramisu/option.py b/tiramisu/option.py index f9a3541..d3c4bf9 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -27,7 +27,7 @@ from types import FunctionType from IPy import IP import warnings -from tiramisu.error import ConflictError, ValueWarning +from tiramisu.error import ConfigError, ConflictError, ValueWarning from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -172,14 +172,13 @@ class BaseOption(object): if isinstance(consistencies, list): new_value = [] for consistency in consistencies: - if load: - new_value.append((consistency[0], - descr.impl_get_opt_by_path( - consistency[1]))) - else: - new_value.append((consistency[0], - descr.impl_get_path_by_opt( - consistency[1]))) + values = [] + for obj in consistency[1]: + if load: + values.append(descr.impl_get_opt_by_path(obj)) + else: + values.append(descr.impl_get_path_by_opt(obj)) + new_value.append((consistency[0], tuple(values))) else: new_value = {} @@ -395,50 +394,62 @@ class Option(BaseOption): self.impl_validate(default) self._default = default - def _launch_consistency(self, func, opt, vals, context, index, opt_): + def _launch_consistency(self, func, right_opt, right_val, context, index, + left_opts): if context is not None: descr = context.cfgimpl_get_description() - if opt is self: - #values are for self, search opt_ values - values = vals - if context is not None: - path = descr.impl_get_path_by_opt(opt_) - values_ = context._getattr(path, validate=False) + #right_opt is also in left_opts + if right_opt not in left_opts: + raise ConfigError(_('right_opt not in left_opts')) + + left_vals = [] + for opt in left_opts: + if right_opt == opt: + value = right_val + else: + if context is not None: + path = descr.impl_get_path_by_opt(opt) + value = context._getattr(path, validate=False) + else: + value = opt.impl_getdefault() + if index is None: + #could be multi or not + left_vals.append(value) else: - values_ = opt_.impl_getdefault() - if index is not None: #value is not already set, could be higher try: - values_ = values_[index] + if right_opt == opt: + val = value + else: + val = value[index] + if val is None: + #no value so no consistencies + return + left_vals.append(val) except IndexError: - values_ = None - else: - #values are for opt_, search self values - values_ = vals - if context is not None: - path = descr.impl_get_path_by_opt(self) - values = context._getattr(path, validate=False) + #so return if no value + return + + if self.impl_is_multi(): + if index is None: + for idx, right_v in enumerate(right_val): + try: + left_v = [] + for left_val in left_vals: + left_v.append(left_val[idx]) + if None in left_v: + continue + except IndexError: + continue + getattr(self, func)(left_opts, left_v) else: - values = self.impl_getdefault() - if index is not None: - #value is not already set, could be higher - try: - values = values[index] - except IndexError: - values = None - if index is None and self.impl_is_multi(): - for index in range(0, len(values)): - try: - value = values[index] - value_ = values_[index] - except IndexError: - value = None - value_ = None - if None not in (value, value_): - getattr(self, func)(opt_._name, value, value_) + if None in left_vals: + return + getattr(self, func)(left_opts, left_vals) else: - if None not in (values, values_): - getattr(self, func)(opt_._name, values, values_) + if None in left_vals: + return + getattr(self, func)(left_opts, left_vals) def impl_validate(self, value, context=None, validate=True, force_no_multi=False): @@ -550,29 +561,31 @@ class Option(BaseOption): def impl_is_multi(self): return self._multi - def impl_add_consistency(self, func, opt): + def impl_add_consistency(self, func, *left_opts): if self._consistencies is None: self._consistencies = [] - if not isinstance(opt, Option): - raise ValueError('consistency must be set with an option') - if self is opt: - raise ValueError('cannot add consistency with itself') - if self.impl_is_multi() != opt.impl_is_multi(): - raise ValueError('options in consistency' - ' should be multi in two sides') + for opt in left_opts: + if not isinstance(opt, Option): + raise ValueError(_('consistency should be set with an option')) + if self is opt: + raise ValueError(_('cannot add consistency with itself')) + if self.impl_is_multi() != opt.impl_is_multi(): + raise ValueError(_('options in consistency should be multi in ' + 'two sides')) func = '_cons_{0}'.format(func) - self._launch_consistency(func, - self, - self.impl_getdefault(), - None, None, opt) - self._consistencies.append((func, opt)) + opts = tuple([self] + list(left_opts)) + self._launch_consistency(func, self, self.impl_getdefault(), None, + None, opts) + self._consistencies.append((func, opts)) self.impl_validate(self.impl_getdefault()) - def _cons_not_equal(self, optname, value, value_): - if value == value_: + def _cons_not_equal(self, opts, vals): + if len(opts) != 2: + raise ConfigError(_('invalid len for opts')) + if vals[0] == vals[1]: raise ValueError(_("invalid value {0} for option {1} " "must be different as {2} option" - "").format(value, self._name, optname)) + "").format(vals[0], self._name, opts[1]._name)) def _impl_convert_callbacks(self, descr, load=False): if not load and self._callback is None: @@ -889,15 +902,17 @@ class NetmaskOption(Option): except ValueError: raise ValueError(_('invalid netmask address {0}').format(self._name)) - def _cons_network_netmask(self, optname, value, value_): + def _cons_network_netmask(self, opts, vals): #opts must be (netmask, network) options - self.__cons_netmask(optname, value, value_, False) + self.__cons_netmask(opts, vals[0], vals[1], False) - def _cons_ip_netmask(self, optname, value, value_): + def _cons_ip_netmask(self, opts, vals): #opts must be (netmask, ip) options - self.__cons_netmask(optname, value, value_, True) + self.__cons_netmask(opts, vals[0], vals[1], True) - def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net): + def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net): + if len(opts) != 2: + raise ConfigError(_('invalid len for opts')) msg = None try: ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), @@ -923,17 +938,30 @@ class NetmaskOption(Option): else: msg = _("invalid network {0} ({1}) with netmask {2} ({3})") if msg is not None: - raise ValueError(msg.format(val_ipnetwork, optname, + raise ValueError(msg.format(val_ipnetwork, opts[1]._name, val_netmask, self._name)) class BroadcastOption(Option): + __slots__ = tuple() + _opt_type = 'broadcast' + def _validate(self, value): try: IP('{0}/32'.format(value)) except ValueError: raise ValueError(_('invalid broadcast address {0}').format(self._name)) + def _cons_broadcast(self, opts, vals): + if len(vals) != 3: + raise ConfigError(_('invalid len for vals')) + broadcast, network, netmask = vals + if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast): + raise ValueError(_('invalid broadcast {0} ({1}) with network {2} ' + '({3}) and netmask {4} ({5})').format( + broadcast, opts[0]._name, network, + opts[1]._name, netmask, opts[2]._name)) + class DomainnameOption(Option): "represents the choice of a domain name" @@ -1098,12 +1126,11 @@ class OptionDescription(BaseOption): if not force_no_consistencies and \ option._consistencies is not None: for consistency in option._consistencies: - func, opt = consistency - opts = (option, opt) - _consistencies.setdefault(opt, - []).append((func, opts)) - _consistencies.setdefault(option, - []).append((func, opts)) + func, left_opts = consistency + for opt in left_opts: + _consistencies.setdefault(opt, + []).append((func, + left_opts)) else: _currpath.append(attr) option.impl_build_cache(cache_path, @@ -1186,17 +1213,15 @@ class OptionDescription(BaseOption): def impl_get_group_type(self): return self._group_type - def _valid_consistency(self, opt, value, context=None, index=None): - consistencies = self._consistencies.get(opt) + def _valid_consistency(self, right_opt, right_val, context=None, index=None): + #[('_cons_not_equal', (opt1, opt2))] + consistencies = self._consistencies.get(right_opt) if consistencies is not None: - for consistency in consistencies: - opt_ = consistency[1] - ret = opt_[0]._launch_consistency(consistency[0], - opt, - value, - context, - index, - opt_[1]) + for func, opts in consistencies: + #opts[0] is the option where func is set + #opts is left_opts + ret = opts[0]._launch_consistency(func, right_opt, right_val, + context, index, opts) if ret is False: return False return True