]> git.proxmox.com Git - mirror_ifupdown2.git/commitdiff
attribute syntax check using validvals/validrange and keywords
authorJulien Fortin <julien@cumulusnetworks.com>
Mon, 1 Aug 2016 07:19:31 +0000 (09:19 +0200)
committerJulien Fortin <julien@cumulusnetworks.com>
Mon, 1 Aug 2016 07:28:16 +0000 (09:28 +0200)
Ticket: CM-8101
Reviewed By: CCR-4949
Testing Done: smoke tests + ran ifup -a -s on every interface configuration file from GSS cl-supports collection

This commit is introducing ~20 keywords. The value of the different attributes
will be check against raw values and <keywords>:

'<mac>'
'<text>'
'<ipv4>'
'<ipv6>'
'<auto>': "auto"
'<ipaddr>': ipv4/6 with preflix len
'<number>'
'<interface>'
'<ipv4-vrf-text>': equivalent to: <ipv4> "vrf" <text>
'<number-ipv4-list>': example: "100=172.16.100.1 101=172.16.101.1"
'<interface-list>': example: "swp1 swp2 swp3"
'<ipv4/prefixlen>'
'<ipv6/prefixlen>'
'<ipaddr/prefixlen>'
'<number-range-list>': example: "2000 2200-3000"
'<interface-range-list>': example: "swp1=100 swp2=100" ('validrange' : ['0', '65535'])
'<mac-ipaddr/prefixlen-list>'
'<number-interface-list>': example: "4 swp1 swp2"
'<interface-yes-no-list>': example: "swp1=yes swp2=no"
'<interface-yes-no-0-1-list>'
'<interface-yes-no-auto-list>'

It's possible to combine a keyword with a range from validrange. example:
validrange: 10-50
validvals: <intrface-range-list>
value: swp1=21 swp2=42 ...

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
addons/address.py
addons/addressvirtual.py
addons/bond.py
addons/bridge.py
addons/bridgevlan.py
addons/mstpctl.py
addons/vlan.py
addons/vrf.py
addons/vrrpd.py
addons/vxlan.py
ifupdown/ifupdownmain.py

index 3a04ed6be90c92f75c6cebdd568e2a8cda6d067f..7c0c826d3df5060722d32f4541d66e37a7c19ded 100644 (file)
@@ -29,18 +29,18 @@ class address(moduleBase):
                 'attrs': {
                       'address' :
                             {'help' : 'ipv4 or ipv6 addresses',
-                             'validvals' : [IPv4Network, IPv6Network],
+                             'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
                              'multiline' : True,
                              'example' : ['address 10.0.12.3/24',
                              'address 2000:1000:1000:1000:3::5/128']},
                       'netmask' :
                             {'help': 'netmask',
-                             'validvals' : [IPv4Address, ],
+                             'validvals' : ['<ipv4>', ],
                              'example' : ['netmask 255.255.255.0'],
                              'compat' : True},
                       'broadcast' :
                             {'help': 'broadcast address',
-                             'validvals' : [IPv4Address, ],
+                             'validvals' : ['<ipv4>', ],
                              'example' : ['broadcast 10.0.1.255']},
                       'scope' :
                             {'help': 'scope',
@@ -53,7 +53,7 @@ class address(moduleBase):
                                           'preferred-lifetime 10']},
                       'gateway' :
                             {'help': 'default gateway',
-                             'validvals' : [IPv4Address, IPv6Address],
+                             'validvals' : ['<ipv4>', '<ipv6>'],
                              'example' : ['gateway 255.255.255.0']},
                       'mtu' :
                             { 'help': 'interface mtu',
@@ -80,7 +80,7 @@ class address(moduleBase):
                       'clagd-vxlan-anycast-ip' :
                             { 'help'     : 'Anycast local IP address for ' +
                               'dual connected VxLANs',
-                              'validvals' : [IPv4Address, ],
+                              'validvals' : ['<ipv4>', ],
                               'example'  : ['clagd-vxlan-anycast-ip 36.0.0.11']}}}
 
     def __init__(self, *args, **kargs):
