1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Files
CumulusNetworks-ifupdown2/ifupdown2/ifupdownaddons/LinkUtils.py
2019-09-18 12:48:26 +02:00

2792 lines
108 KiB
Python

#!/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)