import glob
import shlex
import signal
+import socket
import subprocess
+from string import maketrans
from ipaddr import IPNetwork, IPv6Network
try:
DEFAULT_IP_METRIC = 1024
ADDR_METRIC_SUPPORT = None
+ mac_translate_tab = maketrans(":.-,", " ")
+
def __init__(self, *args, **kargs):
utilsBase.__init__(self, *args, **kargs)
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
battrs = {}
bports = {}
- brout = utils.exec_command('%s showstp %s' % (utils.brctl_cmd, bridgename))
- chunks = re.split(r'\n\n', brout, maxsplit=0, flags=re.MULTILINE)
-
try:
# Get all bridge attributes
- broutlines = chunks[0].splitlines()
# battrs['pathcost'] = broutlines[3].split('path cost')[1].strip()
try:
- battrs['maxage'] = broutlines[4].split('bridge max age')[
- 1].strip().replace('.00', '')
+ battrs['maxage'] = self.read_file_oneline(
+ '/sys/class/net/%s/bridge/max_age' % bridgename)
except:
pass
+
try:
- battrs['hello'] = broutlines[5].split('bridge hello time')[
- 1].strip().replace('.00', '')
+ battrs['hello'] = self.read_file_oneline(
+ '/sys/class/net/%s/bridge/hello_time' % bridgename)
except:
pass
try:
- battrs['fd'] = broutlines[6].split('bridge forward delay')[
- 1].strip().replace('.00', '')
+ battrs['fd'] = self.read_file_oneline(
+ '/sys/class/net/%s/bridge/forward_delay' % bridgename)
except:
pass
try:
- battrs['ageing'] = broutlines[7].split('ageing time')[
- 1].strip().replace('.00', '')
+ battrs['ageing'] = self.read_file_oneline(
+ '/sys/class/net/%s/bridge/ageing_time' % bridgename)
except:
pass
try:
- battrs['mcrouter'] = broutlines[12].split('mc router')[
- 1].strip().split('\t\t\t')[0]
+ battrs['mcrouter'] = self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_router' % bridgename)
except:
pass
# XXX: comment this out until mc attributes become available
# with brctl again
- # battrs['hashel'] = broutlines[10].split('hash elasticity')[1].split()[0].strip()
- # battrs['hashmax'] = broutlines[10].split('hash max')[1].strip()
- # battrs['mclmc'] = broutlines[11].split('mc last member count')[1].split()[0].strip()
+
# battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip()
- # battrs['mcrouter'] = broutlines[12].split('mc router')[1].split()[0].strip()
- ##battrs['mcsnoop'] = broutlines[12].split('mc snooping')[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)
- for cidx in range(1, len(chunks)):
- bpout = chunks[cidx].lstrip('\n')
- if not bpout or bpout[0] == ' ':
- continue
- bplines = bpout.splitlines()
- pname = bplines[0].split()[0]
+
+ names = [os.path.basename(x) for x in glob.glob("/sys/class/net/%s/brif/*" % bridgename)]
+ for pname in names:
bportattrs = {}
try:
- bportattrs['pathcost'] = bplines[2].split(
- 'path cost')[1].strip()
- bportattrs['fdelay'] = bplines[4].split(
- 'forward delay timer')[1].strip()
+
+ 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/learning' % pname)
bportattrs['arp-nd-suppress'] = self.read_file_oneline(
'/sys/class/net/%s/brport/neigh_suppress' % pname)
- # bportattrs['mcrouters'] = bplines[6].split('mc router')[1].split()[0].strip()
- # bportattrs['mc fast leave'] = bplines[6].split('mc fast leave')[1].strip()
+
+ #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
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':
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, '
[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)
return
cmd = 'addr del %s' % address
if broadcast:
- cmd += 'broadcast %s' % broadcast
+ cmd += ' broadcast %s' % broadcast
if peer:
- cmd += 'peer %s' % peer
+ cmd += ' peer %s' % peer
if scope:
- cmd += 'scope %s' % scope
+ cmd += ' scope %s' % scope
cmd += ' dev %s' % ifacename
utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
self._cache_delete([ifacename, 'addrs', address])
interface_name = ifname
if addr_virtual_ifaceobj:
- for virtual in addr_virtual_ifaceobj.get_attr_value('address-virtual') 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('address-virtual') or []:
+ 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 []:
def link_set_hwaddress(self, ifacename, hwaddress, force=False):
if not force:
- if self._cache_check('link', [ifacename, 'hwaddress'], hwaddress):
- return
+ 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:
utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
self.link_up(ifacename)
self._cache_update([ifacename, 'hwaddress'], hwaddress)
+ return True
def link_set_mtu(self, ifacename, mtu):
if ifupdownflags.flags.DRYRUN:
return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
@staticmethod
- def route_add_gateway(ifacename, gateway, vrf=None, metric=None):
+ def route_add_gateway(ifacename, gateway, vrf=None, metric=None, onlink=True):
if not gateway:
return
if not vrf:
(utils.ip_cmd, vrf, gateway))
# Add metric
if metric:
- cmd += 'metric %s' % metric
+ cmd += ' metric %s' % metric
cmd += ' dev %s' % ifacename
+
+ if onlink:
+ cmd += " onlink"
+
utils.exec_command(cmd)
@staticmethod
cmd = ''
if '6' in mode:
- cmd = ' -6 '
+ cmd = ' -6'
+
+ if mode in ['gretap']:
+ cmd += ' link add %s type %s' % (tunnelname, mode)
+ else:
+ cmd += ' tunnel add %s mode %s' % (tunnelname, mode)
- cmd += 'tunnel add'
- cmd += ' %s mode %s' %(tunnelname, mode)
if attrs:
for k, v in attrs.iteritems():
- cmd += ' %s' %k
+ cmd += ' %s' % k
if v:
- cmd += ' %s' %v
+ cmd += ' %s' % v
if self.ipbatch and not self.ipbatch_pause:
self.add_to_batch(cmd)
else:
self.add_to_batch(cmd)
else:
utils.exec_command('ip %s' % cmd)
- self._cache_update([tunnelname], {})
def link_create_vxlan(self, name, vxlanid,
localtunnelip=None,
remoteips=None,
learning='on',
ageing=None,
- anycastip=None):
+ anycastip=None,
+ ttl=None):
if svcnodeip and remoteips:
raise Exception("svcnodeip and remoteip is mutually exclusive")
args = ''
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)
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 False
@staticmethod
- def link_add_macvlan(ifname, macvlan_ifacename):
- utils.exec_commandl(['ip', 'link', 'add', 'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', 'private'])
+ 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):
if not bridgeout: return brvlaninfo
try:
- vlan_json_dict = json.loads(bridgeout, encoding="utf-8")
+ vlan_json = json.loads(bridgeout, encoding="utf-8")
except Exception, e:
self.logger.info('json loads failed with (%s)' % str(e))
return {}
- return vlan_json_dict
+
+ 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():
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:
except:
return []
- def ipv6_addrgen(self, ifname, addrgen):
- cmd = 'link set dev %s addrgenmode %s' % (ifname, 'eui64' if addrgen else 'none')
+ 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:
- utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+ #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)