index a994a7f619b9c2b5f4c43e22d7481c7bf8bdb6b2..71efbe2cb2b49a099b0182b8d033892c355c71ea 100644 (file)
@@ -25,8 +25,8 @@ class addressvirtual(moduleBase):
                           'every mac ip address-virtual line',
                 'attrs' : {
                     'address-virtual' :
-                        { 'help' : 'bridge router virtual mac and ip',
-                          'validvals' : [('<mac>', IPv4Network), ],
+                        { 'help' : 'bridge router virtual mac and ips',
+                          'validvals' : ['<mac-ipaddr/prefixlen-list>',],
                           'example' : ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']}
                  }}
 
index 35fa52f92934e035ed3c3dc7a81892e45f9d55bd..5fb3a453ded6a4cd639371c0f515d167e44e73ad 100644 (file)
@@ -80,12 +80,14 @@ class bond(moduleBase):
                      'bond-ad-sys-mac-addr':
                          {'help' : '802.3ad system mac address',
                           'default' : '00:00:00:00:00:00',
+                          'validvals': ['<mac>', ],
                          'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00'],
                          'deprecated' : True,
                          'new-attribute' : 'bond-ad-actor-system'},
                      'bond-ad-actor-system':
                          {'help' : '802.3ad system mac address',
                           'default' : '00:00:00:00:00:00',
+                          'validvals': ['<mac>', ],
                          'example' : ['bond-ad-actor-system 00:00:00:00:00:00'],},
                      'bond-lacp-bypass-allow':
                          {'help' : 'allow lacp bypass',
@@ -96,6 +98,7 @@ class bond(moduleBase):
                         {'help' : 'bond slaves',
                          'required' : True,
                          'multivalue' : True,
+                         'validvals': ['<interface-list>'],
                          'example' : ['bond-slaves swp1 swp2',
                                       'bond-slaves glob swp1-2',
                                       'bond-slaves regex (swp[1|2)']}}}
@@ -188,20 +191,6 @@ class bond(moduleBase):
                                                ifname=ifaceobj.name,
                                                attr=attrname)
         if attrval:
-            msg = ('%s: invalid value %s for attr %s.'
-                    %(ifaceobj.name, attrval, attrname))
-            optiondict = self.get_mod_attr(attrname)
-            if not optiondict:
-                return None
-            validvals = optiondict.get('validvals')
-            if validvals and attrval not in validvals:
-                raise Exception(msg + ' Valid values are %s' %str(validvals))
-            validrange = optiondict.get('validrange')
-            if validrange:
-                if (int(attrval) < int(validrange[0]) or
-                        int(attrval) > int(validrange[1])):
-                    raise Exception(msg + ' Valid range is [%s,%s]'
-                                    %(validrange[0], validrange[1]))
             if attrname == 'bond-mode':
                 attrval = bond._get_readable_bond_mode(attrval)
                 if attrval == '802.3ad':
index 8242afedbe00959cb3895beefe910cb6c5a305c1..d07cd9fd10646ece78494e1e401a0c5e1a233382 100644 (file)
@@ -42,6 +42,7 @@ class bridge(moduleBase):
                         {'help' : 'bridge ports',
                          'multivalue' : True,
                          'required' : True,
+                         'validvals': ['<interface-list>'],
                          'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
                                       'bridge-ports glob swp1-3.100',
                                       'bridge-ports regex (swp[1|2|3].100)']},
