#!/usr/bin/python # # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. # Author: Roopa Prabhu, roopa@cumulusnetworks.com # Julien Fortin, julien@cumulusnetworks.com # import os import re import glob import shlex import signal import socket import subprocess from string import maketrans from ipaddr import IPNetwork, IPv6Network try: import ifupdown2.ifupdown.statemanager as statemanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags from ifupdown2.nlmanager.nlmanager import Link, Route from ifupdown2.ifupdown.iface import * from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdown.netlink import netlink from ifupdown2.ifupdownaddons.utilsbase import utilsBase from ifupdown2.ifupdownaddons.cache import linkCache, MSTPAttrsCache except ImportError: import ifupdown.ifupdownflags as ifupdownflags import ifupdown.statemanager as statemanager from nlmanager.nlmanager import Link, Route from ifupdown.iface import * from ifupdown.utils import utils from ifupdown.netlink import netlink from ifupdownaddons.utilsbase import utilsBase from ifupdownaddons.cache import linkCache, MSTPAttrsCache class LinkUtils(utilsBase): """ This class contains helper methods to cache and manipulate interfaces through non-netlink APIs (sysfs, iproute2, brctl...) """ _CACHE_FILL_DONE = False VXLAN_UDP_PORT = 4789 ipbatchbuf = '' ipbatch = False ipbatch_pause = False bridge_utils_is_installed = os.path.exists(utils.brctl_cmd) bridge_utils_missing_warning = True DEFAULT_IP_METRIC = 1024 ADDR_METRIC_SUPPORT = None mac_translate_tab = maketrans(":.-,", " ") def __init__(self, *args, **kargs): utilsBase.__init__(self, *args, **kargs) self.supported_command = { '%s -c -json vlan show' % utils.bridge_cmd: True, 'showmcqv4src': True } self.bridge_vlan_cache = {} self.bridge_vlan_cache_fill_done = False if not ifupdownflags.flags.PERFMODE and not LinkUtils._CACHE_FILL_DONE: self._fill_cache() if LinkUtils.ADDR_METRIC_SUPPORT is None: try: cmd = [utils.ip_cmd, 'addr', 'help'] self.logger.info('executing %s addr help' % utils.ip_cmd) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() LinkUtils.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or '' self.logger.info('address metric support: %s' % ('OK' if LinkUtils.ADDR_METRIC_SUPPORT else 'KO')) except Exception: LinkUtils.ADDR_METRIC_SUPPORT = False self.logger.info('address metric support: KO') @classmethod def mac_str_to_int(cls, mac): mac_int = 0 if mac: for n in mac.translate(cls.mac_translate_tab).split(): mac_int += int(n, 16) return mac_int @classmethod def addr_metric_support(cls): return cls.ADDR_METRIC_SUPPORT @classmethod def get_default_ip_metric(cls): return cls.DEFAULT_IP_METRIC @classmethod def reset(cls): LinkUtils._CACHE_FILL_DONE = False LinkUtils.ipbatchbuf = '' LinkUtils.ipbatch = False LinkUtils.ipbatch_pause = False def _fill_cache(self): if not LinkUtils._CACHE_FILL_DONE: self._link_fill() self._addr_fill() LinkUtils._CACHE_FILL_DONE = True return True return False @staticmethod def _get_vland_id(citems, i, warn): try: sub = citems[i:] index = sub.index('id') int(sub[index + 1]) return sub[index + 1] except: if warn: raise Exception('invalid use of \'vlan\' keyword') return None def _link_fill(self, ifacename=None, refresh=False): """ fills cache with link information if ifacename argument given, fill cache for ifacename, else fill cache for all interfaces in the system """ if LinkUtils._CACHE_FILL_DONE and not refresh: return try: # if ifacename already present, return if (ifacename and not refresh and linkCache.get_attr([ifacename, 'ifflag'])): return except: pass if True: try: [linkCache.update_attrdict([ifname], linkattrs) for ifname, linkattrs in netlink.link_dump(ifacename).items()] except Exception as e: self.logger.info('%s' % str(e)) # this netlink call replaces the call to _link_fill_iproute2_cmd() # We shouldn't have netlink calls in the iproute2 module, this will # be removed in the future. We plan to release, a flexible backend # (netlink+iproute2) by default we will use netlink backend but with # a CLI arg we can switch to iproute2 backend. # Until we decide to create this "backend" switch capability, # we have to put the netlink call inside the iproute2 module. else: self._link_fill_iproute2_cmd(ifacename, refresh) self._fill_bond_info(ifacename) self._fill_bridge_info(ifacename) def _fill_bridge_info(self, ifacename): if True: # netlink brports = {} if ifacename: cache_dict = {ifacename: linkCache.links.get(ifacename, {})} else: cache_dict = linkCache.links for ifname, obj in cache_dict.items(): slave_kind = obj.get('slave_kind') if not slave_kind and slave_kind != 'bridge': continue info_slave_data = obj.get('info_slave_data') if not info_slave_data: continue ifla_master = obj.get('master') if not ifla_master: raise Exception('No master associated with bridge port %s' % ifname) for nl_attr in [ Link.IFLA_BRPORT_STATE, Link.IFLA_BRPORT_COST, Link.IFLA_BRPORT_PRIORITY, ]: if nl_attr not in info_slave_data and LinkUtils.bridge_utils_is_installed: self._fill_bridge_info_brctl() return brport_attrs = { 'pathcost': str(info_slave_data.get(Link.IFLA_BRPORT_COST, 0)), 'fdelay': format(float(info_slave_data.get(Link.IFLA_BRPORT_FORWARD_DELAY_TIMER, 0) / 100), '.2f'), 'portmcrouter': str(info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER, 0)), 'portmcfl': str(info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE, 0)), 'portprio': str(info_slave_data.get(Link.IFLA_BRPORT_PRIORITY, 0)), 'unicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_UNICAST_FLOOD, 0)), 'multicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_MCAST_FLOOD, 0)), 'learning': str(info_slave_data.get(Link.IFLA_BRPORT_LEARNING, 0)), 'arp-nd-suppress': str(info_slave_data.get(Link.IFLA_BRPORT_ARP_SUPPRESS, 0)) } if ifla_master in brports: brports[ifla_master][ifname] = brport_attrs else: brports[ifla_master] = {ifname: brport_attrs} linkCache.update_attrdict([ifla_master, 'linkinfo', 'ports'], brports[ifla_master]) else: if LinkUtils.bridge_utils_is_installed: self._fill_bridge_info_brctl() def _fill_bridge_info_brctl(self): brctlout = utils.exec_command('%s show' % utils.brctl_cmd) if not brctlout: return for bline in brctlout.splitlines()[1:]: bitems = bline.split() if len(bitems) < 2: continue try: linkCache.update_attrdict([bitems[0], 'linkinfo'], {'stp': bitems[2]}) except KeyError: linkCache.update_attrdict([bitems[0]], {'linkinfo': {'stp': bitems[2]}}) self._bridge_attrs_fill(bitems[0]) def _bridge_attrs_fill(self, bridgename): battrs = {} bports = {} try: # Get all bridge attributes # battrs['pathcost'] = broutlines[3].split('path cost')[1].strip() try: battrs['maxage'] = self.read_file_oneline( '/sys/class/net/%s/bridge/max_age' % bridgename) except: pass try: battrs['hello'] = self.read_file_oneline( '/sys/class/net/%s/bridge/hello_time' % bridgename) except: pass try: battrs['fd'] = self.read_file_oneline( '/sys/class/net/%s/bridge/forward_delay' % bridgename) except: pass try: battrs['ageing'] = self.read_file_oneline( '/sys/class/net/%s/bridge/ageing_time' % bridgename) except: pass try: battrs['mcrouter'] = self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_router' % bridgename) except: pass try: battrs['bridgeprio'] = self.read_file_oneline( '/sys/class/net/%s/bridge/priority' % bridgename) except: pass try: battrs['vlan-protocol'] = VlanProtocols.ID_TO_ETHERTYPES[ self.read_file_oneline( '/sys/class/net/%s/bridge/vlan_protocol' % bridgename)] except: pass try: battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename)) except: pass # XXX: comment this out until mc attributes become available # with brctl again # battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip() # battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip() except Exception, e: self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e))) pass linkCache.update_attrdict([bridgename, 'linkinfo'], battrs) names = [os.path.basename(x) for x in glob.glob("/sys/class/net/%s/brif/*" % bridgename)] for pname in names: bportattrs = {} try: bportattrs['pathcost'] = self.read_file_oneline( '/sys/class/net/%s/brport/path_cost' % pname) bportattrs['fdelay'] = self.read_file_oneline( '/sys/class/net/%s/brport/forward_delay_timer' % pname) bportattrs['portmcrouter'] = self.read_file_oneline( '/sys/class/net/%s/brport/multicast_router' % pname) bportattrs['portmcfl'] = self.read_file_oneline( '/sys/class/net/%s/brport/multicast_fast_leave' % pname) bportattrs['portprio'] = self.read_file_oneline( '/sys/class/net/%s/brport/priority' % pname) bportattrs['unicast-flood'] = self.read_file_oneline( '/sys/class/net/%s/brport/unicast_flood' % pname) bportattrs['multicast-flood'] = self.read_file_oneline( '/sys/class/net/%s/brport/multicast_flood' % pname) bportattrs['learning'] = self.read_file_oneline( '/sys/class/net/%s/brport/learning' % pname) bportattrs['arp-nd-suppress'] = self.read_file_oneline( '/sys/class/net/%s/brport/neigh_suppress' % pname) #bportattrs['mcrouters'] = self.read_file_oneline( # '/sys/class/net/%s/brport/multicast_router' % pname) #bportattrs['mc fast leave'] = self.read_file_oneline( # '/sys/class/net/%s/brport/multicast_fast_leave' % pname) except Exception, e: self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e))) bports[pname] = bportattrs linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports) _bridge_sysfs_mcattrs = { 'mclmc': 'multicast_last_member_count', 'mcrouter': 'multicast_router', 'mcsnoop': 'multicast_snooping', 'mcsqc': 'multicast_startup_query_count', 'mcqifaddr': 'multicast_query_use_ifaddr', 'mcquerier': 'multicast_querier', 'hashel': 'hash_elasticity', 'hashmax': 'hash_max', 'mclmi': 'multicast_last_member_interval', 'mcmi': 'multicast_membership_interval', 'mcqpi': 'multicast_querier_interval', 'mcqi': 'multicast_query_interval', 'mcqri': 'multicast_query_response_interval', 'mcsqi': 'multicast_startup_query_interval', 'igmp-version': 'multicast_igmp_version', 'mld-version': 'multicast_mld_version', 'vlan-stats': 'vlan_stats_enabled', 'mcstats': 'multicast_stats_enabled', } def _bridge_get_mcattrs_from_sysfs(self, bridgename): mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi'] mcattrs = {} for m, s in self._bridge_sysfs_mcattrs.items(): n = self.read_file_oneline('/sys/class/net/%s/bridge/%s' % (bridgename, s)) if m in mcattrsdivby100: try: v = int(n) / 100 mcattrs[m] = str(v) except Exception, e: self.logger.warn('error getting mc attr %s (%s)' % (m, str(e))) pass else: mcattrs[m] = n return mcattrs def _fill_bond_info(self, ifacename): bonding_masters = self.read_file_oneline('/sys/class/net/bonding_masters') if not bonding_masters: return bond_masters_list = bonding_masters.split() if ifacename: if ifacename in bond_masters_list: bond_masters_list = [ifacename] else: # we want to refresh this interface only if it's a bond master return for bondname in bond_masters_list: try: if bondname not in linkCache.links: linkCache.set_attr([bondname], {'linkinfo': {}}) linkCache.set_attr([bondname, 'linkinfo', 'slaves'], self.read_file_oneline('/sys/class/net/%s/bonding/slaves' % bondname).split()) try: # if some attribute are missing we try to get the bond attributes via sysfs bond_linkinfo = linkCache.links[bondname]['linkinfo'] for attr in [Link.IFLA_BOND_MODE, Link.IFLA_BOND_XMIT_HASH_POLICY, Link.IFLA_BOND_MIN_LINKS]: if attr not in bond_linkinfo: self._fill_bond_info_sysfs(bondname) # after we fill in the cache we can continue to the next bond break except: self._fill_bond_info_sysfs(bondname) except Exception as e: self.logger.debug('LinkUtils: bond cache error: %s' % str(e)) def _fill_bond_info_sysfs(self, bondname): try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS], self.read_file_oneline( '/sys/class/net/%s/bonding/min_links' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MODE], self.read_file_oneline('/sys/class/net/%s/bonding/mode' % bondname).split()[0]) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY], self.read_file_oneline( '/sys/class/net/%s/bonding/xmit_hash_policy' % bondname).split()[0]) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE], self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate' % bondname).split()[1]) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO], self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_sys_prio' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM], self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_system' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS], self.read_file_oneline('/sys/class/net/%s/bonding/lacp_bypass' % bondname).split()[1]) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY], self.read_file_oneline('/sys/class/net/%s/bonding/updelay' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY], self.read_file_oneline('/sys/class/net/%s/bonding/downdelay' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER], self.read_file_oneline('/sys/class/net/%s/bonding/use_carrier' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON], self.read_file_oneline('/sys/class/net/%s/bonding/miimon' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF], self.read_file_oneline('/sys/class/net/%s/bonding/num_unsol_na' % bondname)) except Exception as e: self.logger.debug(str(e)) try: linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF], self.read_file_oneline('/sys/class/net/%s/bonding/num_grat_arp' % bondname)) except Exception as e: self.logger.debug(str(e)) def _link_fill_iproute2_cmd(self, ifacename=None, refresh=False): warn = True linkout = {} if LinkUtils._CACHE_FILL_DONE and not refresh: return try: # if ifacename already present, return if (ifacename and not refresh and linkCache.get_attr([ifacename, 'ifflag'])): return except: pass cmdout = self.link_show(ifacename=ifacename) if not cmdout: return for c in cmdout.splitlines(): citems = c.split() ifnamenlink = citems[1].split('@') if len(ifnamenlink) > 1: ifname = ifnamenlink[0] iflink = ifnamenlink[1].strip(':') else: ifname = ifnamenlink[0].strip(':') iflink = None linkattrs = dict() linkattrs['link'] = iflink linkattrs['ifindex'] = citems[0].strip(':') flags = citems[2].strip('<>').split(',') linkattrs['flags'] = flags linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN' for i in range(0, len(citems)): try: if citems[i] == 'mtu': linkattrs['mtu'] = citems[i + 1] elif citems[i] == 'state': linkattrs['state'] = citems[i + 1] elif citems[i] == 'link/ether': linkattrs['hwaddress'] = citems[i + 1] elif citems[i] in ['link/gre', 'link/ipip', 'link/sit', 'link/gre6', 'link/tunnel6', 'gretap']: linkattrs['kind'] = 'tunnel' tunattrs = {'mode': citems[i].split('/')[-1], 'endpoint' : None, 'local' : None, 'ttl' : None, 'physdev' : None} for j in range(i, len(citems)): if citems[j] == 'local': tunattrs['local'] = citems[j + 1] elif citems[j] == 'remote': tunattrs['endpoint'] = citems[j + 1] elif citems[j] == 'ttl': tunattrs['ttl'] = citems[j + 1] elif citems[j] == 'dev': tunattrs['physdev'] = citems[j + 1] elif citems[j] in ['vti', 'vti6', 'ip6gre', 'ipip6', 'ip6ip6']: tunattrs['mode'] = citems[j] linkattrs['linkinfo'] = tunattrs break elif citems[i] == 'link/ppp': linkattrs['kind'] = 'ppp' elif citems[i] == 'vlan': vlanid = self._get_vland_id(citems, i, warn) if vlanid: linkattrs['linkinfo'] = {'vlanid': vlanid} linkattrs['kind'] = 'vlan' elif citems[i] == 'dummy': linkattrs['kind'] = 'dummy' elif citems[i] == 'vxlan' and citems[i + 1] == 'id': linkattrs['kind'] = 'vxlan' vattrs = {'vxlanid': citems[i + 2], 'svcnode': None, 'remote': [], 'ageing': citems[i + 2], 'learning': 'on'} for j in range(i + 2, len(citems)): if citems[j] == 'local': vattrs['local'] = citems[j + 1] elif citems[j] == 'remote': vattrs['svcnode'] = citems[j + 1] elif citems[j] == 'ageing': vattrs['ageing'] = citems[j + 1] elif citems[j] == 'nolearning': vattrs['learning'] = 'off' elif citems[j] == 'dev': vattrs['physdev'] = citems[j + 1] linkattrs['linkinfo'] = vattrs break elif citems[i] == 'vrf' and citems[i + 1] == 'table': vattrs = {'table': citems[i + 2]} linkattrs['linkinfo'] = vattrs linkattrs['kind'] = 'vrf' linkCache.vrfs[ifname] = vattrs break elif citems[i] == 'veth': linkattrs['kind'] = 'veth' elif citems[i] == 'vrf_slave': linkattrs['slave_kind'] = 'vrf_slave' break elif citems[i] == 'macvlan' and citems[i + 1] == 'mode': linkattrs['kind'] = 'macvlan' elif citems[i] == 'xfrm': linkattrs['kind'] = 'xfrm' except Exception as e: if warn: self.logger.debug('%s: parsing error: id, mtu, state, ' 'link/ether, vlan, dummy, vxlan, local, ' 'remote, ageing, nolearning, vrf, table, ' 'vrf_slave are reserved keywords: %s' % (ifname, str(e))) warn = False # linkattrs['alias'] = self.read_file_oneline( # '/sys/class/net/%s/ifalias' %ifname) linkout[ifname] = linkattrs [linkCache.update_attrdict([ifname], linkattrs) for ifname, linkattrs in linkout.items()] @staticmethod def _addr_filter(ifname, addr, scope=None): default_addrs = ['127.0.0.1/8', '::1/128', '0.0.0.0'] if ifname == 'lo' and addr in default_addrs: return True if scope and scope == 'link': return True return False def _addr_fill(self, ifacename=None, refresh=False): """ fills cache with address information if ifacename argument given, fill cache for ifacename, else fill cache for all interfaces in the system """ if LinkUtils._CACHE_FILL_DONE and not refresh: return try: # Check if ifacename is already full, in which case, return if ifacename and not refresh: linkCache.get_attr([ifacename, 'addrs']) return except: pass if True: try: [linkCache.update_attrdict([ifname], linkattrs) for ifname, linkattrs in netlink.addr_dump(ifname=ifacename).items()] except Exception as e: self.logger.info(str(e)) # this netlink call replaces the call to _addr_fill_iproute2_cmd() # We shouldn't have netlink calls in the iproute2 module, this will # be removed in the future. We plan to release, a flexible backend # (netlink+iproute2) by default we will use netlink backend but with # a CLI arg we can switch to iproute2 backend. # Until we decide to create this "backend" switch capability, # we have to put the netlink call inside the iproute2 module. else: self._addr_fill_iproute2_cmd(ifacename, refresh) def _addr_fill_iproute2_cmd(self, ifacename=None, refresh=False): """ fills cache with address information if ifacename argument given, fill cache for ifacename, else fill cache for all interfaces in the system """ linkout = {} if LinkUtils._CACHE_FILL_DONE and not refresh: return try: # Check if ifacename is already full, in which case, return if ifacename and not refresh: linkCache.get_attr([ifacename, 'addrs']) return except: pass cmdout = self.addr_show(ifacename=ifacename) if not cmdout: return for c in cmdout.splitlines(): citems = c.split() ifnamenlink = citems[1].split('@') if len(ifnamenlink) > 1: ifname = ifnamenlink[0] else: ifname = ifnamenlink[0].strip(':') if not linkout.get(ifname): linkattrs = dict() linkattrs['addrs'] = OrderedDict({}) try: linkout[ifname].update(linkattrs) except KeyError: linkout[ifname] = linkattrs if citems[2] == 'inet': if self._addr_filter(ifname, citems[3], scope=citems[5]): continue addrattrs = dict() addrattrs['scope'] = citems[5] addrattrs['type'] = 'inet' linkout[ifname]['addrs'][citems[3]] = addrattrs elif citems[2] == 'inet6': if self._addr_filter(ifname, citems[3], scope=citems[5]): continue if citems[5] == 'link': continue # skip 'link' addresses addrattrs = dict() addrattrs['scope'] = citems[5] addrattrs['type'] = 'inet6' linkout[ifname]['addrs'][citems[3]] = addrattrs [linkCache.update_attrdict([ifname], linkattrs) for ifname, linkattrs in linkout.items()] def del_cache_entry(self, ifname): try: del linkCache.links[ifname] except: pass def cache_get(self, t, attrlist, refresh=False): return self._cache_get(t, attrlist, refresh) def _cache_get(self, t, attrlist, refresh=False): try: if ifupdownflags.flags.DRYRUN: return False if ifupdownflags.flags.CACHE: if self._fill_cache(): # if we filled the cache, return new data return linkCache.get_attr(attrlist) if not refresh: return linkCache.get_attr(attrlist) if t == 'link': self._link_fill(attrlist[0], refresh) elif t == 'addr': self._addr_fill(attrlist[0], refresh) else: self._link_fill(attrlist[0], refresh) self._addr_fill(attrlist[0], refresh) return linkCache.get_attr(attrlist) except Exception, e: self.logger.debug('_cache_get(%s) : [%s]' % (str(attrlist), str(e))) return None def cache_check(self, attrlist, value, refresh=False): return self._cache_check('link', attrlist, value, refresh=refresh) def _cache_check(self, t, attrlist, value, refresh=False): try: return self._cache_get(t, attrlist, refresh) == value except Exception, e: self.logger.debug('_cache_check(%s) : [%s]' % (str(attrlist), str(e))) return False def cache_update(self, attrlist, value): return self._cache_update(attrlist, value) @staticmethod def _cache_update(attrlist, value): if ifupdownflags.flags.DRYRUN: return try: if attrlist[-1] == 'slaves': linkCache.append_to_attrlist(attrlist, value) return linkCache.set_attr(attrlist, value) except: pass @staticmethod def _cache_delete(attrlist, value=None): if ifupdownflags.flags.DRYRUN: return try: if value: linkCache.remove_from_attrlist(attrlist, value) else: linkCache.del_attr(attrlist) except: pass @staticmethod def _cache_invalidate(): linkCache.invalidate() LinkUtils._CACHE_FILL_DONE = False @staticmethod def batch_start(): LinkUtils.ipbatcbuf = '' LinkUtils.ipbatch = True LinkUtils.ipbatch_pause = False @staticmethod def add_to_batch(cmd): LinkUtils.ipbatchbuf += cmd + '\n' @staticmethod def batch_pause(): LinkUtils.ipbatch_pause = True @staticmethod def batch_resume(): LinkUtils.ipbatch_pause = False def batch_commit(self): if not LinkUtils.ipbatchbuf: LinkUtils.ipbatchbuf = '' LinkUtils.ipbatch = False LinkUtils.ipbatch_pause = False return try: utils.exec_command('%s -force -batch -' % utils.ip_cmd, stdin=self.ipbatchbuf) except: raise finally: LinkUtils.ipbatchbuf = '' LinkUtils.ipbatch = False LinkUtils.ipbatch_pause = False def bridge_batch_commit(self): if not LinkUtils.ipbatchbuf: LinkUtils.ipbatchbuf = '' LinkUtils.ipbatch = False LinkUtils.ipbatch_pause = False return try: utils.exec_command('%s -force -batch -' % utils.bridge_cmd, stdin=self.ipbatchbuf) except: raise finally: LinkUtils.ipbatchbuf = '' LinkUtils.ipbatch = False LinkUtils.ipbatch_pause = False def addr_show(self, ifacename=None): if ifacename: if not self.link_exists(ifacename): return return utils.exec_commandl([utils.ip_cmd, '-o', 'addr', 'show', 'dev', ifacename]) else: return utils.exec_commandl([utils.ip_cmd, '-o', 'addr', 'show']) @staticmethod def link_show(ifacename=None): if ifacename: return utils.exec_commandl([utils.ip_cmd, '-o', '-d', 'link', 'show', 'dev', ifacename]) else: return utils.exec_commandl([utils.ip_cmd, '-o', '-d', 'link', 'show']) def addr_add(self, ifacename, address, broadcast=None, peer=None, scope=None, preferred_lifetime=None, metric=None): if not address: return cmd = 'addr add %s' % address if broadcast: cmd += ' broadcast %s' % broadcast if peer: cmd += ' peer %s' % peer if scope: cmd += ' scope %s' % scope if preferred_lifetime: cmd += ' preferred_lft %s' % preferred_lifetime cmd += ' dev %s' % ifacename if metric: cmd += ' metric %s' % metric if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) self._cache_update([ifacename, 'addrs', address], {}) def addr_del(self, ifacename, address, broadcast=None, peer=None, scope=None): """ Delete ipv4 address """ if not address: return if not self._cache_get('addr', [ifacename, 'addrs', address]): return cmd = 'addr del %s' % address if broadcast: cmd += ' broadcast %s' % broadcast if peer: cmd += ' peer %s' % peer if scope: cmd += ' scope %s' % scope cmd += ' dev %s' % ifacename utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) self._cache_delete([ifacename, 'addrs', address]) def addr_flush(self, ifacename): cmd = 'addr flush dev %s' % ifacename if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) self._cache_delete([ifacename, 'addrs']) def del_addr_all(self, ifacename, skip_addrs=[]): if not skip_addrs: skip_addrs = [] runningaddrsdict = self.get_running_addrs(ifname=ifacename) try: # XXX: ignore errors. Fix this to delete secondary addresses # first [self.addr_del(ifacename, a) for a in set(runningaddrsdict.keys()).difference(skip_addrs)] except: # ignore errors pass def addr_get(self, ifacename, details=True, refresh=False): addrs = self._cache_get('addr', [ifacename, 'addrs'], refresh=refresh) if not addrs: return None if details: return addrs return addrs.keys() def get_running_addrs(self, ifaceobj=None, ifname=None, details=True, addr_virtual_ifaceobj=None): """ We now support addr with link scope. Since the kernel may add it's own link address to some interfaces we need to filter them out and make sure we only deal with the addresses set by ifupdown2. To do so we look at the previous configuration made by ifupdown2 (with the help of the statemanager) together with the addresses specified by the user in /etc/network/interfaces, these addresses are then compared to the running state of the intf (ip addr show) made via a netlink addr dump. For each configured addresses of scope link, we check if it was previously configured by ifupdown2 to create a final set of the addresses watched by ifupdown2 """ if not ifaceobj and not ifname: return None config_addrs = set() if ifaceobj: interface_name = ifaceobj.name else: interface_name = ifname if addr_virtual_ifaceobj: for attr_name in ["address-virtual", "vrrp"]: for virtual in addr_virtual_ifaceobj.get_attr_value(attr_name) or []: for ip in virtual.split(): try: IPNetwork(ip) config_addrs.add(ip) except: pass saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(addr_virtual_ifaceobj.name) for saved_ifaceobj in saved_ifaceobjs or []: for virtual in saved_ifaceobj.get_attr_value(attr_name) or []: for ip in virtual.split(): try: IPNetwork(ip) config_addrs.add(ip) except: pass else: if ifaceobj: for addr in ifaceobj.get_attr_value('address') or []: config_addrs.add(addr) saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(interface_name) for saved_ifaceobj in saved_ifaceobjs or []: for addr in saved_ifaceobj.get_attr_value('address') or []: config_addrs.add(addr) running_addrs = OrderedDict() cached_addrs = self.addr_get(interface_name) if cached_addrs: for addr, addr_details in cached_addrs.items(): try: scope = int(addr_details['scope']) except Exception: try: d = {} addr_obj = IPNetwork(addr) if isinstance(addr_obj, IPv6Network): d['family'] = 'inet6' else: d['family'] = 'inet' running_addrs[addr] = d except: running_addrs[addr] = {} continue if (scope & Route.RT_SCOPE_LINK and addr in config_addrs) or not scope & Route.RT_SCOPE_LINK: running_addrs[addr] = addr_details else: return None if details: return running_addrs return running_addrs.keys() @staticmethod def compare_user_config_vs_running_state(running_addrs, user_addrs): ip4 = [] ip6 = [] for ip in user_addrs or []: obj = IPNetwork(ip) if type(obj) == IPv6Network: ip6.append(str(obj)) else: ip4.append(str(obj)) running_ipobj = [] for ip in running_addrs or []: running_ipobj.append(str(IPNetwork(ip))) return running_ipobj == (ip4 + ip6) def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None): # purges address if purge_existing: # if perfmode is not set and also if iface has no sibling # objects, purge addresses that are not present in the new # config runningaddrs = self.get_running_addrs( ifname=ifacename, details=False, addr_virtual_ifaceobj=ifaceobj ) addrs = utils.get_normalized_ip_addr(ifacename, addrs) if self.compare_user_config_vs_running_state(runningaddrs, addrs): return try: # if primary address is not same, there is no need to keep any. # reset all addresses if (addrs and runningaddrs and (addrs[0] != runningaddrs[0])): self.del_addr_all(ifacename) else: self.del_addr_all(ifacename, addrs) except Exception, e: self.logger.warning('%s: %s' % (ifacename, str(e))) for a in addrs: try: self.addr_add(ifacename, a, metric=metric) except Exception, e: self.logger.error(str(e)) def _link_set_ifflag(self, ifacename, value): # Dont look at the cache, the cache may have stale value # because link status can be changed by external # entity (One such entity is ifupdown main program) cmd = 'link set dev %s %s' % (ifacename, value.lower()) if LinkUtils.ipbatch: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) def link_up(self, ifacename): self._link_set_ifflag(ifacename, 'UP') def link_down(self, ifacename): self._link_set_ifflag(ifacename, 'DOWN') def link_set(self, ifacename, key, value=None, force=False, t=None, state=None): if not force: if (key not in ['master', 'nomaster'] and self._cache_check('link', [ifacename, key], value)): return cmd = 'link set dev %s' % ifacename if t: cmd += ' type %s' % t cmd += ' %s' % key if value: cmd += ' %s' % value if state: cmd += ' %s' % state if LinkUtils.ipbatch: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) if key not in ['master', 'nomaster']: self._cache_update([ifacename, key], value) def link_set_hwaddress(self, ifacename, hwaddress, force=False, keep_down=False): if not force: link_hwaddress = self.link_get_hwaddress(ifacename) if self.mac_str_to_int(link_hwaddress) == self.mac_str_to_int(hwaddress): return False self.link_down(ifacename) cmd = 'link set dev %s address %s' % (ifacename, hwaddress) if LinkUtils.ipbatch: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) if not keep_down: self.link_up(ifacename) self._cache_update([ifacename, 'hwaddress'], hwaddress) return True def link_set_mtu(self, ifacename, mtu): if ifupdownflags.flags.DRYRUN: return True if not mtu or not ifacename: return self.write_file('/sys/class/net/%s/mtu' % ifacename, mtu) self._cache_update([ifacename, 'mtu'], mtu) def link_set_alias(self, ifacename, alias): self.write_file('/sys/class/net/%s/ifalias' % ifacename, '\n' if not alias else alias) def link_get_alias(self, ifacename): return self.read_file_oneline('/sys/class/net/%s/ifalias' % ifacename) def link_isloopback(self, ifacename): flags = self._cache_get('link', [ifacename, 'flags']) if not flags: return if 'LOOPBACK' in flags: return True return False def link_get_status(self, ifacename): return self._cache_get('link', [ifacename, 'ifflag'], refresh=True) @staticmethod def route_add_gateway(ifacename, gateway, vrf=None, metric=None, onlink=True): if not gateway: return if not vrf: cmd = '%s route add default via %s proto kernel' % (utils.ip_cmd, gateway) else: cmd = ('%s route add table %s default via %s proto kernel' % (utils.ip_cmd, vrf, gateway)) # Add metric if metric: cmd += ' metric %s' % metric cmd += ' dev %s' % ifacename if onlink: cmd += " onlink" utils.exec_command(cmd) @staticmethod def route_del_gateway(ifacename, gateway, vrf=None, metric=None): # delete default gw if not gateway: return if not vrf: cmd = ('%s route del default via %s proto kernel' % (utils.ip_cmd, gateway)) else: cmd = ('%s route del table %s default via %s proto kernel' % (utils.ip_cmd, vrf, gateway)) if metric: cmd += ' metric %s' % metric cmd += ' dev %s' % ifacename utils.exec_command(cmd) @staticmethod def _get_vrf_id(ifacename): try: return linkCache.vrfs[ifacename]['table'] except KeyError: dump = netlink.link_dump(ifacename) [linkCache.update_attrdict([ifname], linkattrs) for ifname, linkattrs in dump.items()] if dump and dump.get(ifacename, {}).get('kind') == 'vrf': vrf_table = dump.get(ifacename, {}).get('linkinfo', {}).get('table') linkCache.vrfs[ifacename] = {'table': vrf_table} return vrf_table return None def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips): vrf_table = None if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE: try: for upper_iface in ifaceobj.upperifaces: vrf_table = self._get_vrf_id(upper_iface) if vrf_table: break except: pass ip_route_del = [] for ip in ips: ip_network_obj = IPNetwork(ip) if type(ip_network_obj) == IPv6Network: route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen) if vrf_table: if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: LinkUtils.add_to_batch('route del %s table %s dev %s' % (route_prefix, vrf_table, macvlan_ifacename)) else: utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'table', vrf_table, 'dev', macvlan_ifacename]) else: if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: LinkUtils.add_to_batch('route del %s dev %s' % (route_prefix, macvlan_ifacename)) else: utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'dev', macvlan_ifacename]) ip_route_del.append((route_prefix, vrf_table)) for ip, vrf_table in ip_route_del: if vrf_table: if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: LinkUtils.add_to_batch('route add %s table %s dev %s proto kernel metric 9999' % (ip, vrf_table, macvlan_ifacename)) else: utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'table', vrf_table, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999']) else: if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: LinkUtils.add_to_batch('route add %s dev %s proto kernel metric 9999' % (ip, macvlan_ifacename)) else: utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999']) def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid): if self.link_exists(vlan_device_name): return utils.exec_command('%s link add link %s name %s type vlan id %d' % (utils.ip_cmd, vlan_raw_device, vlan_device_name, vlanid)) self._cache_update([vlan_device_name], {}) def link_create_vlan_from_name(self, vlan_device_name): v = vlan_device_name.split('.') if len(v) != 2: self.logger.warn('invalid vlan device name %s' % vlan_device_name) return self.link_create_vlan(vlan_device_name, v[0], v[1]) def link_create_macvlan(self, name, linkdev, mode='private'): if self.link_exists(name): return cmd = ('link add link %s' % linkdev + ' name %s' % name + ' type macvlan mode %s' % mode) if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) self._cache_update([name], {}) def get_vxlan_peers(self, dev, svcnodeip): cmd = '%s fdb show brport %s' % (utils.bridge_cmd, dev) cur_peers = [] try: ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False) utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT) output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout) ps.wait() utils.disable_subprocess_signal_forwarding(signal.SIGINT) try: ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+') for l in output.split('\n'): m = ppat.search(l) if m and m.group(1) != svcnodeip: cur_peers.append(m.group(1)) except: self.logger.warn('error parsing ip link output') except subprocess.CalledProcessError as e: if e.returncode != 1: self.logger.error(str(e)) finally: utils.disable_subprocess_signal_forwarding(signal.SIGINT) return cur_peers def tunnel_create(self, tunnelname, mode, attrs={}): """ generic link_create function """ if self.link_exists(tunnelname): return cmd = '' if '6' in mode: cmd = ' -6' if mode in ['gretap']: cmd += ' link add %s type %s' % (tunnelname, mode) else: cmd += ' tunnel add %s mode %s' % (tunnelname, mode) if attrs: for k, v in attrs.iteritems(): cmd += ' %s' % k if v: cmd += ' %s' % v if self.ipbatch and not self.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('ip %s' % cmd) self._cache_update([tunnelname], {}) def tunnel_change(self, tunnelname, attrs={}): """ tunnel change function """ if not self.link_exists(tunnelname): return cmd = 'tunnel change' cmd += ' %s' %(tunnelname) if attrs: for k, v in attrs.iteritems(): cmd += ' %s' %k if v: cmd += ' %s' %v if self.ipbatch and not self.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('ip %s' % cmd) def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None, remoteips=None, learning='on', ageing=None, anycastip=None, ttl=None): if svcnodeip and remoteips: raise Exception("svcnodeip and remoteip is mutually exclusive") args = '' if svcnodeip: args += ' remote %s' % svcnodeip if ageing: args += ' ageing %s' % ageing if learning == 'off': args += ' nolearning' if ttl is not None: args += ' ttl %s' % ttl if self.link_exists(name): cmd = 'link set dev %s type vxlan dstport %d' % (name, LinkUtils.VXLAN_UDP_PORT) vxlanattrs = self.get_vxlandev_attrs(name) # on ifreload do not overwrite anycast_ip to individual ip if clagd # has modified if vxlanattrs: running_localtunnelip = vxlanattrs.get('local') if anycastip and running_localtunnelip and anycastip == running_localtunnelip: localtunnelip = running_localtunnelip running_svcnode = vxlanattrs.get('svcnode') if running_svcnode and not svcnodeip: args += ' noremote' else: cmd = 'link add dev %s type vxlan id %s dstport %d' % (name, vxlanid, LinkUtils.VXLAN_UDP_PORT) if localtunnelip: args += ' local %s' % localtunnelip cmd += args if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) # XXX: update linkinfo correctly #self._cache_update([name], {}) @staticmethod def link_exists(ifacename): if ifupdownflags.flags.DRYRUN: return True return os.path.exists('/sys/class/net/%s' % ifacename) @staticmethod def link_exists_nodryrun(ifname): return os.path.exists('/sys/class/net/%s' % ifname) 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 return False @staticmethod def link_add_macvlan(ifname, macvlan_ifacename, mode): utils.exec_commandl(['ip', 'link', 'add', 'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', mode]) @staticmethod def link_add_xfrm(ifname, xfrm_name, xfrm_id): utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id]) @staticmethod def route_add(route): utils.exec_command('%s route add %s' % (utils.ip_cmd, route)) @staticmethod def route6_add(route): utils.exec_command('%s -6 route add %s' % (utils.ip_cmd, route)) def get_vlandev_attrs(self, ifacename): return (self._cache_get('link', [ifacename, 'link']), self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']), self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol'])) def get_vlan_protocol(self, ifacename): return self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol']) def get_vxlandev_attrs(self, ifacename): return self._cache_get('link', [ifacename, 'linkinfo']) def get_vxlandev_learning(self, ifacename): return self._cache_get('link', [ifacename, 'linkinfo', Link.IFLA_VXLAN_LEARNING]) def set_vxlandev_learning(self, ifacename, learn): if learn == 'on': utils.exec_command('%s link set dev %s type vxlan learning' % (utils.ip_cmd, ifacename)) self._cache_update([ifacename, 'linkinfo', 'learning'], 'on') else: utils.exec_command('%s link set dev %s type vxlan nolearning' % (utils.ip_cmd, ifacename)) self._cache_update([ifacename, 'linkinfo', 'learning'], 'off') def link_get_linkinfo_attrs(self, ifacename): return self._cache_get('link', [ifacename, 'linkinfo']) def link_get_mtu(self, ifacename, refresh=False): return self._cache_get('link', [ifacename, 'mtu'], refresh=refresh) def link_get_mtu_sysfs(self, ifacename): return self.read_file_oneline('/sys/class/net/%s/mtu' % ifacename) def link_get_kind(self, ifacename): return self._cache_get('link', [ifacename, 'kind']) def link_get_slave_kind(self, ifacename): return self._cache_get('link', [ifacename, 'slave_kind']) def link_get_hwaddress(self, ifacename): address = self._cache_get('link', [ifacename, 'hwaddress']) # newly created logical interface addresses dont end up in the cache # read hwaddress from sysfs file for these interfaces if not address: address = self.read_file_oneline('/sys/class/net/%s/address' % ifacename) return address def link_create(self, ifacename, t, attrs={}): """ generic link_create function """ if self.link_exists(ifacename): return cmd = 'link add' cmd += ' name %s type %s' % (ifacename, t) if attrs: for k, v in attrs.iteritems(): cmd += ' %s' % k if v: cmd += ' %s' % v if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) self._cache_update([ifacename], {}) def link_delete(self, ifacename): if not self.link_exists(ifacename): return cmd = 'link del %s' % ifacename if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch(cmd) else: utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) self._cache_invalidate() def link_get_master(self, ifacename): sysfs_master_path = '/sys/class/net/%s/master' % ifacename if os.path.exists(sysfs_master_path): link_path = os.readlink(sysfs_master_path) if link_path: return os.path.basename(link_path) else: return None else: return self._cache_get('link', [ifacename, 'master']) def get_brport_peer_link(self, bridgename): try: return self._cache_get('link', [bridgename, 'info_slave_data', Link.IFLA_BRPORT_PEER_LINK]) except: return None @staticmethod def bridge_port_vids_add(bridgeportname, vids): [utils.exec_command('%s vlan add vid %s dev %s' % (utils.bridge_cmd, v, bridgeportname)) for v in vids] @staticmethod def bridge_port_vids_del(bridgeportname, vids): if not vids: return [utils.exec_command('%s vlan del vid %s dev %s' % (utils.bridge_cmd, v, bridgeportname)) for v in vids] @staticmethod def bridge_port_vids_flush(bridgeportname, vid): utils.exec_command('%s vlan del vid %s dev %s' % (utils.bridge_cmd, vid, bridgeportname)) @staticmethod def bridge_port_vids_get(bridgeportname): bridgeout = utils.exec_command('%s vlan show dev %s' % (utils.bridge_cmd, bridgeportname)) if not bridgeout: return [] brvlanlines = bridgeout.readlines()[2:] vids = [l.strip() for l in brvlanlines] return [v for v in vids if v] @staticmethod def bridge_port_vids_get_all(): brvlaninfo = {} bridgeout = utils.exec_command('%s -c vlan show' % utils.bridge_cmd) if not bridgeout: return brvlaninfo brvlanlines = bridgeout.splitlines() brportname = None for l in brvlanlines[1:]: if l and not l.startswith(' ') and not l.startswith('\t'): attrs = l.split() brportname = attrs[0].strip() brvlaninfo[brportname] = {'pvid': None, 'vlan': []} l = ' '.join(attrs[1:]) if not brportname or not l: continue l = l.strip() if 'PVID' in l: brvlaninfo[brportname]['pvid'] = l.split()[0] elif 'Egress Untagged' not in l: brvlaninfo[brportname]['vlan'].append(l) return brvlaninfo def bridge_port_vids_get_all_json(self): if not self.supported_command['%s -c -json vlan show' % utils.bridge_cmd]: return {} brvlaninfo = {} try: bridgeout = utils.exec_command('%s -c -json vlan show' % utils.bridge_cmd) except: self.supported_command['%s -c -json vlan show' % utils.bridge_cmd] = False self.logger.info('%s -c -json vlan show: skipping unsupported command' % utils.bridge_cmd) try: return self.get_bridge_vlan_nojson() except Exception as e: self.logger.info('bridge: get_bridge_vlan_nojson: %s' % str(e)) return {} if not bridgeout: return brvlaninfo try: vlan_json = json.loads(bridgeout, encoding="utf-8") except Exception, e: self.logger.info('json loads failed with (%s)' % str(e)) return {} try: if isinstance(vlan_json, list): # newer iproute2 version changed the bridge vlan show output # ifupdown2 relies on the previous format, we have the convert # data into old format bridge_port_vids = dict() for intf in vlan_json: bridge_port_vids[intf["ifname"]] = intf["vlans"] return bridge_port_vids else: # older iproute2 version have different ways to dump vlans # ifupdown2 prefers the following syntax: # { # "vx-1002": [{ # "vlan": 1002, # "flags": ["PVID", "Egress Untagged"] # } # ], # "vx-1004": [{ # "vlan": 1004, # "flags": ["PVID", "Egress Untagged"] # }] # } return vlan_json except Exception as e: self.logger.debug("bridge vlan show: Unknown json output: %s" % str(e)) return vlan_json @staticmethod def get_bridge_vlan_nojson(): vlan_json = {} bridgeout = utils.exec_commandl([utils.bridge_cmd, '-c', 'vlan', 'show']) if bridgeout: output = [line.split('\n') for line in bridgeout.split('\n\n')] output[0] = output[0][1:] for line in output: current_swp = None if not line: continue for entry in line: if not entry: continue prefix, vlan = entry.split('\t') if prefix: current_swp = prefix vlan_json[prefix] = [] v = {} vlan = vlan[1:] try: v['vlan'] = int(vlan) except: try: if '-' in vlan: start, end = vlan.split('-') if ' ' in end: end = end[0:end.index(' ')] v['vlan'] = int(start) v['vlanEnd'] = int(end) else: v['vlan'] = int(vlan[0:vlan.index(' ')]) flags = [] if 'PVID' in vlan: flags.append('PVID') if 'Egress Untagged' in vlan: flags.append('Egress Untagged') v['flags'] = flags except: continue vlan_json[current_swp].append(v) return vlan_json def bridge_vlan_cache_get(self, ifacename, refresh=False): if not self.bridge_vlan_cache_fill_done or refresh: self.bridge_vlan_cache = self.bridge_port_vids_get_all_json() self.bridge_vlan_cache_fill_done = True return self.bridge_vlan_cache.get(ifacename, {}) def bridge_vlan_get_pvid(self, ifacename, refresh=False): pvid = 0 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh): v = vinfo.get('vlan') pvid = v if 'PVID' in vinfo.get('flags', []) else 0 if pvid: return pvid return pvid def bridge_vlan_get_vids(self, ifacename, refresh=False): vids = [] for vinfo in self.bridge_vlan_cache_get(ifacename, refresh): v = vinfo.get('vlan') ispvid = True if 'PVID' in vinfo.get('flags', []) else False if ispvid: pvid = v if 'PVID' in vinfo.get('flags', []) else 0 if pvid == 1: continue vEnd = vinfo.get('vlanEnd') if vEnd: vids.extend(range(v, vEnd + 1)) else: vids.append(v) return vids def bridge_vlan_get_vids_n_pvid(self, ifacename, refresh=False): vids = [] pvid = 0 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh): v = vinfo.get('vlan') ispvid = True if 'PVID' in vinfo.get('flags', []) else False if ispvid: pvid = v if 'PVID' in vinfo.get('flags', []) else 0 vEnd = vinfo.get('vlanEnd') if vEnd: vids.extend(range(v, vEnd + 1)) else: vids.append(v) return vids, pvid def bridge_port_pvid_add(self, bridgeportname, pvid): if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch('vlan add vid %s untagged pvid dev %s' % (pvid, bridgeportname)) else: utils.exec_command('%s vlan add vid %s untagged pvid dev %s' % (utils.bridge_cmd, pvid, bridgeportname)) def bridge_port_pvid_del(self, bridgeportname, pvid): if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch('vlan del vid %s untagged pvid dev %s' % (pvid, bridgeportname)) else: utils.exec_command('%s vlan del vid %s untagged pvid dev %s' % (utils.bridge_cmd, pvid, bridgeportname)) def bridge_port_pvids_get(self, bridgeportname): return self.read_file_oneline('/sys/class/net/%s/brport/pvid' % bridgeportname) def bridge_vids_add(self, bridgeportname, vids, bridge=True): target = 'self' if bridge else '' if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: [self.add_to_batch('vlan add vid %s dev %s %s' % (v, bridgeportname, target)) for v in vids] else: [utils.exec_command('%s vlan add vid %s dev %s %s' % (utils.bridge_cmd, v, bridgeportname, target)) for v in vids] def bridge_vids_del(self, bridgeportname, vids, bridge=True): target = 'self' if bridge else '' if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: [self.add_to_batch('vlan del vid %s dev %s %s' % (v, bridgeportname, target)) for v in vids] else: [utils.exec_command('%s vlan del vid %s dev %s %s' % (utils.bridge_cmd, v, bridgeportname, target)) for v in vids] @staticmethod def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None): target = 'self' if bridge else '' vlan_str = '' if vlan: vlan_str = 'vlan %s ' % vlan dst_str = '' if remote: dst_str = 'dst %s ' % remote utils.exec_command('%s fdb replace %s dev %s %s %s %s' % (utils.bridge_cmd, address, dev, vlan_str, target, dst_str)) @staticmethod def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None): target = 'self' if bridge else '' vlan_str = '' if vlan: vlan_str = 'vlan %s ' % vlan dst_str = '' if remote: dst_str = 'dst %s ' % remote utils.exec_command('%s fdb append %s dev %s %s %s %s' % (utils.bridge_cmd, address, dev, vlan_str, target, dst_str)) @staticmethod def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None): target = 'self' if bridge else '' vlan_str = '' if vlan: vlan_str = 'vlan %s ' % vlan dst_str = '' if remote: dst_str = 'dst %s ' % remote utils.exec_command('%s fdb del %s dev %s %s %s %s' % (utils.bridge_cmd, address, dev, vlan_str, target, dst_str)) def bridge_is_vlan_aware(self, bridgename): filename = '/sys/class/net/%s/bridge/vlan_filtering' % bridgename if os.path.exists(filename) and self.read_file_oneline(filename) == '1': return True return False @staticmethod def bridge_port_get_bridge_name(bridgeport): filename = '/sys/class/net/%s/brport/bridge' % bridgeport try: return os.path.basename(os.readlink(filename)) except: return None @staticmethod def bridge_port_exists(bridge, bridgeportname): try: return os.path.exists('/sys/class/net/%s/brif/%s' % (bridge, bridgeportname)) except Exception: return False def bridge_fdb_show_dev(self, dev): try: fdbs = {} output = utils.exec_command('%s fdb show dev %s' % (utils.bridge_cmd, 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)) return fdbs except Exception: return None @staticmethod def is_bridge(bridge): return os.path.exists('/sys/class/net/%s/bridge' % bridge) def is_link_up(self, ifacename): ret = False try: flags = self.read_file_oneline('/sys/class/net/%s/flags' % ifacename) iflags = int(flags, 16) if iflags & 0x0001: ret = True except: ret = False return ret def ip_route_get_dev(self, prefix, vrf_master=None): try: if vrf_master: cmd = '%s route get %s vrf %s' % (utils.ip_cmd, prefix, vrf_master) else: cmd = '%s route get %s' % (utils.ip_cmd, prefix) output = utils.exec_command(cmd) if output: rline = output.splitlines()[0] if rline: rattrs = rline.split() return rattrs[rattrs.index('dev') + 1] except Exception, e: self.logger.debug('ip_route_get_dev: failed .. %s' % str(e)) return None @staticmethod def link_get_lowers(ifacename): try: lowers = glob.glob("/sys/class/net/%s/lower_*" % ifacename) if not lowers: return [] return [os.path.basename(l)[6:] for l in lowers] except: return [] @staticmethod def link_get_uppers(ifacename): try: uppers = glob.glob("/sys/class/net/%s/upper_*" % ifacename) if not uppers: return None return [os.path.basename(u)[6:] for u in uppers] except Exception: return None def link_get_vrfs(self): if not LinkUtils._CACHE_FILL_DONE: self._fill_cache() return linkCache.vrfs @staticmethod def cache_get_info_slave(attrlist): try: return linkCache.get_attr(attrlist) except: return None def get_brport_learning(self, ifacename): learn = self.read_file_oneline('/sys/class/net/%s/brport/learning' % ifacename) if learn and learn == '1': return 'on' else: return 'off' def get_brport_learning_bool(self, ifacename): return utils.get_boolean_from_string(self.read_file_oneline('/sys/class/net/%s/brport/learning' % ifacename)) def set_brport_learning(self, ifacename, learn): if learn == 'off': return self.write_file('/sys/class/net/%s/brport/learning' % ifacename, '0') else: return self.write_file('/sys/class/net/%s/brport/learning' % ifacename, '1') ################################################################################# ################################### BOND UTILS ################################## ################################################################################# def _link_cache_get(self, attrlist, refresh=False): return self._cache_get('link', attrlist, refresh) def cache_delete(self, attrlist, value=None): return self._cache_delete(attrlist, value) def link_cache_get(self, attrlist, refresh=False): return self._link_cache_get(attrlist, refresh) def link_cache_check(self, attrlist, value, refresh=False): return self._link_cache_check(attrlist, value, refresh) def _link_cache_check(self, attrlist, value, refresh=False): try: return self._link_cache_get(attrlist, refresh) == value except Exception, e: self.logger.debug('_cache_check(%s) : [%s]' % (str(attrlist), str(e))) pass return False bondcmd_attrmap = { Link.IFLA_BOND_MODE: 'mode', Link.IFLA_BOND_MIIMON: 'miimon', Link.IFLA_BOND_USE_CARRIER: 'use_carrier', Link.IFLA_BOND_AD_LACP_RATE: 'lacp_rate', Link.IFLA_BOND_XMIT_HASH_POLICY: 'xmit_hash_policy', Link.IFLA_BOND_MIN_LINKS: 'min_links', Link.IFLA_BOND_NUM_PEER_NOTIF: 'num_grat_arp', Link.IFLA_BOND_AD_ACTOR_SYSTEM: 'ad_actor_system', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: 'ad_actor_sys_prio', Link.IFLA_BOND_AD_LACP_BYPASS: 'lacp_bypass', Link.IFLA_BOND_UPDELAY: 'updelay', Link.IFLA_BOND_DOWNDELAY: 'downdelay', } def bond_set_attrs_nl(self, bondname, ifla_info_data): bond_attr_name = 'None' # for log purpose (in case an exception raised) for nl_attr, value in ifla_info_data.items(): try: bond_attr_name = self.bondcmd_attrmap[nl_attr] file_path = '/sys/class/net/%s/bonding/%s' % (bondname, bond_attr_name) if os.path.exists(file_path): self.write_file(file_path, str(value)) except Exception as e: exception_str = '%s: %s %s: %s' % (bondname, bond_attr_name, value, str(e)) if ifupdownflags.flags.FORCE: self.logger.warning(exception_str) else: self.logger.debug(exception_str) def bond_set_attrs(self, bondname, attrdict, prehook): for attrname, attrval in attrdict.items(): if (self._link_cache_check([bondname, 'linkinfo', attrname], attrval)): continue if (attrname == 'mode' or attrname == 'xmit_hash_policy' or attrname == 'lacp_rate' or attrname == 'min_links'): if prehook: prehook(bondname) try: if ((attrname not in ['lacp_rate', 'lacp_bypass']) or self._link_cache_check([bondname, 'linkinfo', 'mode'], '802.3ad', True)): self.write_file('/sys/class/net/%s/bonding/%s' % (bondname, attrname), attrval) except Exception, e: if ifupdownflags.flags.FORCE: self.logger.warn(str(e)) pass else: raise def bond_set_use_carrier(self, bondname, use_carrier): if not use_carrier or (use_carrier != '0' and use_carrier != '1'): return if (self._link_cache_check([bondname, 'linkinfo', 'use_carrier'], use_carrier)): return self.write_file('/sys/class/net/%s' % bondname + '/bonding/use_carrier', use_carrier) self._cache_update([bondname, 'linkinfo', 'use_carrier'], use_carrier) def bond_get_use_carrier(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'use_carrier']) def bond_get_use_carrier_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER]) def bond_set_xmit_hash_policy(self, bondname, hash_policy, prehook=None): valid_values = ['layer2', 'layer3+4', 'layer2+3'] if not hash_policy: return if hash_policy not in valid_values: raise Exception('invalid hash policy value %s' % hash_policy) if (self._link_cache_check([bondname, 'linkinfo', 'xmit_hash_policy'], hash_policy)): return if prehook: prehook(bondname) self.write_file('/sys/class/net/%s' % bondname + '/bonding/xmit_hash_policy', hash_policy) self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'], hash_policy) def bond_get_xmit_hash_policy(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'xmit_hash_policy']) def bond_get_xmit_hash_policy_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY]) def bond_set_miimon(self, bondname, miimon): if (self._link_cache_check([bondname, 'linkinfo', 'miimon'], miimon)): return self.write_file('/sys/class/net/%s' % bondname + '/bonding/miimon', miimon) self._cache_update([bondname, 'linkinfo', 'miimon'], miimon) def bond_get_miimon(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'miimon']) def bond_get_miimon_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON]) def bond_set_mode(self, bondname, mode, prehook=None): valid_modes = ['balance-rr', 'active-backup', 'balance-xor', 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb'] if not mode: return if mode not in valid_modes: raise Exception('invalid mode %s' % mode) if (self._link_cache_check([bondname, 'linkinfo', 'mode'], mode)): return if prehook: prehook(bondname) self.write_file('/sys/class/net/%s' % bondname + '/bonding/mode', mode) self._cache_update([bondname, 'linkinfo', 'mode'], mode) def bond_get_mode(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'mode']) def bond_get_mode_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE]) def bond_set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None): if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'): return if (self._link_cache_check([bondname, 'linkinfo', 'lacp_rate'], lacp_rate)): return if prehook: prehook(bondname) try: self.write_file('/sys/class/net/%s' % bondname + '/bonding/lacp_rate', lacp_rate) except: raise finally: if posthook: prehook(bondname) self._cache_update([bondname, 'linkinfo', 'lacp_rate'], lacp_rate) def bond_get_lacp_rate(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'lacp_rate']) def bond_get_lacp_rate_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE]) def bond_set_lacp_bypass_allow(self, bondname, allow, prehook=None, posthook=None): if self._link_cache_check([bondname, 'linkinfo', 'lacp_bypass'], allow): return if prehook: prehook(bondname) try: self.write_file('/sys/class/net/%s' % bondname + '/bonding/lacp_bypass', allow) except: raise finally: if posthook: posthook(bondname) self._cache_update([bondname, 'linkinfo', 'lacp_bypass'], allow) def bond_get_lacp_bypass_allow(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'lacp_bypass']) def bond_get_lacp_bypass_allow_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS]) def bond_set_min_links(self, bondname, min_links, prehook=None): if (self._link_cache_check([bondname, 'linkinfo', 'min_links'], min_links)): return if prehook: prehook(bondname) self.write_file('/sys/class/net/%s/bonding/min_links' % bondname, min_links) self._cache_update([bondname, 'linkinfo', 'min_links'], min_links) def bond_get_min_links(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'min_links']) def get_min_links_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS]) def bond_get_ad_actor_system(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_system']) def bond_get_ad_actor_system_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM]) def bond_get_ad_actor_sys_prio(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_sys_prio']) def bond_get_ad_actor_sys_prio_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO]) def bond_get_num_unsol_na(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'num_unsol_na']) def bond_get_num_unsol_na_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]) def bond_get_num_grat_arp(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'num_grat_arp']) def bond_get_num_grat_arp_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]) def bond_get_updelay(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'updelay']) def bond_get_updelay_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY]) def bond_get_downdelay(self, bondname): return self._link_cache_get([bondname, 'linkinfo', 'downdelay']) def bond_get_downdelay_nl(self, bondname): return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY]) def bond_enslave_slave(self, bondname, slave, prehook=None, posthook=None): slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves']) if slaves and slave in slaves: return if prehook: prehook(slave) self.write_file('/sys/class/net/%s' % bondname + '/bonding/slaves', '+' + slave) if posthook: posthook(slave) self._cache_update([bondname, 'linkinfo', 'slaves'], slave) def bond_remove_slave(self, bondname, slave): slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves']) if not slaves or slave not in slaves: return sysfs_bond_path = ('/sys/class/net/%s' % bondname + '/bonding/slaves') if not os.path.exists(sysfs_bond_path): return self.write_file(sysfs_bond_path, '-' + slave) self._cache_delete([bondname, 'linkinfo', 'slaves'], slave) def bond_remove_slaves_all(self, bondname): if not self._link_cache_get([bondname, 'linkinfo', 'slaves']): return slaves = None sysfs_bond_path = ('/sys/class/net/%s' % bondname + '/bonding/slaves') try: with open(sysfs_bond_path, 'r') as f: slaves = f.readline().strip().split() except IOError, e: raise Exception('error reading slaves of bond %s (%s)' % (bondname, str(e))) for slave in slaves: self.link_down(slave) try: self.bond_remove_slave(bondname, slave) except Exception, e: if not ifupdownflags.flags.FORCE: raise Exception('error removing slave %s from bond %s (%s)' % (slave, bondname, str(e))) else: pass self._cache_delete([bondname, 'linkinfo', 'slaves']) @staticmethod def bond_load_bonding_module(): return utils.exec_command('%s -q bonding' % utils.modprobe_cmd) def create_bond(self, bondname): if self.bond_exists(bondname): return # load_bonding_module() has already been run self.write_file('/sys/class/net/bonding_masters', '+' + bondname) self._cache_update([bondname], {}) def delete_bond(self, bondname): if not os.path.exists('/sys/class/net/%s' % bondname): return self.write_file('/sys/class/net/bonding_masters', '-' + bondname) self._cache_delete([bondname]) def bond_get_slaves(self, bondname): slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves']) if slaves: return list(slaves) slavefile = '/sys/class/net/%s/bonding/slaves' % bondname if os.path.exists(slavefile): buf = self.read_file_oneline(slavefile) if buf: slaves = buf.split() if not slaves: return [] self._cache_update([bondname, 'linkinfo', 'slaves'], slaves) return list(slaves) def bond_slave_exists(self, bond, slave): slaves = self.bond_get_slaves(bond) if not slaves: return False return slave in slaves @staticmethod def bond_exists(bondname): return os.path.exists('/sys/class/net/%s/bonding' % bondname) ################################################################################# ################################## BRIDGE UTILS ################################# ################################################################################# def create_bridge(self, bridgename): if not LinkUtils.bridge_utils_is_installed: return if self.bridge_exists(bridgename): return utils.exec_command('%s addbr %s' % (utils.brctl_cmd, bridgename)) self._cache_update([bridgename], {}) def delete_bridge(self, bridgename): if not LinkUtils.bridge_utils_is_installed: return if not self.bridge_exists(bridgename): return utils.exec_command('%s delbr %s' % (utils.brctl_cmd, bridgename)) self._cache_invalidate() def add_bridge_port(self, bridgename, bridgeportname): """ Add port to bridge """ if not LinkUtils.bridge_utils_is_installed: return ports = self._link_cache_get([bridgename, 'linkinfo', 'ports']) if ports and ports.get(bridgeportname): return utils.exec_command('%s addif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname)) self._cache_update([bridgename, 'linkinfo', 'ports', bridgeportname], {}) def delete_bridge_port(self, bridgename, bridgeportname): """ Delete port from bridge """ if not LinkUtils.bridge_utils_is_installed: return ports = self._link_cache_get([bridgename, 'linkinfo', 'ports']) if not ports or not ports.get(bridgeportname): return utils.exec_command('%s delif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname)) self._cache_delete([bridgename, 'linkinfo', 'ports', 'bridgeportname']) def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict): portattrs = self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname]) if portattrs == None: portattrs = {} for k, v in attrdict.iteritems(): if ifupdownflags.flags.CACHE: curval = portattrs.get(k) if curval and curval == v: continue if k == 'unicast-flood': self.write_file('/sys/class/net/%s/brport/unicast_flood' % bridgeportname, v) elif k == 'multicast-flood': self.write_file('/sys/class/net/%s/brport/multicast_flood' % bridgeportname, v) elif k == 'learning': self.write_file('/sys/class/net/%s/brport/learning' % bridgeportname, v) elif k == 'arp-nd-suppress': self.write_file('/sys/class/net/%s/brport/neigh_suppress' % bridgeportname, v) else: if not LinkUtils.bridge_utils_is_installed: continue utils.exec_command('%s set%s %s %s %s' % (utils.brctl_cmd, k, bridgename, bridgeportname, v)) def set_bridgeport_attr(self, bridgename, bridgeportname, attrname, attrval): if not LinkUtils.bridge_utils_is_installed: return if self._link_cache_check([bridgename, 'linkinfo', 'ports', bridgeportname, attrname], attrval): return utils.exec_command('%s set%s %s %s %s' % (utils.brctl_cmd, attrname, bridgename, bridgeportname, attrval)) def set_bridge_attrs(self, bridgename, attrdict): for k, v in attrdict.iteritems(): if not v: continue if self._link_cache_check([bridgename, 'linkinfo', k], v): continue try: if k == 'igmp-version': self.write_file('/sys/class/net/%s/bridge/' 'multicast_igmp_version' % bridgename, v) elif k == 'mld-version': self.write_file('/sys/class/net/%s/bridge/' 'multicast_mld_version' % bridgename, v) elif k == 'vlan-protocol': self.write_file('/sys/class/net/%s/bridge/' 'vlan_protocol' % bridgename, VlanProtocols.ETHERTYPES_TO_ID.get(v.upper(), None)) elif k == 'vlan-stats': self.write_file('/sys/class/net/%s/bridge/' 'vlan_stats_enabled' % bridgename, v) elif k == 'mcstats': self.write_file('/sys/class/net/%s/bridge/' 'multicast_stats_enabled' % bridgename, v) else: if not LinkUtils.bridge_utils_is_installed: continue cmd = ('%s set%s %s %s' % (utils.brctl_cmd, k, bridgename, v)) utils.exec_command(cmd) except Exception, e: self.logger.warn('%s: %s' % (bridgename, str(e))) pass def set_bridge_attr(self, bridgename, attrname, attrval): if self._link_cache_check([bridgename, 'linkinfo', attrname], attrval): return if attrname == 'igmp-version': self.write_file('/sys/class/net/%s/bridge/multicast_igmp_version' % bridgename, attrval) elif attrname == 'mld-version': self.write_file('/sys/class/net/%s/bridge/multicast_mld_version' % bridgename, attrval) elif attrname == 'vlan-protocol': self.write_file('/sys/class/net/%s/bridge/vlan_protocol' % bridgename, VlanProtocols.ETHERTYPES_TO_ID[attrval.upper()]) elif attrname == 'vlan-stats': self.write_file('/sys/class/net/%s/bridge/vlan_stats_enabled' % bridgename, attrval) elif attrname == 'mcstats': self.write_file('/sys/class/net/%s/bridge/multicast_stats_enabled' % bridgename, attrval) else: if not LinkUtils.bridge_utils_is_installed: return cmd = '%s set%s %s %s' % (utils.brctl_cmd, attrname, bridgename, attrval) utils.exec_command(cmd) def get_bridge_attrs(self, bridgename): attrs = self._link_cache_get([bridgename, 'linkinfo']) no_ints_attrs = {} for key, value in attrs.items(): if type(key) == str: no_ints_attrs[key] = value return no_ints_attrs def get_bridgeport_attrs(self, bridgename, bridgeportname): return self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname]) def get_bridgeport_attr(self, bridgename, bridgeportname, attrname): return self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname, attrname]) @staticmethod def bridge_set_stp(bridge, stp_state): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s stp %s %s' % (utils.brctl_cmd, bridge, stp_state)) def bridge_get_stp(self, bridge): sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' % bridge if not os.path.exists(sysfs_stpstate): return 'error' stpstate = self.read_file_oneline(sysfs_stpstate) if not stpstate: return 'error' try: if int(stpstate) > 0: return 'yes' elif int(stpstate) == 0: return 'no' except: return 'unknown' @staticmethod def _conv_value_to_user(s): try: ret = int(s) / 100 return '%d' % ret except: return None def read_value_from_sysfs(self, filename, preprocess_func): value = self.read_file_oneline(filename) if not value: return None return preprocess_func(value) @staticmethod def bridge_set_ageing(bridge, ageing): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setageing %s %s' % (utils.brctl_cmd, bridge, ageing)) def bridge_get_ageing(self, bridge): return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time' % bridge, self._conv_value_to_user) @staticmethod def set_bridgeprio(bridge, prio): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setbridgeprio %s %s' % (utils.brctl_cmd, bridge, prio)) def get_bridgeprio(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/priority' % bridge) @staticmethod def bridge_set_fd(bridge, fd): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setfd %s %s' % (utils.brctl_cmd, bridge, fd)) def bridge_get_fd(self, bridge): return self.read_value_from_sysfs( '/sys/class/net/%s/bridge/forward_delay' % bridge, self._conv_value_to_user) def bridge_set_gcint(self, bridge, gcint): raise Exception('set_gcint not implemented') @staticmethod def bridge_set_hello(bridge, hello): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s sethello %s %s' % (utils.brctl_cmd, bridge, hello)) def bridge_get_hello(self, bridge): return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time' % bridge, self._conv_value_to_user) @staticmethod def bridge_set_maxage(bridge, maxage): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmaxage %s %s' % (utils.brctl_cmd, bridge, maxage)) def bridge_get_maxage(self, bridge): return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age' % bridge, self._conv_value_to_user) @staticmethod def bridge_set_pathcost(bridge, port, pathcost): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setpathcost %s %s %s' % (utils.brctl_cmd, bridge, port, pathcost)) def bridge_get_pathcost(self, bridge, port): return self.read_file_oneline('/sys/class/net/%s/brport/path_cost' % port) @staticmethod def bridge_set_portprio(bridge, port, prio): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setportprio %s %s %s' % (utils.brctl_cmd, bridge, port, prio)) def bridge_get_portprio(self, bridge, port): return self.read_file_oneline('/sys/class/net/%s/brport/priority' % port) @staticmethod def bridge_set_hashmax(bridge, hashmax): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s sethashmax %s %s' % (utils.brctl_cmd, bridge, hashmax)) def bridge_get_hashmax(self, bridge): return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max' % bridge) @staticmethod def bridge_set_hashel(bridge, hashel): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s sethashel %s %s' % (utils.brctl_cmd, bridge, hashel)) def bridge_get_hashel(self, bridge): return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity' % bridge) @staticmethod def bridge_set_mclmc(bridge, mclmc): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmclmc %s %s' % (utils.brctl_cmd, bridge, mclmc)) def bridge_get_mclmc(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_last_member_count' % bridge) @staticmethod def bridge_set_mcrouter(bridge, mcrouter): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmcrouter %s %s' % (utils.brctl_cmd, bridge, mcrouter)) def bridge_get_mcrouter(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_router' % bridge) @staticmethod def bridge_set_mcsnoop(bridge, mcsnoop): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmcsnoop %s %s' % (utils.brctl_cmd, bridge, mcsnoop)) def bridge_get_mcsnoop(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_snooping' % bridge) @staticmethod def bridge_set_mcsqc(bridge, mcsqc): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmcsqc %s %s' % (utils.brctl_cmd, bridge, mcsqc)) def bridge_get_mcsqc(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_startup_query_count' % bridge) @staticmethod def bridge_set_mcqifaddr(bridge, mcqifaddr): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmcqifaddr %s %s' % (utils.brctl_cmd, bridge, mcqifaddr)) def bridge_get_mcqifaddr(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr' % bridge) @staticmethod def bridge_set_mcquerier(bridge, mcquerier): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmcquerier %s %s' % (utils.brctl_cmd, bridge, mcquerier)) def bridge_get_mcquerier(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_querier' % bridge) def bridge_set_mcqv4src(self, bridge, vlan, mcquerier): try: vlan = int(vlan) except: self.logger.info('%s: set mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e))) return if vlan == 0 or vlan > 4095: self.logger.warn('mcqv4src vlan \'%d\' invalid range' % vlan) return ip = mcquerier.split('.') if len(ip) != 4: self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier) return for k in ip: if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255: self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier) return if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmcqv4src %s %d %s' % (utils.brctl_cmd, bridge, vlan, mcquerier)) def bridge_del_mcqv4src(self, bridge, vlan): if not LinkUtils.bridge_utils_is_installed: return try: vlan = int(vlan) except: self.logger.info('%s: del mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e))) return utils.exec_command('%s delmcqv4src %s %d' % (utils.brctl_cmd, bridge, vlan)) def bridge_get_mcqv4src(self, bridge, vlan=None): if not LinkUtils.bridge_utils_is_installed: return {} if not self.supported_command['showmcqv4src']: return {} mcqv4src = {} try: mcqout = utils.exec_command('%s showmcqv4src %s' % (utils.brctl_cmd, bridge)) except Exception as e: s = str(e).lower() if 'never heard' in s: msg = ('%s showmcqv4src: skipping unsupported command' % utils.brctl_cmd) self.logger.info(msg) self.supported_command['showmcqv4src'] = False return {} raise if not mcqout: return {} mcqlines = mcqout.splitlines() for l in mcqlines[1:]: l = l.strip() k, d, v = l.split('\t') if not k or not v: continue mcqv4src[k] = v if vlan: return mcqv4src.get(vlan) return mcqv4src def bridge_get_mcqv4src_sysfs(self, bridge, vlan=None): if not LinkUtils.bridge_utils_is_installed: return {} if not self.supported_command['showmcqv4src']: return {} if ifupdownflags.flags.PERFMODE: return {} mcqv4src = {} try: filename = '/sys/class/net/%s/bridge/multicast_v4_queriers' % bridge if os.path.exists(filename): for line in self.read_file(filename) or []: vlan_id, ip = line.split('=') mcqv4src[vlan_id] = ip.strip() except Exception as e: s = str(e).lower() msg = ('%s showmcqv4src: skipping unsupported command' % utils.brctl_cmd) self.logger.info(msg) self.supported_command['showmcqv4src'] = False return {} if vlan: return mcqv4src.get(vlan) return mcqv4src @staticmethod def bridge_set_mclmi(bridge, mclmi): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmclmi %s %s' % (utils.brctl_cmd, bridge, mclmi)) def bridge_get_mclmi(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_last_member_interval' % bridge) @staticmethod def bridge_set_mcmi(bridge, mcmi): if not LinkUtils.bridge_utils_is_installed: return utils.exec_command('%s setmcmi %s %s' % (utils.brctl_cmd, bridge, mcmi)) def bridge_get_mcmi(self, bridge): return self.read_file_oneline( '/sys/class/net/%s/bridge/multicast_membership_interval' % bridge) @staticmethod def bridge_exists(bridge): return os.path.exists('/sys/class/net/%s/bridge' % bridge) @staticmethod def is_bridge_port(ifacename): return os.path.exists('/sys/class/net/%s/brport' % ifacename) @staticmethod def bridge_port_exists(bridge, bridgeportname): try: return os.path.exists('/sys/class/net/%s/brif/%s' % (bridge, bridgeportname)) except: return False @staticmethod def get_bridge_ports(bridgename): try: return os.listdir('/sys/class/net/%s/brif/' % bridgename) except: return [] def reset_addr_cache(self, ifname): try: linkCache.links[ifname]['addrs'] = {} self.logger.debug('%s: reset address cache' % ifname) except: pass def get_ipv6_addrgen_mode(self, ifname): try: return self._cache_get('link', [ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] except: # default to 0 (eui64) return 0 def ipv6_addrgen(self, ifname, addrgen, link_created): try: # IFLA_INET6_ADDR_GEN_MODE values: # 0 = eui64 # 1 = none if self._link_cache_get([ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] == addrgen: self.logger.debug('%s: ipv6 addrgen already %s' % (ifname, 'off' if addrgen else 'on')) return disabled_ipv6 = self.read_file_oneline('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % ifname) if not disabled_ipv6 or int(disabled_ipv6) == 1: self.logger.info('%s: cannot set addrgen: ipv6 is disabled on this device' % ifname) return if int(self._link_cache_get([ifname, 'mtu'])) < 1280: self.logger.info('%s: ipv6 addrgen is disabled on device with MTU ' 'lower than 1280: cannot set addrgen %s' % (ifname, 'off' if addrgen else 'on')) return except (KeyError, TypeError): self.logger.debug('%s: ipv6 addrgen probably not supported or disabled on this device' % ifname) return except Exception: pass if not link_created: # When setting addrgenmode it is necessary to flap the macvlan # device. After flapping the device we also need to re-add all # the user configuration. The best way to add the user config # is to flush our internal address cache self.reset_addr_cache(ifname) cmd = 'link set dev %s addrgenmode %s' % (ifname, Link.ifla_inet6_addr_gen_mode_dict.get(addrgen)) is_link_up = self.is_link_up(ifname) if is_link_up: self.link_down(ifname) #if LinkUtils.ipbatch: # self.add_to_batch(cmd) #else: # because this command might fail on older kernel its better to not batch it utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) if is_link_up: self.link_up(ifname)