diff --git a/addons/address.py b/addons/address.py index 9ce31c5..2858b8c 100644 --- a/addons/address.py +++ b/addons/address.py @@ -55,6 +55,7 @@ class address(moduleBase): def __init__(self, *args, **kargs): moduleBase.__init__(self, *args, **kargs) self.ipcmd = None + self._bridge_fdb_query_cache = {} def _add_address_to_bridge(self, ifaceobj, hwaddress): if '.' in ifaceobj.name: @@ -135,14 +136,15 @@ class address(moduleBase): dhclientcmd.release6(ifaceobj.name) except: pass + self.ipcmd.batch_start() self._inet_address_config(ifaceobj) mtu = ifaceobj.get_attr_value_first('mtu') if mtu: - self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu) + self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu) alias = ifaceobj.get_attr_value_first('alias') if alias: - self.ipcmd.link_set_alias(ifaceobj.name, alias) + self.ipcmd.link_set_alias(ifaceobj.name, alias) hwaddress = ifaceobj.get_attr_value_first('hwaddress') if hwaddress: self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress) @@ -193,6 +195,26 @@ class address(moduleBase): outaddrlist.append(addr) return outaddrlist + def _get_bridge_fdbs(self, bridgename, vlan): + fdbs = self._bridge_fdb_query_cache.get(bridgename) + if not fdbs: + fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename) + if not fdbs: + return + self._bridge_fdb_query_cache[bridgename] = fdbs + return fdbs.get(vlan) + + def _check_addresses_in_bridge(self, ifaceobj, hwaddress): + """ If the device is a bridge, make sure the addresses + are in the bridge """ + if '.' in ifaceobj.name: + (bridgename, vlan) = ifaceobj.name.split('.') + if self.ipcmd.bridge_is_vlan_aware(bridgename): + fdb_addrs = self._get_bridge_fdbs(bridgename, vlan) + if not fdb_addrs or hwaddress not in fdb_addrs: + return False + return True + def _query_check(self, ifaceobj, ifaceobjcurr): runningaddrsdict = None if not self.ipcmd.link_exists(ifaceobj.name): @@ -200,8 +222,20 @@ class address(moduleBase): return self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, 'mtu', self.ipcmd.link_get_mtu) - self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, - 'hwaddress', self.ipcmd.link_get_hwaddress) + hwaddress = ifaceobj.get_attr_value_first('hwaddress') + if hwaddress: + rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) + if not rhwaddress or rhwaddress != hwaddress: + ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, + 1) + elif not self._check_addresses_in_bridge(ifaceobj, hwaddress): + # XXX: hw address is not in bridge + ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, + 1) + ifaceobjcurr.status_str = 'bridge fdb error' + else: + ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, + 0) self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, 'alias', self.ipcmd.link_get_alias) # compare addresses @@ -305,7 +339,8 @@ class address(moduleBase): of interfaces. status is success if the running state is same as user required state in ifaceobj. error otherwise. """ - + if ifaceobj.type == ifaceType.BRIDGE_VLAN: + return op_handler = self._run_ops.get(operation) if not op_handler: return diff --git a/addons/addressvirtual.py b/addons/addressvirtual.py index dbe153c..1a9d196 100644 --- a/addons/addressvirtual.py +++ b/addons/addressvirtual.py @@ -24,9 +24,11 @@ class addressvirtual(moduleBase): 'example' : ['address-virtual 00:11:22:33:44:01 11.0.1.254/24 11.0.1.254/24']} }} + def __init__(self, *args, **kargs): moduleBase.__init__(self, *args, **kargs) self.ipcmd = None + self._bridge_fdb_query_cache = {} def _is_supported(self, ifaceobj): if ifaceobj.get_attr_value_first('address-virtual'): @@ -34,18 +36,46 @@ class addressvirtual(moduleBase): return False def _add_addresses_to_bridge(self, ifaceobj, hwaddress): + # XXX: batch the addresses if '.' in ifaceobj.name: (bridgename, vlan) = ifaceobj.name.split('.') if self.ipcmd.bridge_is_vlan_aware(bridgename): [self.ipcmd.bridge_fdb_add(bridgename, addr, vlan) for addr in hwaddress] + elif self.ipcmd.is_bridge(ifaceobj.name): + [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr) + for addr in hwaddress] def _remove_addresses_from_bridge(self, ifaceobj, hwaddress): + # XXX: batch the addresses if '.' in ifaceobj.name: (bridgename, vlan) = ifaceobj.name.split('.') if self.ipcmd.bridge_is_vlan_aware(bridgename): [self.ipcmd.bridge_fdb_del(bridgename, addr, vlan) for addr in hwaddress] + elif self.ipcmd.is_bridge(ifaceobj.name): + [self.ipcmd.bridge_fdb_del(ifaceobj.name, addr) + for addr in hwaddress] + + def _get_bridge_fdbs(self, bridgename, vlan): + fdbs = self._bridge_fdb_query_cache.get(bridgename) + if not fdbs: + fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename) + if not fdbs: + return + self._bridge_fdb_query_cache[bridgename] = fdbs + return fdbs.get(vlan) + + def _check_addresses_in_bridge(self, ifaceobj, hwaddress): + """ If the device is a bridge, make sure the addresses + are in the bridge """ + if '.' in ifaceobj.name: + (bridgename, vlan) = ifaceobj.name.split('.') + if self.ipcmd.bridge_is_vlan_aware(bridgename): + fdb_addrs = self._get_bridge_fdbs(bridgename, vlan) + if not fdb_addrs or hwaddress not in fdb_addrs: + return False + return True def _apply_address_config(self, ifaceobj, address_virtual_list): purge_existing = False if self.PERFMODE else True @@ -177,11 +207,9 @@ class addressvirtual(moduleBase): ifaceobjcurr.update_config_with_status('address-virtual', '', 1) av_idx += 1 continue - raddrs = raddrs.keys() - self.logger.info(rhwaddress) - self.logger.info(raddrs) - if rhwaddress == av_attrs[0] and raddrs == av_attrs[1:]: + if (rhwaddress == av_attrs[0] and raddrs == av_attrs[1:] and + self._check_addresses_in_bridge(ifaceobj, av_attrs[0])): ifaceobjcurr.update_config_with_status('address-virtual', address_virtual, 0) else: @@ -192,7 +220,18 @@ class addressvirtual(moduleBase): return def _query_running(self, ifaceobjrunning): - # Not implemented + macvlan_prefix = '%s-v' %ifaceobjrunning.name.replace('.', '-') + address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix) + for av in address_virtuals: + macvlan_ifacename = os.path.basename(av) + rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename) + raddress = self.ipcmd.addr_get(macvlan_ifacename) + if not raddress: + self.logger.warn('%s: no running addresses' + %ifaceobjrunning.name) + raddress = [] + ifaceobjrunning.update_config('address-virtual', + '%s %s' %(rhwaddress, ''.join(raddress))) return _run_ops = {'up' : _up, diff --git a/addons/bridge.py b/addons/bridge.py index bf36d0e..6239b64 100644 --- a/addons/bridge.py +++ b/addons/bridge.py @@ -753,7 +753,7 @@ class bridge(moduleBase): self._apply_bridge_settings(ifaceobj) self._apply_bridge_port_settings_all(ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) - self._flush_running_vidinfo() + #self._flush_running_vidinfo() except Exception, e: self.log_error(str(e)) if porterr: diff --git a/addons/ethtool.py b/addons/ethtool.py index 740a878..579fdfa 100644 --- a/addons/ethtool.py +++ b/addons/ethtool.py @@ -87,7 +87,7 @@ class ethtool(moduleBase): else: ifaceobjcurr.update_config_with_status('link-duplex', running_duplex, 0) - except Exception, e: + except Exception: pass return diff --git a/addons/vlan.py b/addons/vlan.py index fa883ce..feb16c9 100644 --- a/addons/vlan.py +++ b/addons/vlan.py @@ -25,9 +25,11 @@ class vlan(moduleBase): 'vlan-id' : {'help' : 'vlan id'}}} + def __init__(self, *args, **kargs): moduleBase.__init__(self, *args, **kargs) self.ipcmd = None + self._bridge_vids_query_cache = {} def _is_vlan_device(self, ifaceobj): vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device') @@ -92,27 +94,30 @@ class vlan(moduleBase): return None return [self._get_vlan_raw_device(ifaceobj)] - def _get_bridge_vids(self, bridgename, ifaceobj_getfunc): - ifaceobjs = ifaceobj_getfunc(bridgename) - for ifaceobj in ifaceobjs: - vids = ifaceobj.get_attr_value_first('bridge-vids') - if vids: return re.split(r'[\s\t]\s*', vids) - return None - def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid, - ifaceobj_getfunc, add=True): + add=True): + """ If the lower device is a vlan aware bridge, add/del the vlanid + to the bridge """ if self.ipcmd.bridge_is_vlan_aware(bridgename): - # Check if the bridge vids has the vlanid - vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc) - if vids and vlanid in vids: - return - else: - if add: - self.ipcmd.bridge_vids_add(bridgename, [vlanid]) - else: - self.ipcmd.bridge_vids_del(bridgename, [vlanid]) + if add: + self.ipcmd.bridge_vids_add(bridgename, [vlanid]) + else: + self.ipcmd.bridge_vids_del(bridgename, [vlanid]) - def _up(self, ifaceobj, ifaceobj_getfunc=None): + def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid): + """ If the lower device is a vlan aware bridge, check if the vlanid + is configured on the bridge """ + if not self.ipcmd.bridge_is_vlan_aware(bridgename): + return + vids = self._bridge_vids_query_cache.get(bridgename) + if vids == None: + vids = self.ipcmd.bridge_port_vids_get(bridgename) + self._bridge_vids_query_cache[bridgename] = vids + if not vids or vlanid not in vids: + ifaceobjcurr.status = ifaceStatus.ERROR + ifaceobjcurr.status_str = 'bridge vid error' + + def _up(self, ifaceobj): vlanid = self._get_vlan_id(ifaceobj) if vlanid == -1: raise Exception('could not determine vlanid') @@ -123,15 +128,13 @@ class vlan(moduleBase): if not self.ipcmd.link_exists(vlanrawdevice): raise Exception('rawdevice %s not present' %vlanrawdevice) if self.ipcmd.link_exists(ifaceobj.name): - self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, - ifaceobj_getfunc) + self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid) return rtnetlink_api.rtnl_api.create_vlan(vlanrawdevice, ifaceobj.name, vlanid) - self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, - ifaceobj_getfunc) + self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid) - def _down(self, ifaceobj, ifaceobj_getfunc=None): + def _down(self, ifaceobj): vlanid = self._get_vlan_id(ifaceobj) if vlanid == -1: raise Exception('could not determine vlanid') @@ -142,12 +145,11 @@ class vlan(moduleBase): return try: self.ipcmd.link_delete(ifaceobj.name) - self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, - ifaceobj_getfunc, add=False) + self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False) except Exception, e: self.log_warn(str(e)) - def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): + def _query_check(self, ifaceobj, ifaceobjcurr): if not self.ipcmd.link_exists(ifaceobj.name): ifaceobjcurr.status = ifaceStatus.NOTFOUND return @@ -165,8 +167,9 @@ class vlan(moduleBase): else: ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0) + self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, vlanid) - def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): + def _query_running(self, ifaceobjrunning): if not self.ipcmd.link_exists(ifaceobjrunning.name): if self._is_vlan_by_name(ifaceobjrunning.name): ifaceobjcurr.status = ifaceStatus.NOTFOUND @@ -195,8 +198,7 @@ class vlan(moduleBase): if not self.ipcmd: self.ipcmd = iproute2(**self.get_flags()) - def run(self, ifaceobj, operation, query_ifaceobj=None, - ifaceobj_getfunc=None, **extra_args): + def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): """ run vlan configuration on the interface object passed as argument Args: @@ -222,6 +224,6 @@ class vlan(moduleBase): return self._init_command_handlers() if operation == 'query-checkcurr': - op_handler(self, ifaceobj, query_ifaceobj, ifaceobj_getfunc) + op_handler(self, ifaceobj, query_ifaceobj) else: - op_handler(self, ifaceobj, ifaceobj_getfunc) + op_handler(self, ifaceobj) diff --git a/config/ifupdown2.conf b/config/ifupdown2.conf index d85e188..d450e13 100644 --- a/config/ifupdown2.conf +++ b/config/ifupdown2.conf @@ -12,3 +12,13 @@ template_lookuppath=/etc/network/ifupdown2/templates # Support /etc/network/if-*/ scripts addon_scripts_support=0 + +# ifquery check status strings. +# By default `ifquery --check` prints the check and +# cross marks against interface attributes. +# Use the below strings to modify the default behaviour. + +# check_success_str= +# check_error_str=(x) + +multiple_vlan_aware_bridge_support=0 diff --git a/ifupdown/ifupdownmain.py b/ifupdown/ifupdownmain.py index 1b60e78..c7a8751 100644 --- a/ifupdown/ifupdownmain.py +++ b/ifupdown/ifupdownmain.py @@ -189,6 +189,8 @@ class ifupdownMain(ifupdownBase): self.load_scripts(self.scripts_dir) self.dependency_graph = OrderedDict({}) + self._cache_no_repeats = {} + if self.STATEMANAGER_ENABLE: try: self.statemanager = stateManager() @@ -422,19 +424,26 @@ class ifupdownMain(ifupdownBase): if not self.dependency_graph.get(i): self.dependency_graph[i] = dlist - def _add_ifaceobj(self, ifaceobj): - currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name) - if not currentifaceobjlist: - self.ifaceobjdict[ifaceobj.name]= [ifaceobj] - return - if ifaceobj.compare(currentifaceobjlist[0]): - self.logger.warn('duplicate interface %s found' %ifaceobj.name) - return - currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS - ifaceobj.flags |= iface.HAS_SIBLINGS - self.ifaceobjdict[ifaceobj.name].append(ifaceobj) + def _check_config_no_repeats(self, ifaceobj): + """ check if object has an attribute that is + restricted to a single object in the system. + if yes, warn and return """ + for k,v in self._cache_no_repeats.items(): + iv = ifaceobj.config.get(k) + if iv and iv[0] == v: + self.logger.error('ignoring interface %s. ' %ifaceobj.name + + 'Only one object with attribute ' + + '\'%s %s\' allowed.' %(k, v)) + return True + for k, v in self.config.get('no_repeats', {}).items(): + iv = ifaceobj.config.get(k) + if iv and iv[0] == v: + self._cache_no_repeats[k] = v + return False def _save_iface(self, ifaceobj): + if self._check_config_no_repeats(ifaceobj): + return currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name) if not currentifaceobjlist: self.ifaceobjdict[ifaceobj.name]= [ifaceobj] @@ -442,8 +451,9 @@ class ifupdownMain(ifupdownBase): if ifaceobj.compare(currentifaceobjlist[0]): self.logger.warn('duplicate interface %s found' %ifaceobj.name) return - currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS - ifaceobj.flags |= iface.HAS_SIBLINGS + if currentifaceobjlist[0].type == ifaceobj.type: + currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS + ifaceobj.flags |= iface.HAS_SIBLINGS self.ifaceobjdict[ifaceobj.name].append(ifaceobj) def _iface_configattr_syntax_checker(self, attrname, attrval): @@ -460,7 +470,7 @@ class ifupdownMain(ifupdownBase): def _ifaceobj_syntax_checker(self, ifaceobj): err = False - for attrname in ifaceobj.config: + for attrname, attrvalue in ifaceobj.config.items(): found = False for k, v in self.module_attrs.items(): if v and v.get('attrs', {}).get(attrname): diff --git a/ifupdownaddons/iproute2.py b/ifupdownaddons/iproute2.py index 89d75f2..472d0d1 100644 --- a/ifupdownaddons/iproute2.py +++ b/ifupdownaddons/iproute2.py @@ -616,15 +616,23 @@ class iproute2(utilsBase): [self.exec_command('bridge vlan del vid %s dev %s %s' %(v, bridgeportname, target)) for v in vids] - def bridge_fdb_add(self, dev, address, vlan, bridge=True): + def bridge_fdb_add(self, dev, address, vlan=None, bridge=True): target = 'self' if bridge else '' - self.exec_command('bridge fdb add %s dev %s vlan %s %s' - %(address, dev, vlan, target)) + if vlan: + self.exec_command('bridge fdb add %s dev %s vlan %s %s' + %(address, dev, vlan, target)) + else: + self.exec_command('bridge fdb add %s dev %s %s' + %(address, dev, target)) - def bridge_fdb_del(self, dev, address, vlan, bridge=True): + def bridge_fdb_del(self, dev, address, vlan=None, bridge=True): target = 'self' if bridge else '' - self.exec_command('bridge fdb del %s dev %s vlan %s %s' - %(address, dev, vlan, target)) + if vlan: + self.exec_command('bridge fdb del %s dev %s vlan %s %s' + %(address, dev, vlan, target)) + else: + self.exec_command('bridge fdb del %s dev %s %s' + %(address, dev, target)) def bridge_is_vlan_aware(self, bridgename): filename = '/sys/class/net/%s/bridge/vlan_filtering' %bridgename @@ -645,3 +653,23 @@ class iproute2(utilsBase): %(bridge, bridgeportname)) except Exception: return False + + def bridge_fdb_show_dev(self, dev): + try: + fdbs = {} + output = self.exec_command('bridge fdb show dev %s' %dev) + if output: + for fdb_entry in output.splitlines(): + try: + entries = fdb_entry.split() + fdbs.setdefault(entries[2], []).append(entries[0]) + except: + self.logger.debug('%s: invalid fdb line \'%s\'' + %(dev, fdb_entry)) + pass + return fdbs + except Exception: + return None + + def is_bridge(self, bridge): + return os.path.exists('/sys/class/net/%s/bridge' %bridge) diff --git a/sbin/ifupdown b/sbin/ifupdown index 9dc2257..a5f91f6 100755 --- a/sbin/ifupdown +++ b/sbin/ifupdown @@ -372,6 +372,14 @@ def read_config(): parser.readfp(configFP) configmap_g = dict(parser.items('ifupdown2')) + # Preprocess config map + configval = configmap_g.get('multiple_vlan_aware_bridge_support', '0') + if configval == '0': + # if multiple bridges not allowed, set the bridge-vlan-aware + # attribute in the 'no_repeats' config, so that the ifupdownmain + # module can catch it appropriately + configmap_g['no_repeats'] = {'bridge-vlan-aware' : 'yes'} + def main(argv): """ main function """ args = None