@@ -85,12 +86,14 @@ class bridge(moduleBase):
                           'default' : '20'},
                    'bridge-pathcosts' :
                         { 'help' : 'bridge set port path costs',
+                          'validvals': ['<interface-range-list>'],
                           'validrange' : ['0', '65535'],
                           'example' : ['under the bridge: bridge-pathcosts swp1=100 swp2=100',
                                        'under the port (recommended): bridge-pathcosts 100'],
                           'default' : '100'},
                    'bridge-portprios' :
                         { 'help' : 'bridge port prios',
+                          'validvals': ['<interface-range-list>'],
                           'validrange' : ['0', '65535'],
                           'example' : ['under the bridge: bridge-portprios swp1=32 swp2=32',
                                        'under the port (recommended): bridge-portprios 32'],
@@ -173,12 +176,13 @@ class bridge(moduleBase):
                           'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
                     'bridge-portmcrouter' :
                         { 'help' : 'set port multicast routers',
-                          'validvals' : ['yes', 'no', '0', '1'],
+                          'validvals' : ['<interface-yes-no-0-1-list>'],
                           'default' : 'yes',
                           'example' : ['under the bridge: bridge-portmcrouter swp1=yes swp2=yes',
                                        'under the port (recommended): bridge-portmcrouter yes']},
                     'bridge-portmcfl' :
                         { 'help' : 'port multicast fast leave.',
+                          'validvals': ['<interface-range-list>'],
                           'validrange' : ['0', '65535'],
                           'default' : '0',
                           'example' : ['under the bridge: bridge-portmcfl swp1=0 swp2=0',
@@ -193,6 +197,7 @@ class bridge(moduleBase):
                                 'regex or \"all\" on bridge_ports,' +
                                 'as it wouldnt work.',
                           'default' : '0',
+                          'validvals': ['<number-interface-list>'],
                           'example' : ['bridge-waitport 4 swp1 swp2']},
                     'bridge-maxwait' :
                         { 'help' : 'forces to time seconds the maximum time ' +
@@ -210,6 +215,7 @@ class bridge(moduleBase):
                                    'If specified under the bridge the ports ' +
                                    'inherit it unless overridden by a ' +
                                    'bridge-vids attribute under the port',
+                          'validvals': ['<number-range-list>'],
                           'example' : ['bridge-vids 4000',
                                        'bridge-vids 2000 2200-3000']},
                     'bridge-pvid' :
index 80245ccee82e5d8ffe7712d1c47f626faa69c6dc..ef3cd02e3bb5f34aa761b57dbdbcb393b535f569 100644 (file)
@@ -25,7 +25,7 @@ class bridgevlan(moduleBase):
                         'bridge-igmp-querier-src' :
                             { 'help' : 'bridge igmp querier src. Must be ' +
                                    'specified under the vlan interface',
-                              'validvals' : [IPv4Address, ],
+                              'validvals' : ['<ipv4>', ],
                               'example' : ['bridge-igmp-querier-src 172.16.101.1']}}}
 
     def __init__(self, *args, **kargs):
index cd377840c0788278e4f28e4e0903043f9393abf4..8cafb20411e0c1104dd3170468b3f001e0749cb0 100644 (file)
@@ -30,7 +30,7 @@ class mstpctl(moduleBase):
                          'new-attribute': 'bridge-ports'},
                    'mstpctl-stp' :
                         {'help': 'bridge stp yes/no',
-                         'validvals' : ['yes', 'no'],
+                         'validvals' : ['yes', 'no', 'on', 'off'],
                          'compat' : True,
                          'default' : 'no',
                          'deprecated': True,
@@ -79,6 +79,7 @@ class mstpctl(moduleBase):
                           'example' : ['mstpctl-forcevers rstp']},
                     'mstpctl-portpathcost' :
                         { 'help' : 'bridge port path cost',
+                          'validvals': ['<interface-range-list>'],
                           'validrange' : ['0', '65535'],
                           'default' : '0',
                           'jsonAttr' : 'adminExtPortCost',
@@ -89,7 +90,7 @@ class mstpctl(moduleBase):
                         { 'help' : 'bridge port p2p detection mode',
                           'default' : 'auto',
                           'jsonAttr' : 'adminPointToPoint',
-                          'validvals' : ['yes', 'no', 'auto'],
+                          'validvals' : ['<interface-yes-no-auto-list>'],
                           'required' : False,
                           'example' : ['under the bridge: mstpctl-portp2p swp1=yes swp2=no',
                                        'under the port (recommended): mstpctl-portp2p yes']},
@@ -98,7 +99,7 @@ class mstpctl(moduleBase):
                           'enable/disable port ability to take root role of the port',
                           'default' : 'no',
                           'jsonAttr' : 'restrictedRole',
-                          'validvals' : ['yes', 'no'],
+                          'validvals' : ['<interface-yes-no-list>'],
                           'required' : False,
                           'example' : ['under the bridge: mstpctl-portrestrrole swp1=yes swp2=no',
                                        'under the port (recommended): mstpctl-portrestrrole yes']},
@@ -107,7 +108,7 @@ class mstpctl(moduleBase):
                           'enable/disable port ability to propagate received topology change notification of the port',
                           'default' : 'no',
                           'jsonAttr' : 'restrictedTcn',
