consistancies can have more than one option

add _cons_broadcast
This commit is contained in:
Emmanuel Garette 2013-09-27 23:26:10 +02:00
parent 2490d00935
commit 482dfec7f2
3 changed files with 130 additions and 85 deletions

View File

@ -4,7 +4,7 @@ from py.test import raises
from tiramisu.setting import owners, groups from tiramisu.setting import owners, groups
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
SymLinkOption, OptionDescription BroadcastOption, SymLinkOption, OptionDescription
def test_consistency_not_equal(): def test_consistency_not_equal():
@ -159,3 +159,22 @@ def test_consistency_network_netmask_multi_master():
c.a = ['192.168.1.0'] c.a = ['192.168.1.0']
c.b = ['255.255.255.0'] c.b = ['255.255.255.0']
raises(ValueError, "c.a = ['192.168.1.1']") 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'")

View File

@ -72,7 +72,8 @@ def _diff_opt(opt1, opt2):
if isinstance(val1, list): if isinstance(val1, list):
for index, consistency in enumerate(val1): for index, consistency in enumerate(val1):
assert consistency[0] == val2[index][0] 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': elif attr == '_callback':
assert val1[0] == val2[0] assert val1[0] == val2[0]
if val1[1] is not None: if val1[1] is not None:

View File

