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.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'")

View File

@ -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:

View File

@ -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:
values = []
for obj in consistency[1]:
if load:
new_value.append((consistency[0],
descr.impl_get_opt_by_path(
consistency[1])))
values.append(descr.impl_get_opt_by_path(obj))
else:
new_value.append((consistency[0],
descr.impl_get_path_by_opt(
consistency[1])))
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)
else:
values = self.impl_getdefault()
if index is not None:
#value is not already set, could be higher
#so return if no value
return
if self.impl_is_multi():
if index is None:
for idx, right_v in enumerate(right_val):
try:
values = values[index]
left_v = []
for left_val in left_vals:
left_v.append(left_val[idx])
if None in left_v:
continue
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_)
continue
getattr(self, func)(left_opts, left_v)
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)
else:
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 = []
for opt in left_opts:
if not isinstance(opt, Option):
raise ValueError('consistency must be set with an option')
raise ValueError(_('consistency should be set with an option'))
if self is opt:
raise ValueError('cannot add consistency with itself')
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')
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)
func, left_opts = consistency
for opt in left_opts:
_consistencies.setdefault(opt,
[]).append((func, opts))
_consistencies.setdefault(option,
[]).append((func, opts))
[]).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