-                          'validvals' : ['yes', 'no'],
+                          'validvals' : ['<interface-yes-no-list>'],
                           'required' : False,
                           'example' : ['under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no',
                                        'under the port (recommended): mstpctl-portrestrtcn yes']},
@@ -116,7 +117,7 @@ class mstpctl(moduleBase):
                           'enable/disable bpduguard',
                           'default' : 'no',
                           'jsonAttr' : 'bpduGuardPort',
-                          'validvals' : ['yes', 'no'],
+                          'validvals' : ['<interface-yes-no-list>'],
                           'required' : False,
                           'example' : ['under the bridge: mstpctl-bpduguard swp1=yes swp2=no',
                                        'under the port (recommended): mstpctl-bpduguard yes']},
@@ -124,6 +125,7 @@ class mstpctl(moduleBase):
                         { 'help' :
                           'port priority for MSTI instance',
                           'default' : '128',
+                          'validvals': ['<interface-range-list>'],
                           'validrange' : ['0', '240'],
                           'required' : False,
                           'example' : ['under the bridge: mstpctl-treeportprio swp1=128 swp2=128',
@@ -136,7 +138,7 @@ class mstpctl(moduleBase):
                           'example' : ['mstpctl-hello 2']},
                     'mstpctl-portnetwork' : 
                         { 'help' : 'enable/disable bridge assurance capability for a port',
-                          'validvals' : ['yes', 'no'],
+                          'validvals' : ['<interface-yes-no-list>'],
                           'default' : 'no',
                           'jsonAttr' : 'networkPort',
                           'required' : False,
@@ -144,7 +146,7 @@ class mstpctl(moduleBase):
                                        'under the port (recommended): mstpctl-portnetwork yes']},
                     'mstpctl-portadminedge' : 
                         { 'help' : 'enable/disable initial edge state of the port',
-                          'validvals' : ['yes', 'no'],
+                          'validvals' : ['<interface-yes-no-list>'],
                           'default' : 'no',
                           'jsonAttr' : 'adminEdgePort',
                           'required' : False,
@@ -152,7 +154,7 @@ class mstpctl(moduleBase):
                                        'under the port (recommended): mstpctl-portadminedge yes']},
                     'mstpctl-portautoedge' : 
                         { 'help' : 'enable/disable auto transition to/from edge state of the port',
-                          'validvals' : ['yes', 'no'],
+                          'validvals' : ['<interface-yes-no-list>'],
                           'default' : 'yes',
                           'jsonAttr' : 'autoEdgePort',
                           'required' : False,
@@ -166,7 +168,7 @@ class mstpctl(moduleBase):
                         { 'help' : 'enable/disable bpdu filter on a port. ' +
                                 'syntax varies when defined under a bridge ' +
                                 'vs under a port',
-                          'validvals' : ['yes', 'no'],
+                          'validvals' : ['<interface-yes-no-list>'],
                           'jsonAttr' : 'bpduFilterPort',
                           'default' : 'no',
                           'required' : False,
index ea4e68ef87b2334317e05b508594fd3b114f6c5b..111d432573a1c050cbc544136e726fb4692856ae 100644 (file)
@@ -25,7 +25,7 @@ class vlan(moduleBase):
                 'attrs' : {
                         'vlan-raw-device' :
                             {'help' : 'vlan raw device',
-                             'validvals' : ['<interface>' ,]},
+                             'validvals': ['<interface>']},
                         'vlan-id' :
                             {'help' : 'vlan id',
                              'validrange' : ['0', '4096']}}}
index fab769b7f291fef77cd98ad9eae588f9df74c106..343f5b9584edebd2ce0c2361e3f845aa452cd8a3 100644 (file)
@@ -34,9 +34,11 @@ class vrf(moduleBase):
                                    'creating a vrf device. ' +
                                    'Table id is either \'auto\' or '+
                                    '\'valid routing table id\'',
+                          'validvals': ['<auto>', '<number>'],
                           'example': ['vrf-table auto', 'vrf-table 1001']},
                     'vrf':
                          {'help' : 'vrf the interface is part of.',
+                          'validvals': ['<text>'],
                           'example': ['vrf blue']}}}
 
     iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
