consistancies can have more than one option
authorEmmanuel Garette <egarette@cadoles.com>
Fri, 27 Sep 2013 21:26:10 +0000 (23:26 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Fri, 27 Sep 2013 21:27:08 +0000 (23:27 +0200)
add _cons_broadcast

test/test_option_consistency.py
test/test_state.py
tiramisu/option.py

index 5cf53cd..1c23dac 100644 (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'")
index 8587b4a..4430bd9 100644 (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:
index f9a3541..d3c4bf9 100644 (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:
-                    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:
-                values_ = opt_.impl_getdefault()
-            if index is not None:
-                #value is not already set, could be higher
-                try:
-                    values_ = values_[index]
-                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)
+                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 = 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]
+                    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:
-                    value = None
-                    value_ = None
-                if None not in (value, value_):
-                    getattr(self, func)(opt_._name, value, value_)
+                    #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:
+                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