mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
2792 lines
108 KiB
Python
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)
|