index 575e9926a41404c9b3cc94d2dd7f4728f8dbe6c8..6d1ee7d912e41264771de0041cf1de2bce5539e1 100644 (file)
@@ -35,7 +35,7 @@ class vrrpd(moduleBase):
                              'example' : ['vrrp-priority 20']},
                       'vrrp-virtual-ip' :
                             {'help': 'set vrrp virtual ip',
-                             'validvals' : [IPv4Address, ],
+                             'validvals' : ['<ipv4>', ],
                              'example' : ['vrrp-virtual-ip 10.0.1.254']}}}
 
     def __init__(self, *args, **kargs):
index 4bc1dfc6e460040f3cddca06563d11775e872e25..059b50b67f18aaa7b3c7812b0437c71f919ed3bd 100644 (file)
@@ -22,15 +22,15 @@ class vxlan(moduleBase):
                              'example': ['vxlan-id 100']},
                         'vxlan-local-tunnelip' :
                             {'help' : 'vxlan local tunnel ip',
-                             'validvals' : [IPv4Address, ],
+                             'validvals' : ['<ipv4>', '<ipv6>'],
                              'example': ['vxlan-local-tunnelip 172.16.20.103']},
                         'vxlan-svcnodeip' :
                             {'help' : 'vxlan id',
-                             'validvals' : [IPv4Address, ],
+                             'validvals' : ['<ipv4>', '<ipv6>'],
                              'example': ['vxlan-svcnodeip 172.16.22.125']},
                         'vxlan-remoteip' :
                             {'help' : 'vxlan remote ip',
-                             'validvals' : [IPv4Address, ],
+                             'validvals' : ['<ipv4>', '<ipv6>'],
                              'example': ['vxlan-remoteip 172.16.22.127']},
                         'vxlan-learning' :
                             {'help' : 'vxlan learning yes/no',
index 18231a0f2dd6fe494d06c0370838d6c8bd2aea60..0691a566a3b9f96a1f26d4a75be05b525cc72c86 100644 (file)
@@ -27,6 +27,8 @@ from graph import *
 from exceptions import *
 from sets import Set
 
+from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPAddress, IPv4Address, IPv6Address
+
 """
 .. module:: ifupdownmain
 :synopsis: main module for ifupdown package
@@ -270,6 +272,30 @@ class ifupdownMain(ifupdownBase):
         # This makes config available to addon modules
         ifupdownConfig.config = self.config
 
+        self.validate_keywords = {
+            '<mac>': self._keyword_mac,
+            '<text>': self._keyword_text,
+            '<ipv4>': self._keyword_ipv4,
+            '<ipv6>': self._keyword_ipv6,
+            '<auto>': self._keyword_auto,
+            '<ipaddr>': self._keyword_ipaddr,
+            '<number>': self._keyword_number,
+            '<interface>': self._keyword_interface,
+            '<ipv4-vrf-text>': self._keyword_ipv4_vrf_text,
+            '<number-ipv4-list>': self._keyword_number_ipv4_list,
+            '<interface-list>': self._keyword_interface_list,
+            '<ipv4/prefixlen>': self._keyword_ipv4_prefixlen,
+            '<ipv6/prefixlen>': self._keyword_ipv6_prefixlen,
+            '<ipaddr/prefixlen>': self._keyword_ipaddr_prefixlen,
+            '<number-range-list>': self._keyword_number_range_list,
+            '<interface-range-list>': self._keyword_interface_range_list,
+            '<mac-ipaddr/prefixlen-list>': self._keyword_mac_ipaddr_prefixlen_list,
+            '<number-interface-list>': self._keyword_number_interface_list,
+            '<interface-yes-no-list>': self._keyword_interface_yes_no_list,
+            '<interface-yes-no-0-1-list>': self._keyword_interface_yes_no_0_1_list,
+            '<interface-yes-no-auto-list>': self._keyword_interface_yes_no_auto_list,
+        }
+
     def link_master_slave_ignore_error(self, errorstr):
         # If link master slave flag is set, 
         # there may be cases where the lowerdev may not be
@@ -722,15 +748,357 @@ class ifupdownMain(ifupdownBase):
         ifaceobj.flags |= ifaceobj.OLDEST_SIBLING
         self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
 
+    def _keyword_text(self, value, validrange=None):
+        return isinstance(value, str) and len(value) > 0
+
+    def _keyword_mac(self, value, validrange=None):
+        if value.strip().startswith('ether'):
+            value = value.strip()[6:]
+        return re.match('[0-9a-f]{1,2}([-:])[0-9a-f]{1,2}(\\1[0-9a-f]{1,2}){4}$',
+                        value.lower())
+
+    def _keyword_check_list(self, _list, obj, limit=None):
+        try:
+            if limit and limit > 0:
+                for i in xrange(0, limit):
+                    obj(_list[i])
+                return len(_list) == limit
+            else:
+                for elem in _list:
+                    obj(elem)
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: check list: %s' % str(e))
+            return False
+
+    def _keyword_ipv4(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv4Address, limit=1)
+
+    def _keyword_ipv4_prefixlen(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv4Network, limit=1)
+
+    def _keyword_ipv6(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv6Address, limit=1)
+
+    def _keyword_ipv6_prefixlen(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv6Network, limit=1)
+
+    def _keyword_ipaddr(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPAddress, limit=1)
+
+    def _keyword_ipaddr_prefixlen(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPNetwork, limit=1)
+
+    def _keyword_mac_ipaddr_prefixlen_list(self, value, validrange=None):
+        """
+            <mac> <ipaddr> [<ipaddr> ...]
+            ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
+        """
+        try:
+            res = value.split()
+            if len(res) < 2:
+                return False
+            if not self._keyword_mac(res[0]):
+                return False
+            for ip in res[1:]:
+                if not self._keyword_ipaddr_prefixlen(ip):
+                    return False
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: mac ipaddr prefixlen: %s' % str(e))
+            return False
+
+    def _keyword_number_ipv4_list(self, value, validrange=None):
+        """
+            <number>=<ipv4> [<number>=<ipv4> ...]
+            ex: bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1
+        """
+        try:
+            elements = value.split(' ')
+            if not elements:
+                return False
+            for elem in elements:
+                v = elem.split('=')
+                int(v[0])
+                IPv4Address(v[1])
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: number ipv4: %s' % str(e))
+            return False
+
+    def _keyword_interface(self, ifacename, validrange=None):
+        return self.get_ifaceobjs(ifacename)
+
+    def _keyword_ipv4_vrf_text(self, value, validrange=None):
+        """
+            <ipv4> "vrf" <text>
+            ex: clagd-backup-ip 10.10.10.42 vrf blue
+        """
+        values = value.split()
+        size = len(values)
+
+        if size > 3 or size < 1:
+            return False
+        try:
+            IPv4Address(values[0])
+            if size > 1:
+                if values[1] != 'vrf':
+                    return False
+                if size > 2:
+                    if not self._keyword_text(values[2]):
+                        return False
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: ipv4 vrf text: %s' % str(e))
+            return False
+
+    def _keyword_interface_list_with_value(self, value, validvals):
+        values = value.split()
+        try:
+            if len(values) == 1:
+                if values[0] in validvals:
+                    return True
+            for v in values:
+                iface_value = v.split('=')
+                size = len(iface_value)
+                if size != 2:
+                    if iface_value[0] == 'glob' or iface_value[0] == 'regex':
+                        continue
+                    return False
+                if not iface_value[1] in validvals:
+                    return False
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: interface list with value: %s' % str(e))
+            return False
+
+    def _keyword_interface_yes_no_list(self, value, validrange=None):
+        """
+            <yes|no> | ( <interface>=<yes|no> [<interface>=<yes|no> ...] )
+            ex: mstpctl-portrestrrole swp1=yes swp2=no
+        """
+        return self._keyword_interface_list_with_value(value, ['yes', 'no'])
+
+    def _keyword_interface_yes_no_auto_list(self, value, validrange=None):
+        """
+            <yes|no|auto> |
+                ( <interface>=<yes|no|auto> [<interface>=<yes|no|auto> ...] )
+            ex: mstpctl-portp2p swp1=yes swp2=no swp3=auto
+        """
+        return self._keyword_interface_list_with_value(value,
+                                                        ['yes', 'no', 'auto'])
+
+    def _keyword_interface_yes_no_0_1_list(self, value, validrange=None):
+        """
+            <yes|no|0|1> |
+                ( <interface>=<yes|no|0|1> [<interface>=<yes|no|0|1> ...] )
+            ex: bridge-portmcrouter swp1=yes swp2=yes swp3=1
+        """
+        return self._keyword_interface_list_with_value(value,
+                                                       ['yes', 'no', '1', '0'])
+
+    def _keyword_interface_range_list(self, value, validrange):
+        """
+            <number> | ( <interface>=<number> [ <interface>=number> ...] )
+            ex: mstpctl-portpathcost swp1=0 swp2=1
+        """
+        values = value.split()
+        try:
+            if len(values) == 1:
+                try:
+                    n = int(values[0])
+                    if n < int(validrange[0]) or n > int(
+                        validrange[1]):
+                        raise invalidValueError('value of out range "%s":'
+                                                ' valid attribute range: %s'
+                                                % (values[0],
+                                                   '-'.join(validrange)))
+                    return True
+                except invalidValueError as e:
+                    raise e
+                except Exception as e:
+                    self.logger.debug('keyword: interface range list: %s'
+                                      % str(e))
+                    return False
+            for v in values:
+                iface_value = v.split('=')
+                size = len(iface_value)
+                if size != 2:
+                    return False
+                number = int(iface_value[1])
+                if number < int(validrange[0]) or number > int(
+                        validrange[1]):
+                    raise invalidValueError(
+                        'value of out range "%s" for iface "%s":'
+                        ' valid attribute range: %s'
+                        % (iface_value[1],
+                           iface_value[0],
+                           '-'.join(validrange)))
+            return True
+        except invalidValueError as e:
+            raise e
+        except Exception as e:
+            self.logger.debug('keyword: interface range list: %s' % str(e))
+            return False
+
+    def _keyword_interface_list(self, value, validrange=None):
+        """
+            [glob|regex] <interface> [ [glob|regex] <interface> ...]
+            ex: bridge-ports swp1 swp2 glob swp3-5.100 regex (swp[6|7|8].100)
+        """
+        interface_list = value.split()
+        size = len(interface_list)
+        i = 0
+        while i < size:
+            if interface_list[i] == 'glob' or interface_list[i] == 'regex':
+                i += 1
+            else:
+                if not self._keyword_interface(interface_list[i]):
+                    return False
+            i += 1
+        return True
+
+    def _keyword_number_range_list(self, value, validrange=None):
+        """
+            <number> [<number>-<number>]
+            ex: bridge-vids 42 100-200
+        """
+        number_list = value.split()
+        try:
+            i = 0
+            while i < len(number_list):
+                if '-' in number_list[i]:
+                    range = number_list[i].split('-')
+                    a = int(range[0])
+                    b = int(range[1])
+                    if a > b:
+                        return False
+                else:
+                    int(number_list[i])
+                i += 1
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: number range list: %s' % str(e))
+            return False
+
+    def _keyword_number_interface_list(self, value, validrange=None):
+        """
+            <number> <interface> [<interface>... [<number> <interface> ... ]]
+            bridge-waitport 42 swp1 swp2 swp3 9 swp4
+        """
+        interface_list = value.split()
+        if not interface_list:
+            return False
+        try:
+            int(interface_list[0])
+            prev = True
+            for elem in interface_list[1:]:
+                try:
+                    int(elem)
+                    if prev:
+                        return False
+                    prev = True
+                except:
+                    prev = False
+            return not prev
+        except Exception as e:
+            self.logger.debug('keyword: number interface list: %s' % str(e))
+            return False
+
+    def _keyword_auto(self, value, validrange=None):
+        return value == 'auto'
+
+    def _keyword_number(self, value, validrange=None):
+        try:
+            int(value)
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: number: %s' % str(e))
+            return False
+
+    def _is_keyword(self, value):
+        if isinstance(value, tuple):
+            return True
+        keyword_found = value in self.validate_keywords
+        if value.startswith('<') and value.endswith('>') and not keyword_found:
+            raise Exception('%s: invalid keyword, please make sure to use'
+                            ' a valid keyword see `ifquery -s`' % value)
+        return keyword_found
+
+    def _check_validvals_value(self, attrname, value, validvals, validrange):
+        if validvals and value not in validvals:
+            is_valid = False
+            for keyword in validvals:
+                if self._is_keyword(keyword):
+                    if validrange:
+                        if self.validate_keywords[keyword](value, validrange):
+                            return {'result': True}
+                    else:
+                        if self.validate_keywords[keyword](value):
+                            return {'result': True}
+            if not is_valid:
+                return {
+                    'result': False,
+                    'message': 'invalid value "%s": valid attribute values: %s'
+                               % (value, validvals)
+                }
+        elif validrange:
+            if len(validrange) != 2:
+                raise Exception('%s: invalid range in addon configuration'
+                                % '-'.join(validrange))
+            _value = int(value)
+            if _value < int(validrange[0]) or _value > int(validrange[1]):
+                return {
+                    'result': False,
+                    'message': 'value of out range "%s": '
+                               'valid attribute range: %s'
+                               % (value, '-'.join(validrange))
+                }
+        return {'result': True}
+
+    def _check_validvals(self, ifacename, module_name, attrs):
+        ifaceobj = self.get_ifaceobjs(ifacename)
+        if not ifaceobj:
+            return
+        success = True
+        for attrname, attrvalue in ifaceobj[0].config.items():
+            try:
+                attrname_dict = attrs.get(attrname, {})
+                validvals = attrname_dict.get('validvals', [])
+                validrange = attrname_dict.get('validrange', [])
+                for value in attrvalue:
+                    res = self._check_validvals_value(attrname,
+                                                      value,
+                                                      validvals,
+                                                      validrange)
+                    if not res['result']:
+                        self.logger.warn('%s: %s: %s' %
+                                         (ifacename, attrname, res['message']))
+                        success = False
+            except Exception as e:
+                self.logger.warn('addon \'%s\': %s: %s' % (module_name,
+                                                           attrname,
+                                                           str(e)))
+                success = False
+        return success
+
     def _module_syntax_check(self, filtered_ifacenames):
+        result = True
         for ifacename in filtered_ifacenames:
             for module in self.modules.values():
                 try:
-                    if hasattr(module, 'syntax_check') \
-                            and callable(module.syntax_check):
-                        module.syntax_check(self.get_ifaceobjs(ifacename))
+                    if hasattr(module, '_modinfo'):
+                        if not self._check_validvals(ifacename,
+                                                     module.__class__.__name__,
+                                                     module._modinfo.get('attrs', {})):
+                            result = False
+                    if hasattr(module, 'syntax_check') and callable(module.syntax_check):
+                        if not module.syntax_check(self.get_ifaceobjs(ifacename)):
+                            result = False
                 except Exception, e:
-                    pass #self.logger.warn('%s: %s' % (ifacename, str(e)))
+                    self.logger.warn('%s: %s' % (ifacename, str(e)))
+                    result = False
+        return result
 
     def _iface_configattr_syntax_checker(self, attrname, attrval):
         for m, mdict in self.module_attrs.items():
@@ -1160,7 +1528,8 @@ class ifupdownMain(ifupdownBase):
         # return here because we want to make sure most
         # errors above are caught and reported.
         if syntaxcheck:
-            self._module_syntax_check(filtered_ifacenames)
+            if not self._module_syntax_check(filtered_ifacenames):
+                raise Exception()
             if not iface_read_ret:
                 raise Exception()
             elif self._any_iface_errors(filtered_ifacenames):
@@ -1366,7 +1735,8 @@ class ifupdownMain(ifupdownBase):
         # return here because we want to make sure most
         # errors above are caught and reported.
         if syntaxcheck:
-            self._module_syntax_check(interfaces_to_up)
+            if not self._module_syntax_check(interfaces_to_up):
+                raise Exception()
             if not iface_read_ret:
                 raise Exception()
             elif self._any_iface_errors(interfaces_to_up):
@@ -1449,7 +1819,8 @@ class ifupdownMain(ifupdownBase):
         # return here because we want to make sure most
         # errors above are caught and reported.
         if syntaxcheck:
-            self._module_syntax_check(new_filtered_ifacenames)
+            if not self._module_syntax_check(new_filtered_ifacenames):
+                raise Exception()
             if not iface_read_ret:
                 raise Exception()
             elif self._any_iface_errors(new_filtered_ifacenames):