diff --git a/addons/address.py b/addons/address.py index 74d4301..900aa41 100644 --- a/addons/address.py +++ b/addons/address.py @@ -10,6 +10,7 @@ try: from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPv4Address, IPv6Address from sets import Set from ifupdown.iface import * + from ifupdown.utils import utils from ifupdownaddons.modulebase import moduleBase from ifupdownaddons.iproute2 import iproute2 from ifupdownaddons.dhclient import dhclient @@ -53,6 +54,7 @@ class address(moduleBase): 'gateway' : {'help': 'default gateway', 'validvals' : ['', ''], + 'multiline' : True, 'example' : ['gateway 255.255.255.0']}, 'mtu' : { 'help': 'interface mtu', @@ -65,7 +67,6 @@ class address(moduleBase): 'example': ['hwaddress 44:38:39:00:27:b8']}, 'alias' : { 'help': 'description/alias', - 'validvals' : ['',], 'example' : ['alias testnetwork']}, 'address-purge' : { 'help': 'purge existing addresses. By default ' + @@ -221,6 +222,7 @@ class address(moduleBase): ifaceobjlist = [ifaceobj] (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist) + newaddrs = utils.get_normalized_ip_addr(ifaceobj.name, newaddrs) if not addr_supported: return if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)): @@ -234,11 +236,11 @@ class address(moduleBase): if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes': # if perfmode is not set and purge addresses is not set to 'no' # lets purge addresses not in the config - runningaddrs = self.ipcmd.addr_get(ifaceobj.name, details=False) + runningaddrs = utils.get_normalized_ip_addr(ifaceobj.name, self.ipcmd.addr_get(ifaceobj.name, details=False)) # if anycast address is configured on 'lo' and is in running config # add it to newaddrs so that ifreload doesn't wipe it out - anycast_addr = self._get_anycast_addr(ifaceobjlist) + anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist)) if runningaddrs and anycast_addr and anycast_addr in runningaddrs: newaddrs.append(anycast_addr) @@ -335,37 +337,35 @@ class address(moduleBase): try: self.ipcmd.batch_commit() except Exception as e: - self.logger.error('%s: %s' % (ifaceobj.name, str(e))) - ifaceobj.set_status(ifaceStatus.ERROR) - - hwaddress = self._get_hwaddress(ifaceobj) - if hwaddress: - running_hwaddress = None - if not ifupdownflags.flags.PERFMODE: # system is clean - running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) - if hwaddress != running_hwaddress: - slave_down = False - netlink.link_set_updown(ifaceobj.name, "down") - if ifaceobj.link_kind & ifaceLinkKind.BOND: - # if bond, down all the slaves - if ifaceobj.lowerifaces: - for l in ifaceobj.lowerifaces: - netlink.link_set_updown(l, "down") - slave_down = True - try: - self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress) - finally: - netlink.link_set_updown(ifaceobj.name, "up") - if slave_down: - for l in ifaceobj.lowerifaces: - netlink.link_set_updown(l, "up") + self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False) try: + hwaddress = self._get_hwaddress(ifaceobj) + if hwaddress: + running_hwaddress = None + if not ifupdownflags.flags.PERFMODE: # system is clean + running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) + if hwaddress != running_hwaddress: + slave_down = False + netlink.link_set_updown(ifaceobj.name, "down") + if ifaceobj.link_kind & ifaceLinkKind.BOND: + # if bond, down all the slaves + if ifaceobj.lowerifaces: + for l in ifaceobj.lowerifaces: + netlink.link_set_updown(l, "down") + slave_down = True + try: + self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress) + finally: + netlink.link_set_updown(ifaceobj.name, "up") + if slave_down: + for l in ifaceobj.lowerifaces: + netlink.link_set_updown(l, "up") + # Handle special things on a bridge self._process_bridge(ifaceobj, True) except Exception, e: self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) - pass if addr_method != "dhcp": gateways = ifaceobj.get_attr_value('gateway') @@ -468,11 +468,12 @@ class address(moduleBase): # compare addresses if addr_method == 'dhcp': return - addrs = self._get_iface_addresses(ifaceobj) + addrs = utils.get_normalized_ip_addr(ifaceobj.name, + self._get_iface_addresses(ifaceobj)) runningaddrsdict = self.ipcmd.addr_get(ifaceobj.name) # if anycast address is configured on 'lo' and is in running config # add it to addrs so that query_check doesn't fail - anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip') + anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')) if anycast_addr: anycast_addr = anycast_addr+'/32' if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr): diff --git a/addons/addressvirtual.py b/addons/addressvirtual.py index dd5f339..a09b688 100644 --- a/addons/addressvirtual.py +++ b/addons/addressvirtual.py @@ -36,10 +36,9 @@ class addressvirtual(moduleBase): self.ipcmd = None self._bridge_fdb_query_cache = {} - def _is_supported(self, ifaceobj): - if ifaceobj.get_attr_value_first('address-virtual'): - return True - return False + def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): + if ifaceobj.get_attr_value('address-virtual'): + ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE def _get_macvlan_prefix(self, ifaceobj): return '%s-v' %ifaceobj.name[0:13].replace('.', '-') @@ -195,11 +194,22 @@ class addressvirtual(moduleBase): self._fix_connected_route(ifaceobj, macvlan_ifacename, ips[0]) if update_mtu: - lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.lowerifaces[0], refresh=True) + lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.name, refresh=True) update_mtu = False if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifacename): - self.ipcmd.link_set_mtu(macvlan_ifacename, lower_iface_mtu) + try: + self.ipcmd.link_set_mtu(macvlan_ifacename, + lower_iface_mtu) + except Exception as e: + self.logger.info('%s: failed to set mtu %s: %s' % + (macvlan_ifacename, lower_iface_mtu, e)) + + # set macvlan device to up in anycase. + # since we auto create them here..we are responsible + # to bring them up here in the case they were brought down + # by some other entity in the system. + netlink.link_set_updown(macvlan_ifacename, "up") # handle vrf slaves if (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE): @@ -281,10 +291,73 @@ class addressvirtual(moduleBase): self.logger.error("%s: Multicast bit is set in the virtual mac address '%s'" %(ifaceobj.name, mac)) return False return True - except Exception, e: + except Exception: return False - def _up(self, ifaceobj): + def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None): + """ This function fixes up address virtual interfaces + (macvlans) on vrf slaves. Since this fixup is an overhead, + this must be called only in cases when ifupdown2 is + called on the vrf device or its slave and not when + ifupdown2 is called for all devices. When all + interfaces are brought up, the expectation is that + the normal path will fix up a vrf device or its slaves""" + + if not ifaceobj_getfunc: + return + if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and + self.ipcmd.link_exists(ifaceobj.name)): + # if I am a vrf device and I have slaves + # that have address virtual config, + # enslave the slaves 'address virtual + # interfaces (macvlans)' to myself: + running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name) + if running_slaves: + # pick up any existing slaves of a vrf device and + # look for their upperdevices and enslave them to the + # vrf device: + for s in running_slaves: + sobjs = ifaceobj_getfunc(s) + if (sobjs and + (sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)): + # enslave all its upper devices to + # the vrf device + upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name) + if not upperdevs: + continue + for u in upperdevs: + # skip vrf device which + # will also show up in the + # upper device list + if u == ifaceobj.name: + continue + self.ipcmd.link_set(u, 'master', ifaceobj.name, + state='up') + elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and + (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and + self.ipcmd.link_exists(ifaceobj.name)): + # If I am a vrf slave and I have 'address virtual' + # config, make sure my addrress virtual interfaces + # (macvlans) are also enslaved to the vrf device + vrfname = ifaceobj.get_attr_value_first('vrf') + if not vrfname or not self.ipcmd.link_exists(vrfname): + return + running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name) + if not running_uppers: + return + macvlan_prefix = self._get_macvlan_prefix(ifaceobj) + if not macvlan_prefix: + return + for u in running_uppers: + if u == vrfname: + continue + if u.startswith(macvlan_prefix): + self.ipcmd.link_set(u, 'master', vrfname, + state='up') + + def _up(self, ifaceobj, ifaceobj_getfunc=None): + if not ifupdownflags.flags.ALL: + self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc) address_virtual_list = ifaceobj.get_attr_value('address-virtual') if not address_virtual_list: # XXX: address virtual is not present. In which case, @@ -302,7 +375,7 @@ class addressvirtual(moduleBase): return self._apply_address_config(ifaceobj, address_virtual_list) - def _down(self, ifaceobj): + def _down(self, ifaceobj, ifaceobj_getfunc=None): try: self._remove_address_config(ifaceobj, ifaceobj.get_attr_value('address-virtual')) @@ -348,18 +421,31 @@ class addressvirtual(moduleBase): % (ifaceobj.name, macvlan_ifacename, ' '.join(av_attrs))) - 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: - raddress_virtual = '%s %s' %(rhwaddress, ' '.join(raddrs)) - ifaceobjcurr.update_config_with_status('address-virtual', - raddress_virtual, 1) + try: + cmp_av_addr = av_attrs[1:][0] + cmp_raddr = raddrs[0] + + if '/' in cmp_raddr and '/' not in cmp_av_addr: + cmp_av_addr = str(IPNetwork(cmp_av_addr)) + elif '/' in cmp_av_addr and '/' not in cmp_raddr: + cmp_raddr = str(IPNetwork(cmp_raddr)) + + if (rhwaddress == av_attrs[0] and cmp_raddr == cmp_av_addr and + self._check_addresses_in_bridge(ifaceobj, av_attrs[0])): + ifaceobjcurr.update_config_with_status('address-virtual', + address_virtual, 0) + else: + raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs)) + ifaceobjcurr.update_config_with_status('address-virtual', + raddress_virtual, 1) + except: + raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs)) + ifaceobjcurr.update_config_with_status('address-virtual', + raddress_virtual, 1) av_idx += 1 return - def _query_running(self, ifaceobjrunning): + def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning) address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix) for av in address_virtuals: @@ -387,7 +473,8 @@ class addressvirtual(moduleBase): if not self.ipcmd: self.ipcmd = iproute2() - def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): + def run(self, ifaceobj, operation, query_ifaceobj=None, + ifaceobj_getfunc=None, **extra_args): """ run vlan configuration on the interface object passed as argument Args: @@ -412,4 +499,4 @@ class addressvirtual(moduleBase): if operation == 'query-checkcurr': op_handler(self, ifaceobj, query_ifaceobj) else: - op_handler(self, ifaceobj) + op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) diff --git a/addons/bridge.py b/addons/bridge.py index fe51ef3..b91e3b6 100644 --- a/addons/bridge.py +++ b/addons/bridge.py @@ -268,6 +268,25 @@ class bridge(moduleBase): attr='warn_on_untagged_bridge_absence') self.warn_on_untagged_bridge_absence = should_warn == 'yes' + def syntax_check(self, ifaceobj, ifaceobj_getfunc): + return self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc) + + def check_bridge_vlan_aware_port(self, ifaceobj, ifaceobj_getfunc): + if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE: + ports = self._get_bridge_port_list(ifaceobj) + if not ports: + return False + result = True + for port_name in ports: + port_obj_l = ifaceobj_getfunc(port_name) + if port_obj_l and port_obj_l[0].link_kind & ifaceLinkKind.VLAN: + self.logger.error('%s: %s: vlan sub-interface is not ' + 'supported in a vlan-aware bridge' + % (ifaceobj.name, port_name)) + result = False + return result + return True + def _is_bridge(self, ifaceobj): if ifaceobj.get_attr_value_first('bridge-ports'): @@ -487,14 +506,19 @@ class bridge(moduleBase): example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6] """ result = [] - for part in rangelist: - if '-' in part: - a, b = part.split('-') - a, b = int(a), int(b) - result.extend(range(a, b + 1)) - else: - a = int(part) - result.append(a) + try: + for part in rangelist: + if '-' in part: + a, b = part.split('-') + a, b = int(a), int(b) + result.extend(range(a, b + 1)) + else: + a = int(part) + result.append(a) + except: + self.logger.warn('unable to parse vids \'%s\'' + %''.join(rangelist)) + pass return result def _compress_into_ranges(self, vids_ints): @@ -752,16 +776,20 @@ class bridge(moduleBase): def _check_vids(self, ifaceobj, vids): ret = True for v in vids: - if '-' in v: - va, vb = v.split('-') - va, vb = int(va), int(vb) - if (self._handle_reserved_vlan(va, ifaceobj.name) or - self._handle_reserved_vlan(vb, ifaceobj.name)): - ret = False - else: - va = int(v) - if self._handle_reserved_vlan(va, ifaceobj.name): - ret = False + try: + if '-' in v: + va, vb = v.split('-') + va, vb = int(va), int(vb) + if (self._handle_reserved_vlan(va, ifaceobj.name) or + self._handle_reserved_vlan(vb, ifaceobj.name)): + ret = False + else: + va = int(v) + if self._handle_reserved_vlan(va, ifaceobj.name): + ret = False + except Exception: + self.logger.warn('%s: unable to parse vid \'%s\'' + %(ifaceobj.name, v)) return ret def _apply_bridge_port_pvids(self, bportifaceobj, pvid, running_pvid): @@ -868,7 +896,13 @@ class bridge(moduleBase): """ vids_int = self._ranges_to_ints(vids) - pvid_int = int(pvid) if pvid else 0 + try: + pvid_int = int(pvid) if pvid else 0 + except Exception: + self.logger.warn('%s: unable to parse pvid \'%s\'' + %(bportifaceobj.name, pvid)) + pvid_int = 0 + pass vids_to_del = [] vids_to_add = vids_int @@ -1160,6 +1194,8 @@ class bridge(moduleBase): try: bridge_vlan_aware = False if ifaceobj.get_attr_value_first('bridge-vlan-aware') == 'yes': + if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc): + return bridge_vlan_aware = True if (bridge_just_created or not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)): @@ -1214,7 +1250,10 @@ class bridge(moduleBase): pass if ifaceobj.addr_method == 'manual': - netlink.link_set_updown(ifaceobj.name, "up") + try: + netlink.link_set_updown(ifaceobj.name, "up") + except Exception as e: + self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) if err: raise Exception(errstr) @@ -1229,7 +1268,7 @@ class bridge(moduleBase): map(lambda p: netlink.link_set_updown(p, "down"), ports) except Exception, e: - self.log_error(str(e)) + self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) def _query_running_vidinfo_compat(self, ifaceobjrunning, ports): running_attrs = {} @@ -1336,7 +1375,7 @@ class bridge(moduleBase): ports = None skip_kernel_stp_attrs = 0 - if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1': + if self.systcl_get_net_bridge_stp_user_space() == '1': userspace_stp = 1 tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name) @@ -1770,7 +1809,7 @@ class bridge(moduleBase): ifaceobjrunning, None)) def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename): - if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1': + if self.systcl_get_net_bridge_stp_user_space() == '1': return v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name) @@ -1797,6 +1836,8 @@ class bridge(moduleBase): (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str( ifaceobjrunning.name) + if bridge_port_vids and bridge_port_pvid in bridge_port_vids: + bridge_port_vids.remove(bridge_port_pvid) bridgeifaceobjlist = ifaceobj_getfunc(bridgename) if bridgeifaceobjlist: diff --git a/addons/mstpctl.py b/addons/mstpctl.py index 8cafb20..7c9bcdf 100644 --- a/addons/mstpctl.py +++ b/addons/mstpctl.py @@ -7,6 +7,7 @@ import os from sets import Set from ifupdown.iface import * +from ifupdown.utils import utils from ifupdownaddons.modulebase import moduleBase from ifupdownaddons.bridgeutils import brctl from ifupdownaddons.iproute2 import iproute2 @@ -46,29 +47,34 @@ class mstpctl(moduleBase): 'validrange' : ['0', '4096'], 'default' : '300', 'required' : False, + 'jsonAttr': 'ageingTime', 'example' : ['mstpctl-ageing 300']}, 'mstpctl-maxage' : { 'help' : 'max message age', 'validrange' : ['0', '255'], 'default' : '20', + 'jsonAttr': 'maxAge', 'required' : False, 'example' : ['mstpctl-maxage 20']}, 'mstpctl-fdelay' : { 'help' : 'set forwarding delay', 'validrange' : ['0', '255'], 'default' : '15', + 'jsonAttr': 'fwdDelay', 'required' : False, 'example' : ['mstpctl-fdelay 15']}, 'mstpctl-maxhops' : { 'help' : 'bridge max hops', 'validrange' : ['0', '255'], 'default' : '15', + 'jsonAttr': 'maxHops', 'required' : False, 'example' : ['mstpctl-maxhops 15']}, 'mstpctl-txholdcount' : { 'help' : 'bridge transmit holdcount', 'validrange' : ['0', '255'], 'default' : '6', + 'jsonAttr': 'txHoldCounter', 'required' : False, 'example' : ['mstpctl-txholdcount 6']}, 'mstpctl-forcevers' : @@ -76,6 +82,7 @@ class mstpctl(moduleBase): 'validvals' : ['rstp', ], 'default' : 'rstp', 'required' : False, + 'jsonAttr': 'forceProtocolVersion', 'example' : ['mstpctl-forcevers rstp']}, 'mstpctl-portpathcost' : { 'help' : 'bridge port path cost', @@ -127,6 +134,7 @@ class mstpctl(moduleBase): 'default' : '128', 'validvals': [''], 'validrange' : ['0', '240'], + 'jsonAttr': 'treeportprio', 'required' : False, 'example' : ['under the bridge: mstpctl-treeportprio swp1=128 swp2=128', 'under the port (recommended): mstpctl-treeportprio 128']}, @@ -135,6 +143,7 @@ class mstpctl(moduleBase): 'validrange' : ['0', '255'], 'default' : '2', 'required' : False, + 'jsonAttr': 'portHelloTime', 'example' : ['mstpctl-hello 2']}, 'mstpctl-portnetwork' : { 'help' : 'enable/disable bridge assurance capability for a port', @@ -163,7 +172,9 @@ class mstpctl(moduleBase): 'mstpctl-treeportcost' : { 'help' : 'port tree cost', 'validrange' : ['0', '255'], - 'required' : False}, + 'required' : False, + 'jsonAttr': 'extPortCost', + }, 'mstpctl-portbpdufilter' : { 'help' : 'enable/disable bpdu filter on a port. ' + 'syntax varies when defined under a bridge ' + @@ -215,6 +226,17 @@ class mstpctl(moduleBase): else: self.default_vxlan_ports_set_bpduparams = False + def syntax_check(self, ifaceobj, ifaceobj_getfunc): + if self._is_bridge(ifaceobj): + if (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE + and ifaceobj.get_attr_value_first('mstpctl-portadminedge')): + self.logger.error('%s: unsupported use of keyword ' + '\'mstpctl-portadminedge\' when ' + 'bridge-vlan-aware is on' + % ifaceobj.name) + return False + return True + def _is_bridge(self, ifaceobj): if (ifaceobj.get_attr_value_first('mstpctl-ports') or ifaceobj.get_attr_value_first('bridge-ports')): @@ -240,6 +262,12 @@ class mstpctl(moduleBase): return None return self.brctlcmd.get_bridge_ports(ifaceobj.name) + def _get_bridge_port_attr_value(self, bridgename, portname, attr): + json_attr = self.get_mod_subattr(attr, 'jsonAttr') + return self.mstpctlcmd.get_bridge_port_attr(bridgename, + portname, + json_attr) + def _get_bridge_port_list(self, ifaceobj): # port list is also available in the previously @@ -330,12 +358,11 @@ class mstpctl(moduleBase): for port in bridgeports: if not self.brctlcmd.is_bridge_port(port): continue - running_val = self.mstpctlcmd.get_mstpctl_bridgeport_attr(ifaceobj.name, - port, jsonAttr) - if running_val != default_val: - # we will not bother checking since we already checked - self.mstpctlcmd.set_bridgeport_attr(ifaceobj.name, - port, dstattrname, default_val, False) + self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name, + port, + dstattrname, + default_val, + json_attr=jsonAttr) except: self.logger.info('%s: not resetting %s config' %(ifaceobj.name, attrname)) @@ -355,8 +382,12 @@ class mstpctl(moduleBase): # if it is not bridge port, continue if not os.path.exists('/sys/class/net/%s/brport' %port): continue - self.mstpctlcmd.set_bridgeport_attr(ifaceobj.name, - port, dstattrname, val, check) + json_attr = self.get_mod_subattr(attrname, 'jsonAttr') + self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name, + port, + dstattrname, + val, + json_attr=json_attr) except Exception, e: self.log_error('%s: error setting %s (%s)' %(ifaceobj.name, attrname, str(e)), @@ -409,14 +440,18 @@ class mstpctl(moduleBase): (ifaceobj.link_kind & ifaceLinkKind.VXLAN)): for attr in ['mstpctl-portbpdufilter', 'mstpctl-bpduguard']: - config_val = self._get_default_val(attr, ifaceobj, bridgeifaceobj) + json_attr = self.get_mod_subattr(attr, 'jsonAttr') + config_val = self._get_default_val(attr, ifaceobj, + bridgeifaceobj) try: - self.mstpctlcmd.set_bridgeport_attr(bridgename, - ifaceobj.name, self._port_attrs_map[attr], - config_val, check) + self.mstpctlcmd.set_bridge_port_attr(bridgename, + ifaceobj.name, + self._port_attrs_map[attr], + config_val, + json_attr=json_attr) except Exception, e: self.log_warn('%s: error setting %s (%s)' - %(ifaceobj.name, attr, str(e))) + % (ifaceobj.name, attr, str(e))) return applied # set bridge port attributes for attrname, dstattrname in self._port_attrs_map.items(): @@ -427,7 +462,7 @@ class mstpctl(moduleBase): # to see the running value, stp would have to be on # so we would have parsed mstpctl showportdetail json output try: - running_val = self.mstpctlcmd.get_mstpctl_bridgeport_attr(bridgename, + running_val = self.mstpctlcmd.get_bridge_port_attr(bridgename, ifaceobj.name, jsonAttr) except: self.logger.info('%s %s: could not get running %s value' @@ -442,8 +477,8 @@ class mstpctl(moduleBase): continue try: - self.mstpctlcmd.set_bridgeport_attr(bridgename, - ifaceobj.name, dstattrname, config_val, check) + self.mstpctlcmd.set_bridge_port_attr(bridgename, + ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr) applied = True except Exception, e: self.log_error('%s: error setting %s (%s)' @@ -568,6 +603,7 @@ class mstpctl(moduleBase): bridgeattrdict = {} tmpbridgeattrdict = self.mstpctlcmd.get_bridge_attrs(ifaceobjrunning.name) + #self.logger.info('A' + str(tmpbridgeattrdict)) if not tmpbridgeattrdict: return bridgeattrdict @@ -590,7 +626,6 @@ class mstpctl(moduleBase): 'mstpctl-portnetwork' : '', 'mstpctl-portpathcost' : '', 'mstpctl-portadminedge' : '', - 'mstpctl-portautoedge' : '', 'mstpctl-portp2p' : '', 'mstpctl-portrestrrole' : '', 'mstpctl-portrestrtcn' : '', @@ -599,70 +634,26 @@ class mstpctl(moduleBase): 'mstpctl-treeportcost' : ''} for p in ports: - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portautoedge') - if v and v != 'no': - portconfig['mstpctl-portautoedge'] += ' %s=%s' %(p, v) - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portbpdufilter') - if v and v != 'no': - portconfig['mstpctl-portbpdufilter'] += ' %s=%s' %(p, v) + for attr in ['mstpctl-portautoedge', + 'mstpctl-portbpdufilter', + 'mstpctl-portnetwork', + 'mstpctl-portadminedge', + 'mstpctl-portp2p', + 'mstpctl-portrestrrole', + 'mstpctl-portrestrtcn', + 'mstpctl-bpduguard', + '']: + v = self._get_bridge_port_attr_value(ifaceobjrunning.name, + p, attr) + if v and v != 'no': + portconfig[attr] += ' %s=%s' % (p, v) - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portnetwork') - if v and v != 'no': - portconfig['mstpctl-portnetwork'] += ' %s=%s' %(p, v) - - # XXX: Can we really get path cost of a port ??? - #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p) - #if v and v != self.get_mod_subattr('mstpctl-portpathcost', - # 'default'): - # portconfig['mstpctl-portpathcost'] += ' %s=%s' %(p, v) - - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portadminedge') - if v and v != 'no': - portconfig['mstpctl-portadminedge'] += ' %s=%s' %(p, v) - - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portp2p') - if v and v != 'no': - portconfig['mstpctl-portp2p'] += ' %s=%s' %(p, v) - - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portrestrrole') - if v and v != 'no': - portconfig['mstpctl-portrestrrole'] += ' %s=%s' %(p, v) - - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portrestrtcn') - if v and v != 'no': - portconfig['mstpctl-portrestrtcn'] += ' %s=%s' %(p, v) - - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'bpduguard') - if v and v != 'no': - portconfig['mstpctl-bpduguard'] += ' %s=%s' %(p, v) - - # XXX: Can we really get path cost of a port ??? - #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - # p, 'treeprio') - #if v and v != self.get_mod_subattr('mstpctl-treeportprio', - # 'default'): - # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v) - - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'portpathcost') - if v and v != self.get_mod_subattr('mstpctl-portpathcost', - 'default'): - portconfig['mstpctl-portpathcost'] += ' %s=%s' %(p, v) - - v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'treeportcost') - if v and v != self.get_mod_subattr('mstpctl-treeportcost', - 'default'): - portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v) + for attr in ['mstpctl-portpathcost', 'mstpctl-treeportcost']: + v = self._get_bridge_port_attr_value(ifaceobjrunning.name, + p, attr) + if v and v != self.get_mod_subattr(attr, 'default'): + portconfig[attr] += ' %s=%s' % (p, v) bridgeattrdict.update({k : [v] for k, v in portconfig.items() if v}) @@ -685,6 +676,7 @@ class mstpctl(moduleBase): if not ifaceattrs: return runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name) + #self.logger.info('B' + str(runningattrs)) if not runningattrs: runningattrs = {} running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name) @@ -728,7 +720,7 @@ class mstpctl(moduleBase): conf = config_val[bport] jsonAttr = self.get_mod_subattr(k, 'jsonAttr') try: - running_val = self.mstpctlcmd.get_mstpctl_bridgeport_attr(ifaceobj.name, bport, jsonAttr) + running_val = self.mstpctlcmd.get_bridge_port_attr(ifaceobj.name, bport, jsonAttr) except: self.logger.info('%s %s: could not get running %s value' %(ifaceobj.name, bport, attr)) @@ -797,8 +789,7 @@ class mstpctl(moduleBase): for vlistitem in vlist: try: (p, v) = vlistitem.split('=') - currv = self.mstpctlcmd.get_bridgeport_attr( - ifaceobj.name, p, mstpctlcmdattrname) + currv = self._get_bridge_port_attr_value(ifaceobj.name, p, k) if currv: currstr += ' %s=%s' %(p, currv) else: @@ -844,7 +835,7 @@ class mstpctl(moduleBase): continue config_val = 'yes' try: - running_val = self.mstpctlcmd.get_mstpctl_bridgeport_attr(bifaceobj.name, + running_val = self.mstpctlcmd.get_bridge_port_attr(bifaceobj.name, ifaceobj.name, jsonAttr) except: self.logger.info('%s %s: could not get running %s value' @@ -876,6 +867,7 @@ class mstpctl(moduleBase): if not ifaceattrs: return runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name) + #self.logger.info('C' + str(runningattrs)) if not runningattrs: runningattrs = {} for k in ifaceattrs: @@ -885,8 +877,7 @@ class mstpctl(moduleBase): if not v or k in blacklistedattrs: ifaceobjcurr.update_config_with_status(k, v, -1) continue - currv = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobj.name, self._port_attrs_map.get(k)) + currv = self._get_bridge_port_attr_value(bridgename, ifaceobj.name, k) if currv: if currv != v: ifaceobjcurr.update_config_with_status(k, currv, 1) @@ -904,6 +895,15 @@ class mstpctl(moduleBase): else: self._query_check_bridge_port(ifaceobj, ifaceobjcurr) + def _query_bridge_port_attr(self, ifaceobjrunning, bridgename, attr, value_cmp): + v = self._get_bridge_port_attr_value(bridgename, + ifaceobjrunning.name, + attr) + if v and value_cmp and v != value_cmp: + ifaceobjrunning.update_config(attr, v) + elif v and not value_cmp: + ifaceobjrunning.update_config(attr, v) + def _query_running_bridge_port(self, ifaceobjrunning): bridgename = self.ipcmd.bridge_port_get_bridge_name( ifaceobjrunning.name) @@ -915,27 +915,20 @@ class mstpctl(moduleBase): # This bridge does not run stp, return return # if userspace stp not set, return - if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1': - return + if self.systcl_get_net_bridge_stp_user_space() != '1': + return - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, - 'portautoedge') - if v and v != self.get_mod_subattr('mstpctl-portautoedge', - 'default'): - ifaceobjrunning.update_config('mstpctl-portautoedge', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-portautoedge', + self.get_mod_subattr('mstpctl-portautoedge', 'default')) - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, - 'portbpdufilter') - if v and v != 'no': - ifaceobjrunning.update_config('mstpctl-portbpdufilter', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-portbpdufilter', + 'no') - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, - 'portnetwork') - if v and v != 'no': - ifaceobjrunning.update_config('mstpctl-portnetwork', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-portnetwork', + 'no') # XXX: Can we really get path cost of a port ??? #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p) @@ -943,30 +936,25 @@ class mstpctl(moduleBase): # 'default'): # ifaceobjrunning.update_config('mstpctl-network', v) - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, 'portadminedge') - if v and v != 'no': - ifaceobjrunning.update_config('mstpctl-portadminedge', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-portadminedge', + 'no') - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name,'portp2p') - if v and v != 'auto': - ifaceobjrunning.update_config('mstpctl-portp2p', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-portp2p', + 'auto') - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, 'portrestrrole') - if v and v != 'no': - ifaceobjrunning.update_config('mstpctl-portrestrrole', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-portrestrrole', + 'no') - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, 'portrestrtcn') - if v and v != 'no': - ifaceobjrunning.update_config('mstpctl-portrestrtcn', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-portrestrtcn', + 'no') - v = self.mstpctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, 'bpduguard') - if v and v != 'no': - ifaceobjrunning.update_config('mstpctl-bpduguard', v) + self._query_bridge_port_attr(ifaceobjrunning, bridgename, + 'mstpctl-bpduguard', + 'no') # XXX: Can we really get path cost of a port ??? #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, @@ -986,8 +974,8 @@ class mstpctl(moduleBase): # This bridge does not run stp, return return # if userspace stp not set, return - if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1': - return + if self.systcl_get_net_bridge_stp_user_space() != '1': + return # Check if mstp really knows about this bridge if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name): return diff --git a/addons/vlan.py b/addons/vlan.py index fff1d07..827047e 100644 --- a/addons/vlan.py +++ b/addons/vlan.py @@ -146,7 +146,10 @@ class vlan(moduleBase): netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid) self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid) if ifaceobj.addr_method == 'manual': - netlink.link_set_updown(ifaceobj.name, "up") + try: + netlink.link_set_updown(ifaceobj.name, "up") + except Exception as e: + self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) def _down(self, ifaceobj): vlanid = self._get_vlan_id(ifaceobj) diff --git a/addons/vrf.py b/addons/vrf.py index 79edd68..0e02a54 100644 --- a/addons/vrf.py +++ b/addons/vrf.py @@ -123,7 +123,7 @@ class vrf(moduleBase): self.vrf_fix_local_table = True self.vrf_count = 0 self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper') - + self.vrf_close_socks_on_down = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-close-socks-on-down') self.warn_on_vrf_map_write_err = True def _iproute2_vrf_map_initialize(self, writetodisk=True): @@ -151,8 +151,7 @@ class vrf(moduleBase): continue self.iproute2_vrf_map[int(table)] = vrf_name except Exception, e: - self.logger.info('vrf: iproute2_vrf_map: unable to parse %s' - %l) + self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e))) pass vrfs = self.ipcmd.link_get_vrfs() @@ -302,7 +301,6 @@ class vrf(moduleBase): self.iproute2_vrf_map_fd.flush() self.vrf_count += 1 return - if old_vrf_name != vrfifaceobj.name: self.log_error('table id %d already assigned to vrf dev %s' %(table_id, old_vrf_name)) @@ -313,7 +311,7 @@ class vrf(moduleBase): self.iproute2_vrf_map_sync_to_disk = True del self.iproute2_vrf_map[int(table_id)] except Exception, e: - self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)' + self.logger.info('vrf: iproute2 vrf map del failed for %s (%s)' %(table_id, str(e))) pass @@ -393,8 +391,8 @@ class vrf(moduleBase): try: master_exists = True if vrf_exists or self.ipcmd.link_exists(vrfname): - upper = self.ipcmd.link_get_upper(ifacename) - if not upper or upper != vrfname: + uppers = self.ipcmd.link_get_uppers(ifacename) + if not uppers or vrfname not in uppers: self._handle_existing_connections(ifaceobj, vrfname) self.ipcmd.link_set(ifacename, 'master', vrfname) elif ifaceobj: @@ -801,8 +799,23 @@ class vrf(moduleBase): vrf_table, mode)) + def _close_sockets(self, ifaceobj, ifindex): + if not self.vrf_close_socks_on_down: + return + + try: + utils.exec_command('/bin/ss -aK \"dev == %s\"' + %ifindex) + except Exception, e: + self.logger.info('%s: closing socks using ss' + ' failed (%s)\n' %(ifaceobj.name, str(e))) + pass + def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): + if not self.ipcmd.link_exists(ifaceobj.name): + return + if vrf_table == 'auto': vrf_table = self._get_iproute2_vrf_table(ifaceobj.name) @@ -837,12 +850,16 @@ class vrf(moduleBase): self.logger.info('%s: %s' %(ifaceobj.name, str(e))) pass + ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name) + try: self.ipcmd.link_delete(ifaceobj.name) except Exception, e: self.logger.info('%s: %s' %(ifaceobj.name, str(e))) pass + self._close_sockets(ifaceobj, ifindex) + try: self._iproute2_vrf_table_entry_del(vrf_table) except Exception, e: @@ -854,7 +871,12 @@ class vrf(moduleBase): try: self._handle_existing_connections(ifaceobj, vrfname) self.ipcmd.link_set(ifacename, 'nomaster') - netlink.link_set_updown(ifacename, "down") + # Down this slave only if it is a slave ifupdown2 manages. + # we dont want to down slaves that maybe up'ed by + # somebody else. One such example is a macvlan device + # which ifupdown2 addressvirtual addon module auto creates + if ifaceobj: + netlink.link_set_updown(ifacename, "down") except Exception, e: self.logger.warn('%s: %s' %(ifacename, str(e))) diff --git a/debian/changelog b/debian/changelog index c9a799a..e5c958d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,10 +1,26 @@ +ifupdown2 (1.1-cl3u6) UNRELEASED; urgency=medium + + * Closes: github #14. add environment variables passed to user scripts + * New. Enabled: addons may provide a list of ifupdown scripts to ignore + + -- dev-support Mon, 19 Sep 2016 16:37:36 -0700 + +ifupdown2 (1.1-cl3u5) RELEASED; urgency=medium + + * Closes: CM-12798. fix handling of EXISTS errors on address add + * Closes: CM-11214. fix handling of mtu on addressvirtual macvlan devices + * Closes: CM-12884. fix mako namespace handling + + -- dev-support Fri, 16 Sep 2016 12:48:04 -0700 + ifupdown2 (1.1-cl3u4) RELEASED; urgency=medium * Performance improvements * New. Enabled: sbin: start-networking: support hotplug class from init script * New. Enabled: support for classical numerical bond modes * New. Enabled: extend ifquery support for mstpctl addons - * New. Enabled: each addon may perform semantic and syntax checks by implementing a custom method + * New. Enabled: each addon may perform semantic and syntax checks by + implementing a custom method * Closes: CM-11745. Support for address-virtual lines under a vrf slave * Closes: CM-11718. Defaults for link attributes were not applied * Closes: CM-11511. Disable IPv6 duplicate address detection on VRR interfaces @@ -15,7 +31,8 @@ ifupdown2 (1.1-cl3u4) RELEASED; urgency=medium ifupdown2 (1.1-cl3u3) RELEASED; urgency=medium - * Closes: CM-11214. Interface configuration parsing error when keyword vlan is the interface name. + * Closes: CM-11214. Interface configuration parsing error when keyword vlan + is the interface name. -- dev-support Sun, 05 Jun 2016 08:55:50 -0700 @@ -25,10 +42,12 @@ ifupdown2 (1.1-cl3u2) RELEASED; urgency=medium * New. Deprecated: `mstpctl-stp` attribute * New. Deprecated: lacp parameters: bond-ad-sys-priority, bond-ad-sys-mac-addr * New. Enabled: addon module for configuring vrf - * New. Enabled: bridge: display warning when (in vlan unware bridge) an untagged bridge is not configured + * New. Enabled: bridge: display warning when (in vlan unware bridge) + an untagged bridge is not configured * New. Enabled: adjusting MTU for vlan devices depending on lower device mtu * New. Enabled: introduce checks for reserved vrf table names - * New. Enabled: ifquery: new option '--with-defaults' to include default attributes + * New. Enabled: ifquery: new option '--with-defaults' to include default + attributes * New. Enabled: bridge: disabling ipv6 on bridge if any VXLAN port * New. Enabled: vrf awareness in dhcp addon module diff --git a/ifupdown/iface.py b/ifupdown/iface.py index 9dd5e14..d668a75 100644 --- a/ifupdown/iface.py +++ b/ifupdown/iface.py @@ -62,6 +62,7 @@ class ifaceLinkPrivFlags(): VRF_SLAVE = 0x00100 BRIDGE_VLAN_AWARE = 0x01000 BRIDGE_VXLAN = 0x10000 + ADDRESS_VIRTUAL_SLAVE = 0x100000 @classmethod def get_str(cls, flag): diff --git a/ifupdown/ifupdownflags.py b/ifupdown/ifupdownflags.py index 4be891a..d76e733 100644 --- a/ifupdown/ifupdownflags.py +++ b/ifupdown/ifupdownflags.py @@ -17,6 +17,7 @@ class ifupdownFlags(): self.PERFMODE = False self.CACHE = False self.WITHDEFAULTS = False + self.IGNORE_ERRORS = False # Flags self.CACHE_FLAGS = 0x0 diff --git a/ifupdown/ifupdownmain.py b/ifupdown/ifupdownmain.py index b93adbc..c9457d0 100644 --- a/ifupdown/ifupdownmain.py +++ b/ifupdown/ifupdownmain.py @@ -306,7 +306,7 @@ class ifupdownMain(ifupdownBase): # is a LINK_SLAVE of a bridge (in other words the bond is # part of a bridge) which is not up yet if self._link_master_slave: - if 'Network is down': + if 'Network is down' in errorstr: return True return False @@ -318,7 +318,7 @@ class ifupdownMain(ifupdownBase): if self.flags.STATEMANAGER_ENABLE: return self.statemanager.get_ifaceobjs(ifacename) else: - None + return None def get_ifaceobj_first(self, ifacename): ifaceobjs = self.get_ifaceobjs(ifacename) @@ -1089,7 +1089,8 @@ class ifupdownMain(ifupdownBase): 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)): + if not module.syntax_check(self.get_ifaceobjs(ifacename)[0], + self.get_ifaceobjs): result = False except Exception, e: self.logger.warn('%s: %s' % (ifacename, str(e))) diff --git a/ifupdown/netlink.py b/ifupdown/netlink.py index 3a7e7b2..11aba56 100644 --- a/ifupdown/netlink.py +++ b/ifupdown/netlink.py @@ -20,8 +20,6 @@ class Netlink(utilsBase): self._nlmanager_api = NetlinkManager() def get_iface_index(self, ifacename): - self.logger.info('%s: netlink: %s: get iface index' - % (ifacename, ifacename)) if ifupdownflags.flags.DRYRUN: return try: return self._nlmanager_api.get_iface_index(ifacename) diff --git a/ifupdown/scheduler.py b/ifupdown/scheduler.py index c79278f..22d32a5 100644 --- a/ifupdown/scheduler.py +++ b/ifupdown/scheduler.py @@ -567,10 +567,21 @@ class ifaceScheduler(): ifupdownobj.logger.info('running upperifaces (parent interfaces) ' + 'if available ..') try: + # upperiface bring up is best effort. + # eg case: if we are bringing up a bridge port + # this section does an 'ifup on the bridge' + # so that the recently up'ed bridge port gets enslaved + # to the bridge. But the up on the bridge may + # throw out more errors if the bridge is not + # in the correct state. Lets not surprise + # the user with such errors when he has + # only requested to bring up the bridge port. cls._STATE_CHECK = False + ifupdownflags.flags.IGNORE_ERRORS = True cls.run_upperifaces(ifupdownobj, ifacenames, ops) - cls._STATE_CHECK = True finally: + ifupdownflags.flags.IGNORE_ERRORS = False + cls._STATE_CHECK = True # upperiface bringup is best effort, so dont propagate errors # reset scheduler status to True cls.set_sched_status(True) diff --git a/ifupdown/utils.py b/ifupdown/utils.py index 6aeba0b..1962d40 100644 --- a/ifupdown/utils.py +++ b/ifupdown/utils.py @@ -17,6 +17,7 @@ import subprocess import ifupdownflags from functools import partial +from ipaddr import IPNetwork, IPAddress def signal_handler_f(ps, sig, frame): if ps: @@ -164,6 +165,26 @@ class utils(): else: return 'cmd \'%s\' failed: returned %d' % (cmd, cmd_returncode) + @classmethod + def get_normalized_ip_addr(cls, ifacename, ipaddrs): + if not ipaddrs: return None + if isinstance(ipaddrs, list): + addrs = [] + for ip in ipaddrs: + if not ip: + continue + try: + addrs.append(str(IPNetwork(ip)) if '/' in ip else str(IPAddress(ip))) + except Exception as e: + cls.logger.warning('%s: %s' % (ifacename, e)) + return addrs + else: + try: + return str(IPNetwork(ipaddrs)) if '/' in ipaddrs else str(IPAddress(ipaddrs)) + except Exception as e: + cls.logger.warning('%s: %s' % (ifacename, e)) + return ipaddrs + @classmethod def _execute_subprocess(cls, cmd, env=None, diff --git a/ifupdownaddons/iproute2.py b/ifupdownaddons/iproute2.py index c1efba7..dddbb41 100644 --- a/ifupdownaddons/iproute2.py +++ b/ifupdownaddons/iproute2.py @@ -434,7 +434,8 @@ class iproute2(utilsBase): def link_down(self, ifacename): self._link_set_ifflag(ifacename, 'DOWN') - def link_set(self, ifacename, key, value=None, force=False, type=None): + def link_set(self, ifacename, key, value=None, + force=False, type=None, state=None): if not force: if (key not in ['master', 'nomaster'] and self._cache_check('link', [ifacename, key], value)): @@ -445,6 +446,8 @@ class iproute2(utilsBase): cmd += ' %s' %key if value: cmd += ' %s' %value + if state: + cmd += ' %s' %state if self.ipbatch: self.add_to_batch(cmd) else: @@ -631,6 +634,11 @@ class iproute2(utilsBase): return True return os.path.exists('/sys/class/net/%s' %ifacename) + def link_get_ifindex(self, ifacename): + if ifupdownflags.flags.DRYRUN: + return True + return self.read_file_oneline('/sys/class/net/%s/ifindex' %ifacename) + def is_vlan_device_by_name(self, ifacename): if re.search(r'\.', ifacename): return True @@ -917,12 +925,12 @@ class iproute2(utilsBase): except: return [] - def link_get_upper(self, ifacename): + def link_get_uppers(self, ifacename): try: - upper = glob.glob("/sys/class/net/%s/upper_*" %ifacename) - if not upper: + uppers = glob.glob("/sys/class/net/%s/upper_*" %ifacename) + if not uppers: return None - return os.path.basename(upper[0])[6:] + return [ os.path.basename(u)[6:] for u in uppers ] except: return None diff --git a/ifupdownaddons/modulebase.py b/ifupdownaddons/modulebase.py index 2fa2ce7..96f07ef 100644 --- a/ifupdownaddons/modulebase.py +++ b/ifupdownaddons/modulebase.py @@ -37,10 +37,12 @@ class moduleBase(object): re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"), re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")] + self._bridge_stp_user_space = None + def log_warn(self, str, ifaceobj=None): """ log a warning if err str is not one of which we should ignore """ - if not self.ignore_error(str): + if not self.ignore_error(str) and not ifupdownflags.flags.IGNORE_ERRORS: if self.logger.getEffectiveLevel() == logging.DEBUG: traceback.print_stack() self.logger.warn(str) @@ -50,7 +52,7 @@ class moduleBase(object): def log_error(self, str, ifaceobj=None, raise_error=True): """ log an err if err str is not one of which we should ignore and raise an exception """ - if not self.ignore_error(str): + if not self.ignore_error(str) and not ifupdownflags.flags.IGNORE_ERRORS: if self.logger.getEffectiveLevel() == logging.DEBUG: traceback.print_stack() if raise_error: @@ -253,6 +255,12 @@ class moduleBase(object): """ get value of sysctl variable """ return utils.exec_command('sysctl %s' % variable).split('=')[1].strip() + def systcl_get_net_bridge_stp_user_space(self): + if self._bridge_stp_user_space: + return self._bridge_stp_user_space + self._bridge_stp_user_space = self.sysctl_get('net.bridge.bridge-stp-user-space') + return self._bridge_stp_user_space + def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc, prehook=None, prehookargs=None): ifacename = ifaceobj.name diff --git a/ifupdownaddons/mstpctlutil.py b/ifupdownaddons/mstpctlutil.py index 468244f..1b069b5 100644 --- a/ifupdownaddons/mstpctlutil.py +++ b/ifupdownaddons/mstpctlutil.py @@ -9,7 +9,6 @@ from utilsbase import * from ifupdown.iface import * from ifupdown.utils import utils from cache import * -import re import json class mstpctlutil(utilsBase): @@ -51,114 +50,70 @@ class mstpctlutil(utilsBase): else: return True - def get_bridgeport_attr(self, bridgename, portname, attrname): - try: - cmdl = ['/sbin/mstpctl', 'showportdetail', bridgename, portname, - self._bridgeportattrmap[attrname]] - return utils.exec_commandl(cmdl).strip('\n') - except Exception, e: - pass - return None - - def get_bridgeport_attrs(self, bridgename, portname): - bridgeattrs = {} - try: - bridgeattrs = dict((k, self.get_bridgeport_attr(bridgename, v)) - for k, v in self._bridgeattrmap.items()) - bridgeattrs['treeprio'] = int(bridgeattrs.get('bridgeid', - '').split('.')[0], base=16) * 4096 - except Exception, e: - self.logger.warn(str(e)) - pass - return bridgeattrs - def _extract_bridge_port_prio(self, portid): try: return str(int(portid[0], 16) * 16) except: - pass - return mstpctlutil._DEFAULT_PORT_PRIO + return mstpctlutil._DEFAULT_PORT_PRIO - def _get_mstpctl_bridgeport_attr_from_cache(self, bridgename): + def _get_bridge_port_attrs_from_cache(self, bridgename): attrs = MSTPAttrsCache.get(bridgename) - if not attrs: - try: - cmd = ['/sbin/mstpctl', 'showportdetail', bridgename, 'json'] - output = utils.exec_commandl(cmd) - if not output: - return None - except Exception as e: - self.logger.info(str(e)) - return None - mstpctl_bridgeport_attrs_dict = {} - try: - mstpctl_bridge_cache = json.loads(output.strip('\n')) - for portname in mstpctl_bridge_cache.keys(): - # we will ignore the portid for now and just index - # by bridgename, portname, and json attribute - for portid in mstpctl_bridge_cache[portname].keys(): - mstpctl_bridgeport_attrs_dict[portname] = {} - mstpctl_bridgeport_attrs_dict[portname]['treeportprio'] = self._extract_bridge_port_prio(portid) - for jsonAttr in mstpctl_bridge_cache[portname][portid].keys(): - jsonVal = mstpctl_bridge_cache[portname][portid][jsonAttr] - mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal) - MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict) + if attrs: + return attrs + mstpctl_bridgeport_attrs_dict = {} + try: + cmd = ['/sbin/mstpctl', 'showportdetail', bridgename, 'json'] + output = utils.exec_commandl(cmd) + if not output: return mstpctl_bridgeport_attrs_dict - except Exception as e: - self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s', str(e)) - return attrs + except Exception as e: + self.logger.info(str(e)) + return mstpctl_bridgeport_attrs_dict + try: + mstpctl_bridge_cache = json.loads(output.strip('\n')) + for portname in mstpctl_bridge_cache.keys(): + for portid in mstpctl_bridge_cache[portname].keys(): + mstpctl_bridgeport_attrs_dict[portname] = {} + mstpctl_bridgeport_attrs_dict[portname]['treeportprio'] = self._extract_bridge_port_prio(portid) + for jsonAttr in mstpctl_bridge_cache[portname][portid].keys(): + jsonVal = mstpctl_bridge_cache[portname][portid][jsonAttr] + mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal) + MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict) + except Exception as e: + self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % str(e)) + return mstpctl_bridgeport_attrs_dict - def get_mstpctl_bridgeport_attr(self, bridgename, portname, attr): - attrs = self._get_mstpctl_bridgeport_attr_from_cache(bridgename) + def get_bridge_ports_attrs(self, bridgename): + return self._get_bridge_port_attrs_from_cache(bridgename) + + def get_bridge_port_attr(self, bridgename, portname, attrname): + attrs = self._get_bridge_port_attrs_from_cache(bridgename) + value = attrs.get(portname, {}).get(attrname, 'no') + if value == 'True' or value == 'true': + return 'yes' + return str(value) + + def update_cache(self, bridgename, portname, attrname, value): + attrs = self.get_bridge_ports_attrs(bridgename) if not attrs: - return 'no' - else: - val = attrs.get(portname,{}).get(attr, 'no') - if val == 'True': - val = 'yes' - return str(val) + attrs = {} + if not portname in attrs: + attrs[portname] = {} + attrs[portname][attrname] = value + MSTPAttrsCache.set(bridgename, attrs) - def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict, - check=True): - for k, v in attrdict.iteritems(): - if not v: - continue - try: - self.set_bridgeport_attr(bridgename, bridgeportname, - k, v, check) - except Exception, e: - self.logger.warn(str(e)) - - def _get_bridge_port_attr_with_prio(self, - bridgename, - bridgeportname, - attrname): - attrvalue_curr = self.get_bridgeport_attr(bridgename, - bridgeportname, attrname) - if attrname == 'treeportprio': - try: - attrs = self._get_mstpctl_bridgeport_attr_from_cache(bridgename) - attrvalue_curr = attrs[bridgeportname]['treeportprio'] - except: - pass - return attrvalue_curr - - def set_bridgeport_attr(self, bridgename, bridgeportname, attrname, - attrvalue, check=True): - if check: - attrvalue_curr = self._get_bridge_port_attr_with_prio(bridgename, - bridgeportname, - attrname) - if attrvalue_curr and attrvalue_curr == attrvalue: - return + def set_bridge_port_attr(self, bridgename, portname, attrname, value, json_attr=None): + cache_value = self.get_bridge_port_attr(bridgename, portname, json_attr) + if cache_value and cache_value == value: + return if attrname == 'treeportcost' or attrname == 'treeportprio': utils.exec_commandl(['/sbin/mstpctl', 'set%s' % attrname, - '%s' % bridgename, '%s' % bridgeportname, '0', - '%s' % attrvalue]) + bridgename, portname, '0', value]) else: utils.exec_commandl(['/sbin/mstpctl', 'set%s' % attrname, - '%s' % bridgename, '%s' % bridgeportname, - '%s' % attrvalue]) + bridgename, portname, value]) + if json_attr: + self.update_cache(bridgename, portname, json_attr, value) def get_bridge_attrs(self, bridgename): bridgeattrs = {} @@ -206,7 +161,6 @@ class mstpctlutil(utilsBase): self.set_bridge_attr(bridgename, k, v, check) except Exception, e: self.logger.warn('%s: %s' %(bridgename, str(e))) - pass def get_bridge_treeprio(self, bridgename): try: diff --git a/nlmanager/nlmanager.py b/nlmanager/nlmanager.py index 6406677..8db7751 100644 --- a/nlmanager/nlmanager.py +++ b/nlmanager/nlmanager.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from collections import OrderedDict from ipaddr import IPv4Address, IPv6Address from nlpacket import * from select import select @@ -137,9 +138,8 @@ class NetlinkManager(object): self.tx_socket_allocate() self.tx_socket.sendall(nlpacket.message) - # If debugs are enabled we will print the contents of the - # packet via the decode_packet call...so avoid printing - # two messages for one packet. + # If nlpacket.debug is True we already printed the following in the + # build_message() call...so avoid printing two messages for one packet. if not nlpacket.debug: log.debug("TXed %12s, pid %d, seq %d, %d bytes" % (nlpacket.get_type_string(), nlpacket.pid, nlpacket.seq, nlpacket.length)) @@ -223,11 +223,13 @@ class NetlinkManager(object): (pid, nlpacket.pid)) data = data[length:] continue + if seq != nlpacket.seq: log.debug(debug_str + '...we are not interested in this seq %s since ours is %s' % (seq, nlpacket.seq)) data = data[length:] continue + # See if we RXed an ACK for our RTM_GETXXXX if msgtype == NLMSG_DONE: log.debug(debug_str + '...this is an ACK') @@ -243,25 +245,32 @@ class NetlinkManager(object): # 0 is NLE_SUCCESS...everything else is a true error if error_code: error_code_str = msg.error_to_string.get(error_code) - - if error_code_str != 'None': - error_str = 'Operation failed with \'%s\' (%s)' % (error_code_str, debug_str) + if error_code_str: + error_str = 'Operation failed with \'%s\'' % error_code_str else: - error_str = 'Operation failed with code %s (%s)' % (error_code, debug_str) + error_str = 'Operation failed with code %s' % error_code + + log.debug(debug_str) if error_code == Error.NLE_NOADDR: raise NetlinkNoAddressError(error_str) elif error_code == Error.NLE_INTR: nle_intr_count += 1 - log.info("%s: RXed NLE_INTR Interrupted system call %d/%d" % (s, nle_intr_count, MAX_ERROR_NLE_INTR)) + log.debug("%s: RXed NLE_INTR Interrupted system call %d/%d" % (s, nle_intr_count, MAX_ERROR_NLE_INTR)) if nle_intr_count >= MAX_ERROR_NLE_INTR: raise NetlinkInterruptedSystemCall(error_str) + else: - if error_code_str == 'None': + msg.dump() + if not error_code_str: try: # os.strerror might raise ValueError - raise NetlinkError('Operation failed with \'%s\' (%s)' % (os.strerror(error_code), debug_str)) + strerror = os.strerror(error_code) + if strerror: + raise NetlinkError('Operation failed with \'%s\'' % strerror) + else: + raise NetlinkError(error_str) except ValueError: pass raise NetlinkError(error_str) @@ -273,12 +282,6 @@ class NetlinkManager(object): else: nle_intr_count = 0 - # If debugs are enabled we will print the contents of the - # packet via the decode_packet call...so avoid printing - # two messages for one packet. - if not nlpacket.debug: - log.debug(debug_str) - if msgtype == RTM_NEWLINK or msgtype == RTM_DELLINK: msg = Link(msgtype, nlpacket.debug) @@ -297,6 +300,9 @@ class NetlinkManager(object): msg.decode_packet(length, flags, seq, pid, data) msgs.append(msg) + if nlpacket.debug: + msg.dump() + data = data[length:] def ip_to_afi(self, ip): @@ -539,40 +545,178 @@ class NetlinkManager(object): """ return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE}) - def _link_bridge_vlan(self, msgtype, ifindex, vlanid, pvid, untagged, master): + def vlan_get(self, filter_ifindex=(), filter_vlanid=(), compress_vlans=True): """ - Build and TX a RTM_NEWLINK message to add the desired interface + filter_ifindex should be a tuple if interface indexes, this is a whitelist filter + filter_vlandid should be a tuple if VLAN IDs, this is a whitelist filter """ + debug = RTM_GETLINK in self.debug - if master: - flags = 0 - else: - flags = Link.BRIDGE_FLAGS_SELF + link = Link(RTM_GETLINK, debug) + link.flags = NLM_F_DUMP | NLM_F_REQUEST + link.body = pack('Bxxxiii', socket.AF_BRIDGE, 0, 0, 0) - if pvid: - vflags = Link.BRIDGE_VLAN_INFO_PVID | Link.BRIDGE_VLAN_INFO_UNTAGGED - elif untagged: - vflags = Link.BRIDGE_VLAN_INFO_UNTAGGED + if compress_vlans: + link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN_COMPRESSED) else: - vflags = 0 + link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN) + + link.build_message(self.sequence.next(), self.pid) + reply = self.tx_nlpacket_get_response(link) + + iface_vlans = {} + + for msg in reply: + if msg.family != socket.AF_BRIDGE: + continue + + if filter_ifindex and msg.ifindex not in filter_ifindex: + continue + + ifla_af_spec = msg.get_attribute_value(Link.IFLA_AF_SPEC) + + if not ifla_af_spec: + continue + + ifname = msg.get_attribute_value(Link.IFLA_IFNAME) + + ''' + Example IFLA_AF_SPEC + + 20: 0x1c001a00 .... Length 0x001c (28), Type 0x001a (26) IFLA_AF_SPEC + 21: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO + 22: 0x00000a00 .... + 23: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO + 24: 0x00001000 .... + 25: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO + 26: 0x00001400 .... + ''' + for (x_type, x_value) in ifla_af_spec.iteritems(): + if x_type == Link.IFLA_BRIDGE_VLAN_INFO: + for (vlan_flag, vlan_id) in x_value: + if filter_vlanid is None or vlan_id in filter_vlanid: + + if ifname not in iface_vlans: + iface_vlans[ifname] = [] + + # We store these in the tuple as (vlan, flag) instead (flag, vlan) + # so that we can sort the list of tuples + iface_vlans[ifname].append((vlan_id, vlan_flag)) + + return iface_vlans + + def vlan_show(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True): + + def vlan_flag_to_string(vlan_flag): + flag_str = [] + if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID: + flag_str.append('PVID') + + if vlan_flag & Link.BRIDGE_VLAN_INFO_UNTAGGED: + flag_str.append('Egress Untagged') + + return ', '.join(flag_str) + + iface_vlans = self.vlan_get(filter_ifindex, filter_vlanid, compress_vlans) + log.debug("iface_vlans:\n%s\n" % pformat(iface_vlans)) + range_begin_vlan_id = None + range_flag = None + + print " Interface VLAN Flags" + print " ========== ==== =====" + + for (ifname, vlan_tuples) in sorted(iface_vlans.iteritems()): + for (vlan_id, vlan_flag) in sorted(vlan_tuples): + + if vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN: + range_begin_vlan_id = vlan_id + range_flag = vlan_flag + + elif vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_END: + range_flag |= vlan_flag + + if not range_begin_vlan_id: + log.warning("BRIDGE_VLAN_INFO_RANGE_END is %d but we never saw a BRIDGE_VLAN_INFO_RANGE_BEGIN" % vlan_id) + range_begin_vlan_id = vlan_id + + for x in xrange(range_begin_vlan_id, vlan_id + 1): + print " %10s %4d %s" % (ifname, x, vlan_flag_to_string(vlan_flag)) + ifname = '' + + range_begin_vlan_id = None + range_flag = None + + else: + print " %10s %4d %s" % (ifname, vlan_id, vlan_flag_to_string(vlan_flag)) + ifname = '' + + + def vlan_modify(self, msgtype, ifindex, vlanid_start, vlanid_end=None, bridge_self=False, bridge_master=False, pvid=False, untagged=False): + """ + iproute2 bridge/vlan.c vlan_modify() + """ + assert msgtype in (RTM_SETLINK, RTM_DELLINK), "Invalid msgtype %s, must be RTM_SETLINK or RTM_DELLINK" % msgtype + assert vlanid_start >= 1 and vlanid_start <= 4096, "Invalid VLAN start %s" % vlanid_start + + if vlanid_end is None: + vlanid_end = vlanid_start + + assert vlanid_end >= 1 and vlanid_end <= 4096, "Invalid VLAN end %s" % vlanid_end + assert vlanid_start <= vlanid_end, "Invalid VLAN range %s-%s, start must be <= end" % (vlanid_start, vlanid_end) debug = msgtype in self.debug + bridge_flags = 0 + vlan_info_flags = 0 link = Link(msgtype, debug) link.flags = NLM_F_REQUEST | NLM_F_ACK link.body = pack('Bxxxiii', socket.AF_BRIDGE, ifindex, 0, 0) - link.add_attribute(Link.IFLA_AF_SPEC, { - Link.IFLA_BRIDGE_FLAGS: flags, - Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid) - }) + + if bridge_self: + bridge_flags |= Link.BRIDGE_FLAGS_SELF + + if bridge_master: + bridge_flags |= Link.BRIDGE_FLAGS_MASTER + + if pvid: + vlan_info_flags |= Link.BRIDGE_VLAN_INFO_PVID + + if untagged: + vlan_info_flags |= Link.BRIDGE_VLAN_INFO_UNTAGGED + + ifla_af_spec = OrderedDict() + + if bridge_flags: + ifla_af_spec[Link.IFLA_BRIDGE_FLAGS] = bridge_flags + + # just one VLAN + if vlanid_start == vlanid_end: + ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [(vlan_info_flags, vlanid_start), ] + + # a range of VLANs + else: + ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [ + (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_BEGIN, vlanid_start), + (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_END, vlanid_end) + ] + + link.add_attribute(Link.IFLA_AF_SPEC, ifla_af_spec) link.build_message(self.sequence.next(), self.pid) return self.tx_nlpacket_get_response(link) - def link_add_bridge_vlan(self, ifindex, vlanid, pvid=False, untagged=False, master=False): - self._link_bridge_vlan(RTM_SETLINK, ifindex, vlanid, pvid, untagged, master) + def link_add_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False): + """ + Add VLAN(s) to a bridge interface + """ + bridge_self = False if master else True + self.vlan_modify(RTM_SETLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged) - def link_del_bridge_vlan(self, ifindex, vlanid, pvid=False, untagged=False, master=False): - self._link_bridge_vlan(RTM_DELLINK, ifindex, vlanid, pvid, untagged, master) + def link_del_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False): + """ + Delete VLAN(s) from a bridge interface + """ + bridge_self = False if master else True + self.vlan_modify(RTM_DELLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged) def link_set_updown(self, ifname, state): """ diff --git a/nlmanager/nlpacket.py b/nlmanager/nlpacket.py index f4539aa..efc0de1 100644 --- a/nlmanager/nlpacket.py +++ b/nlmanager/nlpacket.py @@ -319,7 +319,7 @@ class AttributeString(Attribute): self.LEN = None def encode(self): - # some interface names come from JSON unicode strings + # some interface names come from JSON as unicode strings # and cannot be packed as is so we must convert them to strings if isinstance(self.value, unicode): self.value = str(self.value) @@ -503,7 +503,22 @@ class AttributeIFLA_AF_SPEC(Attribute): # # Until we cross that bridge though we will keep things nice and simple and # pack everything via a single pack() call. + sub_attr_to_add = [] + for (sub_attr_type, sub_attr_value) in self.value.iteritems(): + + if sub_attr_type == Link.IFLA_BRIDGE_FLAGS: + sub_attr_to_add.append((sub_attr_type, sub_attr_value)) + + elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO: + for (vlan_flag, vlan_id) in sub_attr_value: + sub_attr_to_add.append((sub_attr_type, (vlan_flag, vlan_id))) + + else: + self.log.debug('Add support for encoding IFLA_AF_SPEC sub-attribute type %d' % sub_attr_type) + continue + + for (sub_attr_type, sub_attr_value) in sub_attr_to_add: sub_attr_pack_layout = ['=', 'HH'] sub_attr_payload = [0, sub_attr_type] sub_attr_length_index = 0 @@ -517,10 +532,6 @@ class AttributeIFLA_AF_SPEC(Attribute): sub_attr_payload.append(sub_attr_value[0]) sub_attr_payload.append(sub_attr_value[1]) - else: - self.log.debug('Add support for encoding IFLA_AF_SPEC sub-attribute type %d' % sub_attr_type) - continue - sub_attr_length = calcsize(''.join(sub_attr_pack_layout)) sub_attr_payload[sub_attr_length_index] = sub_attr_length @@ -574,7 +585,9 @@ class AttributeIFLA_AF_SPEC(Attribute): self.value[Link.IFLA_BRIDGE_FLAGS] = unpack("=H", sub_attr_data[0:2])[0] elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO: - self.value[Link.IFLA_INFO_DATA] = tuple(unpack("=HH", sub_attr_data[0:4])) + if Link.IFLA_BRIDGE_VLAN_INFO not in self.value: + self.value[Link.IFLA_BRIDGE_VLAN_INFO] = [] + self.value[Link.IFLA_BRIDGE_VLAN_INFO].append(tuple(unpack("=HH", sub_attr_data[0:4]))) else: self.log.debug('Add support for decoding IFLA_AF_SPEC sub-attribute type %s (%d), length %d, padded to %d' % @@ -1216,14 +1229,14 @@ class NetlinkPacket(object): # Modifiers to GET query if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC): - if flags & NLM_F_ROOT: - foo.append('NLM_F_ROOT') - - if flags & NLM_F_MATCH: - foo.append('NLM_F_MATCH') - if flags & NLM_F_DUMP: foo.append('NLM_F_DUMP') + else: + if flags & NLM_F_MATCH: + foo.append('NLM_F_MATCH') + + if flags & NLM_F_ROOT: + foo.append('NLM_F_ROOT') if flags & NLM_F_ATOMIC: foo.append('NLM_F_ATOMIC') @@ -1724,7 +1737,7 @@ class Link(NetlinkPacket): IFLA_AF_SPEC : ('IFLA_AF_SPEC', AttributeIFLA_AF_SPEC), IFLA_GROUP : ('IFLA_GROUP', AttributeFourByteValue), IFLA_NET_NS_FD : ('IFLA_NET_NS_FD', AttributeGeneric), - IFLA_EXT_MASK : ('IFLA_EXT_MASK', AttributeGeneric), + IFLA_EXT_MASK : ('IFLA_EXT_MASK', AttributeFourByteValue), IFLA_PROMISCUITY : ('IFLA_PROMISCUITY', AttributeGeneric), IFLA_NUM_TX_QUEUES : ('IFLA_NUM_TX_QUEUES', AttributeGeneric), IFLA_NUM_RX_QUEUES : ('IFLA_NUM_RX_QUEUES', AttributeGeneric), @@ -2207,14 +2220,18 @@ class Link(NetlinkPacket): } # BRIDGE_VLAN_INFO flags - BRIDGE_VLAN_INFO_MASTER = 1 - BRIDGE_VLAN_INFO_PVID = 2 - BRIDGE_VLAN_INFO_UNTAGGED = 4 + BRIDGE_VLAN_INFO_MASTER = 1 << 0 + BRIDGE_VLAN_INFO_PVID = 1 << 1 + BRIDGE_VLAN_INFO_UNTAGGED = 1 << 2 + BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3 + BRIDGE_VLAN_INFO_RANGE_END = 1 << 4 bridge_vlan_to_string = { - BRIDGE_VLAN_INFO_MASTER : 'BRIDGE_VLAN_INFO_MASTER', - BRIDGE_VLAN_INFO_PVID : 'BRIDGE_VLAN_INFO_PVID', - BRIDGE_VLAN_INFO_UNTAGGED : 'BRIDGE_VLAN_INFO_UNTAGGED' + BRIDGE_VLAN_INFO_MASTER : 'BRIDGE_VLAN_INFO_MASTER', + BRIDGE_VLAN_INFO_PVID : 'BRIDGE_VLAN_INFO_PVID', + BRIDGE_VLAN_INFO_UNTAGGED : 'BRIDGE_VLAN_INFO_UNTAGGED', + BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN', + BRIDGE_VLAN_INFO_RANGE_END : 'BRIDGE_VLAN_INFO_RANGE_END' } # Bridge flags @@ -2226,6 +2243,19 @@ class Link(NetlinkPacket): BRIDGE_FLAGS_SELF : 'BRIDGE_FLAGS_SELF' } + # filters for IFLA_EXT_MASK + RTEXT_FILTER_VF = 1 << 0 + RTEXT_FILTER_BRVLAN = 1 << 1 + RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2 + RTEXT_FILTER_SKIP_STATS = 1 << 3 + + rtext_to_string = { + RTEXT_FILTER_VF : 'RTEXT_FILTER_VF', + RTEXT_FILTER_BRVLAN : 'RTEXT_FILTER_BRVLAN', + RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED', + RTEXT_FILTER_SKIP_STATS : 'RTEXT_FILTER_SKIP_STATS' + } + def __init__(self, msgtype, debug=False, logger=None): NetlinkPacket.__init__(self, msgtype, debug, logger) self.PACK = 'BxHiII'