@ -27,7 +27,7 @@ from types import FunctionType
from IPy import IP from IPy import IP
import warnings import warnings
from tiramisu.error import ConflictError, ValueWarning from tiramisu.error import ConfigError, ConflictError, ValueWarning
from tiramisu.setting import groups, multitypes from tiramisu.setting import groups, multitypes
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
@ -172,14 +172,13 @@ class BaseOption(object):
if isinstance(consistencies, list): if isinstance(consistencies, list):
new_value = [] new_value = []
for consistency in consistencies: for consistency in consistencies:
if load: values = []
new_value.append((consistency[0], for obj in consistency[1]:
descr.impl_get_opt_by_path( if load:
consistency[1]))) values.append(descr.impl_get_opt_by_path(obj))
else: else:
new_value.append((consistency[0], values.append(descr.impl_get_path_by_opt(obj))
descr.impl_get_path_by_opt( new_value.append((consistency[0], tuple(values)))
consistency[1])))
else: else:
new_value = {} new_value = {}
@ -395,50 +394,62 @@ class Option(BaseOption):
self.impl_validate(default) self.impl_validate(default)
self._default = 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: if context is not None:
descr = context.cfgimpl_get_description() descr = context.cfgimpl_get_description()
if opt is self: #right_opt is also in left_opts
#values are for self, search opt_ values if right_opt not in left_opts:
values = vals raise ConfigError(_('right_opt not in left_opts'))
if context is not None:
path = descr.impl_get_path_by_opt(opt_) left_vals = []
values_ = context._getattr(path, validate=False) 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: else:
values_ = opt_.impl_getdefault()
if index is not None:
#value is not already set, could be higher #value is not already set, could be higher
try: 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: except IndexError:
values_ = None #so return if no value
else: return
#values are for opt_, search self values
values_ = vals if self.impl_is_multi():
if context is not None: if index is None:
path = descr.impl_get_path_by_opt(self) for idx, right_v in enumerate(right_val):
values = context._getattr(path, validate=False) 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: else:
values = self.impl_getdefault() if None in left_vals:
if index is not None: return
#value is not already set, could be higher getattr(self, func)(left_opts, left_vals)
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_)
else: else:
if None not in (values, values_): if None in left_vals:
getattr(self, func)(opt_._name, values, values_) return
getattr(self, func)(left_opts, left_vals)
def impl_validate(self, value, context=None, validate=True, def impl_validate(self, value, context=None, validate=True,
force_no_multi=False): force_no_multi=False):
@ -550,29 +561,31 @@ class Option(BaseOption):
def impl_is_multi(self): def impl_is_multi(self):
return self._multi return self._multi
def impl_add_consistency(self, func, opt): def impl_add_consistency(self, func, *left_opts):
if self._consistencies is None: if self._consistencies is None:
self._consistencies = [] self._consistencies = []
if not isinstance(opt, Option): for opt in left_opts:
raise ValueError('consistency must be set with an option') if not isinstance(opt, Option):
if self is opt: raise ValueError(_('consistency should be set with an option'))
raise ValueError('cannot add consistency with itself') if self is opt:
if self.impl_is_multi() != opt.impl_is_multi(): raise ValueError(_('cannot add consistency with itself'))
raise ValueError('options in consistency' if self.impl_is_multi() != opt.impl_is_multi():
' should be multi in two sides') raise ValueError(_('options in consistency should be multi in '
'two sides'))
func = '_cons_{0}'.format(func) func = '_cons_{0}'.format(func)
self._launch_consistency(func, opts = tuple([self] + list(left_opts))
self, self._launch_consistency(func, self, self.impl_getdefault(), None,
self.impl_getdefault(), None, opts)
None, None, opt) self._consistencies.append((func, opts))
self._consistencies.append((func, opt))
self.impl_validate(self.impl_getdefault()) self.impl_validate(self.impl_getdefault())
def _cons_not_equal(self, optname, value, value_): def _cons_not_equal(self, opts, vals):
if value == value_: if len(opts) != 2:
raise ConfigError(_('invalid len for opts'))
if vals[0] == vals[1]:
raise ValueError(_("invalid value {0} for option {1} " raise ValueError(_("invalid value {0} for option {1} "
"must be different as {2} option" "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): def _impl_convert_callbacks(self, descr, load=False):
if not load and self._callback is None: if not load and self._callback is None:
@ -889,15 +902,17 @@ class NetmaskOption(Option):
except ValueError: except ValueError:
raise ValueError(_('invalid netmask address {0}').format(self._name)) 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 #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 #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 msg = None
try: try:
ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
@ -923,17 +938,30 @@ class NetmaskOption(Option):
else: else:
msg = _("invalid network {0} ({1}) with netmask {2} ({3})") msg = _("invalid network {0} ({1}) with netmask {2} ({3})")
if msg is not None: 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)) val_netmask, self._name))
class BroadcastOption(Option): class BroadcastOption(Option):
__slots__ = tuple()
_opt_type = 'broadcast'
def _validate(self, value): def _validate(self, value):
try: try:
IP('{0}/32'.format(value)) IP('{0}/32'.format(value))
except ValueError: except ValueError:
raise ValueError(_('invalid broadcast address {0}').format(self._name)) 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): class DomainnameOption(Option):
"represents the choice of a domain name" "represents the choice of a domain name"
@ -1098,12 +1126,11 @@ class OptionDescription(BaseOption):
if not force_no_consistencies and \ if not force_no_consistencies and \
option._consistencies is not None: option._consistencies is not None:
for consistency in option._consistencies: for consistency in option._consistencies:
func, opt = consistency func, left_opts = consistency
opts = (option, opt) for opt in left_opts:
_consistencies.setdefault(opt, _consistencies.setdefault(opt,
[]).append((func, opts)) []).append((func,
_consistencies.setdefault(option, left_opts))
[]).append((func, opts))
else: else:
_currpath.append(attr) _currpath.append(attr)
option.impl_build_cache(cache_path, option.impl_build_cache(cache_path,
@ -1186,17 +1213,15 @@ class OptionDescription(BaseOption):
def impl_get_group_type(self): def impl_get_group_type(self):
return self._group_type return self._group_type
def _valid_consistency(self, opt, value, context=None, index=None): def _valid_consistency(self, right_opt, right_val, context=None, index=None):
consistencies = self._consistencies.get(opt) #[('_cons_not_equal', (opt1, opt2))]
consistencies = self._consistencies.get(right_opt)
if consistencies is not None: if consistencies is not None:
for consistency in consistencies: for func, opts in consistencies:
opt_ = consistency[1] #opts[0] is the option where func is set
ret = opt_[0]._launch_consistency(consistency[0], #opts is left_opts
opt, ret = opts[0]._launch_consistency(func, right_opt, right_val,
value, context, index, opts)
context,
index,
opt_[1])
if ret is False: if ret is False:
return False return False
return True return True