1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00

ifupdown2 2.0.0-1

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
This commit is contained in:
Julien Fortin
2019-12-17 01:04:54 +01:00
parent 8ebee96099
commit 223ba5af1d
68 changed files with 12533 additions and 9450 deletions

2
.gitignore vendored
View File

@@ -16,7 +16,7 @@ dist/
downloads/
eggs/
.eggs/
lib/
/lib/
lib64/
parts/
sdist/

20
debian/changelog vendored
View File

@@ -1,8 +1,22 @@
ifupdown2 (1.2.9-1) unstable; urgency=medium
ifupdown2 (2.0.0-1) unstable; urgency=medium
*
* Introduction of the live netlink cache
* Refactoring and PEP8 fixes
* Install systemd ifupdown2.netowrking.service and ifup.service
* Addons: bond: bond-primary attributes (closes: #9)
* Addons: address-virtual: vrrp support
* Addons: address: add arp-accept option
* Addons: tunnel: adding "tunnel-" prefix to every attributes
* Loopback interface won't go down (unless link-down yes)
* Macvlans (address-virtual) can now be configured without ips
* Add support for vxlan multicast group (vxlan-mcastgrp)
* New sets of poliicies:
- bridge polcy for vxlan port: bridge-vxlan-arp-nd-suppres (ON/off)
- bridge policy for vxlan port: bridge_vxlan_port_learning (ON/off)
- bridge policy for vxlan port:
vxlan_bridge_igmp_snooping_enable_port_mcrouter (1/0)
-- Julien Fortin <julien@cumulusnetworks.com> Thu, 11 Jul 2019 23:42:42 +0200
-- Julien Fortin <julien@cumulusnetworks.com> Tue, 01 Oct 2019 23:42:42 +0200
ifupdown2 (1.2.8-1) unstable; urgency=medium

6
debian/control vendored
View File

@@ -2,13 +2,15 @@ Source: ifupdown2
Section: admin
Priority: optional
Maintainer: Julien Fortin <julien@cumulusnetworks.com>
Build-Depends: debhelper (>= 9.20160709),
Build-Depends: debhelper (>=9),
dh-systemd,
dh-python,
python-all,
python-setuptools,
python-docutils
Standards-Version: 4.2.1
Homepage: https://github.com/cumulusnetworks/ifupdown2
X-Python-Version: >= 2.7
Package: ifupdown2
Architecture: all
@@ -16,7 +18,7 @@ Provides: ifupdown
Conflicts: ifupdown
Replaces: ifupdown
Depends: ${python:Depends}, ${misc:Depends}, iproute2, python-argcomplete, python-ipaddr
Suggests: isc-dhcp-client, bridge-utils, ethtool, python-gvgen, python-mako, python-pkg-resources
Suggests: isc-dhcp-client, bridge-utils, ethtool, python-gvgen, python-mako
Description: Network Interface Management tool similar to ifupdown
ifupdown2 is ifupdown re-written in Python. It replaces ifupdown and provides
the same user interface as ifupdown for network interface configuration.

17
debian/ifup@.service vendored Normal file
View File

@@ -0,0 +1,17 @@
[Unit]
Description=ifup for %I
After=local-fs.target network-pre.target networking.service systemd-sysctl.service
Before=network.target shutdown.target network-online.target
Conflicts=shutdown.target
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
DefaultDependencies=no
IgnoreOnIsolate=yes
[Service]
# avoid stopping on shutdown via stopping system-ifup.slice
Slice=system.slice
ExecStart=/sbin/ifup --allow=hotplug %I
ExecStop=/sbin/ifdown %I
RemainAfterExit=true
TimeoutStartSec=2min

View File

@@ -1,3 +1,2 @@
etc/default/networking /etc/default/
etc/network/ifupdown2/addons.conf /etc/network/ifupdown2/
etc/network/ifupdown2/ifupdown2.conf /etc/network/ifupdown2/

View File

@@ -1,5 +1,5 @@
[Unit]
Description=ifupdown2 networking initialization
Description=Network initialization
Documentation=man:interfaces(5) man:ifup(8) man:ifdown(8)
DefaultDependencies=no
Before=shutdown.target
@@ -10,9 +10,9 @@ Type=oneshot
RemainAfterExit=yes
SyslogIdentifier=networking
TimeoutStopSec=30s
ExecStart=/usr/share/ifupdown2/sbin/start-networking start
ExecStop=/usr/share/ifupdown2/sbin/start-networking stop
ExecReload=/usr/share/ifupdown2/sbin/start-networking reload
ExecStart=/sbin/ifup -a
ExecStop=/sbin/ifdown -a
ExecReload=/sbin/ifreload -a
[Install]
WantedBy=basic.target network.target shutdown.target

9
debian/rules vendored
View File

@@ -11,14 +11,17 @@ override_dh_installman:
./ifupdown2/man/genmanpages.sh ./ifupdown2/man ./man
dh_installman
override_dh_install:
dh_install
mkdir -p debian/ifupdown2/lib/systemd/system/
install --mode=644 debian/ifup@.service debian/ifupdown2/lib/systemd/system/
override_dh_systemd_start:
dh_systemd_start --name=networking --no-start
override_dh_systemd_enable:
dh_systemd_enable --name=networking
override_dh_installinit:
dh_installinit --name=networking --no-start
override_dh_compress:
dh_compress -X.py

1
debian/watch vendored Normal file
View File

@@ -0,0 +1 @@
version=3

View File

@@ -1,23 +0,0 @@
#
#
# Parameters for the /etc/init.d/networking script
#
#
# Change the below to yes if you want verbose logging to be enabled
VERBOSE="no"
# Change the below to yes if you want debug logging to be enabled
DEBUG="no"
# Change the below to yes if you want logging to go to syslog
SYSLOG="no"
# Exclude interfaces
EXCLUDE_INTERFACES=
# Set to 'yes' if you want to skip ifdown during system reboot
# and shutdown. This is of interest in large scale interface
# deployments where you dont want to wait for interface
# deconfiguration to speed up shutdown/reboot
SKIP_DOWN_AT_SYSRESET="yes"

View File

@@ -12,7 +12,9 @@ pre-up,bridgevlan
pre-up,mstpctl
pre-up,tunnel
pre-up,vrf
pre-up,tunnel
pre-up,ethtool
pre-up,address
up,dhcp
up,address
up,addressvirtual
@@ -40,4 +42,4 @@ post-down,batman_adv
post-down,usercmds
post-down,link
post-down,tunnel
post-down,xfrm
post-down,xfrm

View File

@@ -4,6 +4,9 @@
# This file contains default settings for ifupdown
#
# use ifupdown2d
use_daemon=no
# enable templates
template_enable=1
@@ -84,5 +87,5 @@ adjust_logical_dev_mtu=1
# directory where the state file is stored
# if this directory doesn't exists ifupdown2 will create it
# if directory creation fails or state_dir variable is empty
# state_dir will default to /run/network/
state_dir=/run/network/
# state_dir will default to /var/tmp/network/
state_dir=/var/tmp/network/

View File

@@ -1,9 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__version__ = '1.2.9'
__version__ = '2.0.0'
# Copyright (C) 2014,2015,2016,2017,2018,219 Cumulus Networks, Inc. All rights reserved
# Copyright (C) 2014,2015,2016,2017,2018,2019 Cumulus Networks, Inc. All rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as

View File

@@ -1,205 +1,128 @@
#!/usr/bin/python
# Copyright (C) 2016, 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
#
# Copyright 2016 Cumulus Networks, Inc. All rights reserved.
# Author: Julien Fortin, julien@cumulusnetworks.com
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# ifupdown2 - Network Manager
#
import os
import re
import sys
import json
import errno
import struct
import select
import socket
import signal
try:
import ifupdown2.ifupdown.config as core_config
from ifupdown2.ifupdown.log import log
from ifupdown2.lib.log import LogManager, root_logger
from ifupdown2.lib.status import Status
except:
from lib.log import LogManager, root_logger
from lib.status import Status
# first thing first, setup the logging infra
LogManager.get_instance()
try:
import ifupdown2.ifupdown.config as config
from ifupdown2 import __version__
core_config.__version__ = __version__
config.__version__ = __version__
from ifupdown2.lib.exceptions import ExitWithStatus, ExitWithStatusAndError
from ifupdown2.ifupdown.client import Client
from ifupdown2.ifupdown.exceptions import ArgvParseHelp
except:
import ifupdown.config as core_config
from ifupdown.log import log
import ifupdown.config as config
core_config.__version__ = __import__('__init__').__version__
config.__version__ = __import__("__init__").__version__
from lib.exceptions import ExitWithStatus, ExitWithStatusAndError
from ifupdown.client import Client
from ifupdown.exceptions import ArgvParseHelp
class Ifupdown2Complete(Exception):
def __init__(self, status):
self.status = status
class Ifupdown2Client:
def __init__(self, argv):
self.stdin = None
self.argv = argv
self.data = ''
self.HEADER_SIZE = 4
self.daemon_pid = -1
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
self.socket.connect('/var/run/ifupdown2d/uds')
signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler)
signal.signal(signal.SIGQUIT, self.signal_handler)
try:
self.SO_PEERCRED = socket.SO_PEERCRED
except AttributeError:
# powerpc is the only non-generic we care about. alpha, mips,
# sparc, and parisc also have non-generic values.
machine = os.uname()[4]
if re.search(r'^(ppc|powerpc)', machine):
self.SO_PASSCRED = 20
self.SO_PEERCRED = 21
else:
self.SO_PASSCRED = 16
self.SO_PEERCRED = 17
try:
self.socket.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
except Exception as e:
raise Exception('setsockopt: %s' % str(e))
except socket.error:
self.socket.close()
self.socket = None
sys.stderr.write("""
ERROR: %s could not connect to ifupdown2d
Try starting ifupdown2d with:
sudo systemctl start ifupdown2d
To configure ifupdown2d to start when the box boots:
sudo systemctl enable ifupdown2d
""" % argv[0])
def __del__(self):
if self.socket:
self.socket.close()
def signal_handler(self, sig, frame):
if self.daemon_pid > 0:
os.kill(self.daemon_pid, sig)
def read_data(self):
ready = select.select([self.socket], [], [])
if ready and ready[0] and ready[0][0] == self.socket:
d = self.socket.recv(65536)
if self.data:
self.data += d
else:
self.data = d
return True
def daemon_mode():
""" Check ifupdown2 config to see if we should start the client """
try:
with open(config.IFUPDOWN2_CONF_PATH) as f:
return "use_daemon=yes" in f.read()
except:
return False
def get_packets(self):
"""
ifupdown2 output is divided into "json packets"
the first 4 bytes is the size of the next json
object to etract
"""
data_size = len(self.data)
if not data_size:
raise Ifupdown2Complete(status=1)
packets = []
try:
while data_size > 0:
packet_len = struct.unpack('=I', self.data[:self.HEADER_SIZE])[0]
packet_data = self.data[self.HEADER_SIZE:packet_len + self.HEADER_SIZE]
fmt = "=%ds" % packet_len
packets.append(json.loads(struct.unpack(fmt, packet_data)[0]))
self.data = self.data[self.HEADER_SIZE + packet_len:]
data_size -= self.HEADER_SIZE + packet_len
except:
pass
return packets
def process_packets(self, packets):
for packet in packets:
if 'pid' in packet:
self.daemon_pid = packet['pid']
if 'stdout' in packet:
sys.stdout.write(packet['stdout'])
if 'stderr' in packet:
sys.stderr.write(packet['stderr'])
if 'status' in packet:
raise Ifupdown2Complete(packet['status'])
def run(self):
status = 1
if self.socket:
for arg in ['-i', '--interfaces']:
try:
if self.argv[self.argv.index(arg) + 1] == '-':
self.stdin = sys.stdin.read()
continue
except (ValueError, IndexError):
pass
self.socket.send(json.dumps({
'argv': self.argv,
'stdin': self.stdin
}))
try:
while True:
try:
self.read_data()
self.process_packets(self.get_packets())
except Ifupdown2Complete as e:
status = e.status
break
except Exception as e:
if ((isinstance(e.args, tuple) and e[0] == 4)
or (hasattr(e, 'errno') and e.errno == errno.EINTR)):
pass
else:
raise
except Exception as e:
sys.stderr.write(str(e))
finally:
self.socket.close()
return status if status != None else 1
def ifupdown2_standalone():
def client():
try:
import ifupdown2.ifupdown.main as main_ifupdown2
status = Client(sys.argv).run()
except ExitWithStatusAndError as e:
root_logger.error(e.message)
status = e.status
except ExitWithStatus as e:
status = e.status
return status
def stand_alone():
if not sys.argv[0].endswith("query") and os.geteuid() != 0:
sys.stderr.write('must be root to run this command\n')
return 1
try:
from ifupdown2.ifupdown.main import Ifupdown2
from ifupdown2.lib.nlcache import NetlinkListenerWithCache, NetlinkListenerWithCacheErrorNotInitialized
except:
import ifupdown.main as main_ifupdown2
ifupdown2 = main_ifupdown2.Ifupdown2(daemon=False, uid=os.geteuid())
ifupdown2.parse_argv(sys.argv)
ifupdown2.update_logger()
return ifupdown2.main()
from ifupdown.main import Ifupdown2
from lib.nlcache import NetlinkListenerWithCache, NetlinkListenerWithCacheErrorNotInitialized
ifupdown2 = Ifupdown2(daemon=False, uid=os.geteuid())
try:
ifupdown2.parse_argv(sys.argv)
LogManager.get_instance().start_standalone_logging(ifupdown2.args)
except ArgvParseHelp:
# on --help parse_args raises SystemExit, we catch it and raise a
# custom exception ArgvParseHelp to return 0
return 0
try:
status = ifupdown2.main()
finally:
try:
NetlinkListenerWithCache.get_instance().cleanup()
except NetlinkListenerWithCacheErrorNotInitialized:
status = Status.Client.STATUS_NLERROR
return status
def main():
try:
if 'use_daemon=yes' in open(core_config.IFUPDOWN2_CONF_PATH).read():
return Ifupdown2Client(sys.argv).run()
if daemon_mode():
return client()
else:
return ifupdown2_standalone()
return stand_alone()
except ArgvParseHelp:
return Status.Client.STATUS_SUCCESS
except KeyboardInterrupt:
return 1
return Status.Client.STATUS_KEYBOARD_INTERRUPT
except Exception as e:
log.error(str(e))
return 1
root_logger.exception("main: %s" % str(e))
return Status.Client.STATUS_EXCEPTION_MAIN
if __name__ == '__main__':
try:
sys.exit(main())
except KeyboardInterrupt:
sys.exit(1)
sys.exit(Status.Client.STATUS_KEYBOARD_INTERRUPT)

File diff suppressed because it is too large Load Diff

View File

@@ -6,19 +6,18 @@
import os
import glob
import subprocess
from collections import deque
from ipaddr import IPNetwork, IPv6Network
try:
from ifupdown2.lib.addon import Addon
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.nlmanager.nlpacket import Link
from ifupdown2.ifupdownaddons.cache import *
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
import ifupdown2.ifupdown.statemanager as statemanager
@@ -26,14 +25,12 @@ try:
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
except ImportError:
from lib.addon import Addon
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdown.netlink import netlink
from nlmanager.nlpacket import Link
from ifupdownaddons.cache import *
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
import ifupdown.statemanager as statemanager
@@ -42,42 +39,45 @@ except ImportError:
import ifupdown.ifupdownconfig as ifupdownconfig
class addressvirtual(moduleBase):
class addressvirtual(Addon, moduleBase):
""" ifupdown2 addon module to configure virtual addresses """
_modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
'interfaces. It creates a macvlan interface for ' +
'every mac ip address-virtual line',
'attrs' : {
'address-virtual' :
{ 'help' : 'bridge router virtual mac and ips',
'multivalue' : True,
'validvals' : ['<mac-ip/prefixlen-list>',],
'example': ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']
},
'address-virtual-ipv6-addrgen': {
'help': 'enable disable ipv6 link addrgenmode',
'validvals': ['on', 'off'],
'default': 'on',
'example': [
'address-virtual-ipv6-addrgen on',
'address-virtual-ipv6-addrgen off'
]
},
"vrrp": {
"help": "VRRP support",
"multivalue": True,
"example": [
"vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
"vrrp 42 10.0.0.42/24"
]
}
}}
_modinfo = {
"mhelp": "address module configures virtual addresses for interfaces. "
"It creates a macvlan interface for every mac ip address-virtual line",
"attrs": {
"address-virtual": {
"help": "bridge router virtual mac and ips",
"multivalue": True,
"validvals": ["<mac-ip/prefixlen-list>", ],
"example": ["address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24"]
},
"address-virtual-ipv6-addrgen": {
"help": "enable disable ipv6 link addrgenmode",
"validvals": ["on", "off"],
"default": "on",
"example": [
"address-virtual-ipv6-addrgen on",
"address-virtual-ipv6-addrgen off"
]
},
"vrrp": {
"help": "VRRP support",
"multivalue": True,
"example": [
"vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
"vrrp 42 10.0.0.42/24"
]
}
}
}
DEFAULT_IP_METRIC = 1024
ADDR_METRIC_SUPPORT = None
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
self._bridge_fdb_query_cache = {}
self.addressvirtual_with_route_metric = utils.get_boolean_from_string(
policymanager.policymanager_api.get_module_globals(
@@ -89,6 +89,27 @@ class addressvirtual(moduleBase):
self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
if addressvirtual.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()
addressvirtual.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or ''
self.logger.info('address metric support: %s' % ('OK' if addressvirtual.ADDR_METRIC_SUPPORT else 'KO'))
except Exception:
addressvirtual.ADDR_METRIC_SUPPORT = False
self.logger.info('address metric support: KO')
@classmethod
def addr_metric_support(cls):
return cls.ADDR_METRIC_SUPPORT
@classmethod
def get_default_ip_metric(cls):
return cls.DEFAULT_IP_METRIC
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
if ifaceobj.get_attr_value('address-virtual') or ifaceobj.get_attr_value("vrrp"):
ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
@@ -96,20 +117,19 @@ class addressvirtual(moduleBase):
def _get_macvlan_prefix(self, ifaceobj):
return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
@staticmethod
def get_vrrp_prefix(ifname, family):
return "vrrp%s-%s-" % (family, netlink.get_iface_index(ifname))
def get_vrrp_prefix(self, ifname, family):
return "vrrp%s-%s-" % (family, self.cache.get_ifindex(ifname))
def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
# XXX: batch the addresses
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
bridgename = ifaceobj.lowerifaces[0]
vlan = self._get_vlan_id(ifaceobj)
if self.ipcmd.bridge_is_vlan_aware(bridgename):
[self.ipcmd.bridge_fdb_add(bridgename, addr,
if self.cache.bridge_is_vlan_aware(bridgename):
[self.iproute2.bridge_fdb_add(bridgename, addr,
vlan) for addr in hwaddress]
elif self.ipcmd.is_bridge(ifaceobj.name):
[self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
elif self.cache.link_is_bridge(ifaceobj.name):
[self.iproute2.bridge_fdb_add(ifaceobj.name, addr)
for addr in hwaddress]
def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
@@ -117,17 +137,17 @@ class addressvirtual(moduleBase):
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
bridgename = ifaceobj.lowerifaces[0]
vlan = self._get_vlan_id(ifaceobj)
if self.ipcmd.bridge_is_vlan_aware(bridgename):
if self.cache.bridge_is_vlan_aware(bridgename):
for addr in hwaddress:
try:
self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
self.iproute2.bridge_fdb_del(bridgename, addr, vlan)
except Exception, e:
self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
pass
elif self.ipcmd.is_bridge(ifaceobj.name):
elif self.cache.link_is_bridge(ifaceobj.name):
for addr in hwaddress:
try:
self.ipcmd.bridge_fdb_del(ifaceobj.name, addr)
self.iproute2.bridge_fdb_del(ifaceobj.name, addr)
except Exception, e:
self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
pass
@@ -135,7 +155,7 @@ class addressvirtual(moduleBase):
def _get_bridge_fdbs(self, bridgename, vlan):
fdbs = self._bridge_fdb_query_cache.get(bridgename)
if not fdbs:
fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
fdbs = self.iproute2.bridge_fdb_show_dev(bridgename)
if not fdbs:
return
self._bridge_fdb_query_cache[bridgename] = fdbs
@@ -147,13 +167,13 @@ class addressvirtual(moduleBase):
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
bridgename = ifaceobj.lowerifaces[0]
vlan = self._get_vlan_id(ifaceobj)
if self.ipcmd.bridge_is_vlan_aware(bridgename):
if self.cache.bridge_is_vlan_aware(bridgename):
fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
if not fdb_addrs:
return False
hwaddress_int = self.ipcmd.mac_str_to_int(hwaddress)
hwaddress_int = utils.mac_str_to_int(hwaddress)
for mac in fdb_addrs:
if self.ipcmd.mac_str_to_int(mac) == hwaddress_int:
if utils.mac_str_to_int(mac) == hwaddress_int:
return True
return False
return True
@@ -182,29 +202,24 @@ class addressvirtual(moduleBase):
route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
vrf_master = self.ipcmd.link_get_master(ifaceobj.name)
vrf_master = self.cache.get_master(ifaceobj.name)
else:
vrf_master = None
dev = self.ipcmd.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
dev = self.iproute2.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
if dev and dev != ifaceobj.name:
self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
'seems to be of the macvlan dev %s'
%vifacename +
' .. flapping macvlan dev to fix entry.')
self.ipcmd.link_down(vifacename)
self.ipcmd.link_up(vifacename)
self.iproute2.link_down(vifacename)
self.iproute2.link_up(vifacename)
except Exception, e:
self.logger.debug('%s: fixing route entry failed (%s)'
% (ifaceobj.name, str(e)))
pass
def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
vrfname = self.ipcmd.link_get_master(ifaceobj.name)
if vrfname:
self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
def _get_macs_from_old_config(self, ifaceobj=None):
""" This method returns a list of the mac addresses
in the address-virtual attribute for the bridge. """
@@ -251,10 +266,10 @@ class addressvirtual(moduleBase):
return False, None
def _remove_running_address_config(self, ifaceobj):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
hwaddress = []
self.ipcmd.batch_start()
for macvlan_prefix in [
self._get_macvlan_prefix(ifaceobj),
self.get_vrrp_prefix(ifaceobj.name, "4"),
@@ -262,13 +277,13 @@ class addressvirtual(moduleBase):
]:
for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % macvlan_prefix):
macvlan_ifacename = os.path.basename(macvlan_ifacename)
if not self.ipcmd.link_exists(macvlan_ifacename):
if not self.cache.link_exists(macvlan_ifacename):
continue
hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
self.netlink.link_del(os.path.basename(macvlan_ifacename))
# XXX: Also delete any fdb addresses. This requires, checking mac address
# on individual macvlan interfaces and deleting the vlan from that.
self.ipcmd.batch_commit()
if any(hwaddress):
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
@@ -277,28 +292,20 @@ class addressvirtual(moduleBase):
self._remove_running_address_config(ifaceobj)
return
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
hwaddress = []
self.ipcmd.batch_start()
av_idx = 0
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
for av in address_virtual_list:
av_attrs = av.split()
if len(av_attrs) < 2:
self.log_error("%s: incorrect address-virtual attrs '%s'"
%(ifaceobj.name, av), ifaceobj,
raise_error=False)
av_idx += 1
continue
# Delete the macvlan device on this device
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
self.netlink.link_del(os.path.basename(macvlan_ifacename))
if av_attrs[0] != 'None':
hwaddress.append(av_attrs[0])
av_idx += 1
self.ipcmd.batch_commit()
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
def check_mac_address(self, ifaceobj, mac):
@@ -325,12 +332,12 @@ class addressvirtual(moduleBase):
if not ifaceobj_getfunc:
return
if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
self.ipcmd.link_exists(ifaceobj.name)):
self.cache.link_exists(ifaceobj.name)):
# if I am a vrf device and I have slaves
# that have address virtual config,
# enslave the slaves 'address virtual
# interfaces (macvlans)' to myself:
running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
if running_slaves:
# pick up any existing slaves of a vrf device and
# look for their upperdevices and enslave them to the
@@ -341,7 +348,7 @@ class addressvirtual(moduleBase):
(sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
# enslave all its upper devices to
# the vrf device
upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name)
upperdevs = self.sysfs.link_get_uppers(sobjs[0].name)
if not upperdevs:
continue
for u in upperdevs:
@@ -350,18 +357,18 @@ class addressvirtual(moduleBase):
# upper device list
if u == ifaceobj.name:
continue
self.ipcmd.link_set(u, 'master', ifaceobj.name,
state='up')
self.netlink.link_set_master(u, ifaceobj.name)
self.netlink.link_up(u)
elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
(ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
self.ipcmd.link_exists(ifaceobj.name)):
self.cache.link_exists(ifaceobj.name)):
# If I am a vrf slave and I have 'address virtual'
# config, make sure my addrress virtual interfaces
# (macvlans) are also enslaved to the vrf device
vrfname = ifaceobj.get_attr_value_first('vrf')
if not vrfname or not self.ipcmd.link_exists(vrfname):
if not vrfname or not self.cache.link_exists(vrfname):
return
running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name)
running_uppers = self.sysfs.link_get_uppers(ifaceobj.name)
if not running_uppers:
return
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
@@ -371,10 +378,11 @@ class addressvirtual(moduleBase):
if u == vrfname:
continue
if u.startswith(macvlan_prefix):
self.ipcmd.link_set(u, 'master', vrfname,
state='up')
self.netlink.link_set_master(u, vrfname)
self.netlink.link_up(u)
def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False):
"""
intf_config_list = [
{
@@ -384,17 +392,25 @@ class addressvirtual(moduleBase):
},
]
"""
hw_address_list = []
if not intf_config_list:
return hw_address_list
user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj)
purge_existing = False if ifupdownflags.flags.PERFMODE else True
hw_address_list = []
ifname = ifaceobj.name
lower_iface_mtu = update_mtu = None
update_mtu = lower_iface_mtu = lower_iface_mtu_str = None
if ifupdownconfig.config.get("adjust_logical_dev_mtu", "1") != "0":
if ifaceobj.lowerifaces and intf_config_list:
update_mtu = True
self.ipcmd.batch_start()
if update_mtu:
lower_iface_mtu = self.cache.get_link_mtu(ifaceobj.name)
lower_iface_mtu_str = str(lower_iface_mtu)
self.iproute2.batch_start() # TODO: make sure we only do 1 ip link set down and set up (only one flap in the batch)
for intf_config_dict in intf_config_list:
link_created = False
@@ -403,19 +419,27 @@ class addressvirtual(moduleBase):
macvlan_mode = intf_config_dict.get("mode")
ips = intf_config_dict.get("ips")
if not self.ipcmd.link_exists(macvlan_ifname):
self.ipcmd.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
if not self.cache.link_exists(macvlan_ifname):
# When creating VRRP macvlan with bridge mode, the kernel
# return an error: 'Invalid argument' (22)
# so for now we should only use the iproute2 API.
# try:
# self.netlink.link_add_macvlan(ifname, macvlan_ifname)
# except:
self.iproute2.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
link_created = True
# first thing we need to handle vrf enslavement
if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
self._handle_vrf_slaves(macvlan_ifname, ifaceobj)
vrf_ifname = self.cache.get_master(ifaceobj.name)
if vrf_ifname:
self.iproute2.link_set_master(macvlan_ifname, vrf_ifname)
# if we are dealing with a VRRP macvlan we need to set addrgenmode
# to RANDOM, and protodown on
if vrrp:
try:
self.ipcmd.ipv6_addrgen(
self.iproute2.link_set_ipv6_addrgen(
macvlan_ifname,
Link.IN6_ADDR_GEN_MODE_RANDOM,
link_created
@@ -425,22 +449,26 @@ class addressvirtual(moduleBase):
"operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
try:
if link_created:
netlink.link_set_protodown(macvlan_ifname, "on")
self.netlink.link_set_protodown_on(macvlan_ifname)
except Exception as e:
self.logger.warning("%s: %s: ip link set dev %s protodown on: operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
elif user_configured_ipv6_addrgenmode:
self.ipcmd.ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
self.iproute2.link_set_ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
if macvlan_hwaddr:
self.ipcmd.link_set_hwaddress(macvlan_ifname, macvlan_hwaddr, keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN)
self.iproute2.link_set_address_and_keep_down(
macvlan_ifname,
macvlan_hwaddr,
keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN
)
hw_address_list.append(macvlan_hwaddr)
if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support():
metric = self.ipcmd.get_default_ip_metric()
if self.addressvirtual_with_route_metric and self.addr_metric_support():
metric = self.get_default_ip_metric()
else:
metric = None
self.ipcmd.addr_add_multiple(
self.iproute2.add_addresses(
ifaceobj,
macvlan_ifname,
ips,
@@ -450,53 +478,58 @@ class addressvirtual(moduleBase):
if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
self.logger.info("%s: keeping macvlan down - link-down yes on lower device %s" % (macvlan_ifname, ifname))
netlink.link_set_updown(macvlan_ifname, "down")
self.netlink.link_down(macvlan_ifname)
# If link existed before, flap the link
if not link_created:
if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
# if the system doesn't support ip addr set METRIC
# we need to do manually check the ordering of the ip4 routes
self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
if update_mtu:
lower_iface_mtu = self.ipcmd.link_get_mtu(ifname, refresh=True)
update_mtu = False
if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifname, refresh=True):
try:
self.ipcmd.link_set_mtu(macvlan_ifname,
lower_iface_mtu)
self.sysfs.link_set_mtu(macvlan_ifname, mtu_str=lower_iface_mtu_str, mtu_int=lower_iface_mtu)
except Exception as e:
self.logger.info('%s: failed to set mtu %s: %s' %
(macvlan_ifname, lower_iface_mtu, e))
self.logger.info('%s: failed to set mtu %s: %s' % (macvlan_ifname, lower_iface_mtu, e))
# set macvlan device to up in anycase.
# since we auto create them here..we are responsible
# to bring them up here in the case they were brought down
# by some other entity in the system.
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
netlink.link_set_updown(macvlan_ifname, "up")
self.netlink.link_up(macvlan_ifname)
else:
try:
if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
# if the system doesn't support ip addr set METRIC
# we need to do manually check the ordering of the ip6 routes
self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
self.iproute2.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
except Exception as e:
self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
# Disable IPv6 duplicate address detection on VRR interfaces
sysctl_prefix = "net.ipv6.conf.%s" % macvlan_ifname
try:
syskey = "%s.%s" % (sysctl_prefix, "enhanced_dad")
if self.sysctl_get(syskey) != "0":
self.sysctl_set(syskey, "0")
except Exception as e:
self.logger.info("sysctl failure: operation not supported: %s" % str(e))
for key, sysval in {
"accept_dad": "0",
"dad_transmits": "0"
}.iteritems():
syskey = "net.ipv6.conf.%s.%s" % (macvlan_ifname, key)
syskey = "%s.%s" % (sysctl_prefix, key)
if self.sysctl_get(syskey) != sysval:
self.sysctl_set(syskey, sysval)
self.ipcmd.batch_commit()
self.iproute2.batch_commit()
return hw_address_list
def _up(self, ifaceobj, ifaceobj_getfunc=None):
@@ -518,7 +551,7 @@ class addressvirtual(moduleBase):
"with no upper interfaces or parent interfaces)"
% ifaceobj.name, ifaceobj)
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
addr_virtual_macs = self.create_macvlan_and_apply_config(
@@ -597,7 +630,7 @@ class addressvirtual(moduleBase):
if ip4 or ifquery:
merged_with_existing_obj = False
macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
macvlan_ip4_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip4_mac)
macvlan_ip4_mac_int = utils.mac_str_to_int(macvlan_ip4_mac)
# if the vrr config is defined in different lines for the same ID
# we need to save the ip4 and ip6 in the objects we previously
# created, example:
@@ -622,13 +655,13 @@ class addressvirtual(moduleBase):
elif not ip4 and not ifquery:
# special check to see if all ipv4 were removed from the vrrp
# configuration, if so we need to remove the associated macvlan
if self.ipcmd.link_exists(macvlan_ip4_ifname):
netlink.link_del(macvlan_ip4_ifname)
if self.cache.link_exists(macvlan_ip4_ifname):
self.netlink.link_del(macvlan_ip4_ifname)
if ip6 or ifquery:
merged_with_existing_obj = False
macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
macvlan_ip6_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip6_mac)
macvlan_ip6_mac_int = utils.mac_str_to_int(macvlan_ip6_mac)
# if the vrr config is defined in different lines for the same ID
# we need to save the ip4 and ip6 in the objects we previously
# created, example:
@@ -654,8 +687,8 @@ class addressvirtual(moduleBase):
elif not ip6 and not ifquery:
# special check to see if all ipv6 were removed from the vrrp
# configuration, if so we need to remove the associated macvlan
if self.ipcmd.link_exists(macvlan_ip6_ifname):
netlink.link_del(macvlan_ip6_ifname)
if self.cache.link_exists(macvlan_ip6_ifname):
self.netlink.link_del(macvlan_ip6_ifname)
if not ifquery:
# check if vrrp attribute was removed/re-assigned
@@ -681,11 +714,11 @@ class addressvirtual(moduleBase):
macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), id_to_remove)
macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), id_to_remove)
if self.ipcmd.link_exists(macvlan_ip4_ifname):
netlink.link_del(macvlan_ip4_ifname)
if self.cache.link_exists(macvlan_ip4_ifname):
self.netlink.link_del(macvlan_ip4_ifname)
if self.ipcmd.link_exists(macvlan_ip6_ifname):
netlink.link_del(macvlan_ip6_ifname)
if self.cache.link_exists(macvlan_ip6_ifname):
self.netlink.link_del(macvlan_ip6_ifname)
except Exception as e:
self.logger.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname)
@@ -714,13 +747,6 @@ class addressvirtual(moduleBase):
for index, addr_virtual in enumerate(address_virtual_list):
av_attrs = addr_virtual.split()
if len(av_attrs) < 2:
self.log_error("%s: incorrect address-virtual attrs '%s'"
% (ifaceobj.name, addr_virtual), ifaceobj,
raise_error=False)
continue
mac = av_attrs[0]
if mac:
mac = mac.lower()
@@ -735,7 +761,7 @@ class addressvirtual(moduleBase):
if mac != "none":
config["hwaddress"] = mac
config["hwaddress_int"] = self.ipcmd.mac_str_to_int(mac)
config["hwaddress_int"] = utils.mac_str_to_int(mac)
ip_network_obj_list = []
for ip in av_attrs[1:]:
@@ -746,9 +772,6 @@ class addressvirtual(moduleBase):
return user_config_list
def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
def _down(self, ifaceobj, ifaceobj_getfunc=None):
try:
self._remove_address_config(ifaceobj,
@@ -756,17 +779,15 @@ class addressvirtual(moduleBase):
#### VRR
hwaddress = []
self.ipcmd.batch_start()
for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
macvlan_ifacename = os.path.basename(macvlan_ifacename)
if not self.ipcmd.link_exists(macvlan_ifacename):
if not self.cache.link_exists(macvlan_ifacename):
continue
hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
self.netlink.link_del(macvlan_ifacename)
# XXX: Also delete any fdb addresses. This requires, checking mac address
# on individual macvlan interfaces and deleting the vlan from that.
self.ipcmd.batch_commit()
if any(hwaddress):
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
except Exception, e:
@@ -776,7 +797,7 @@ class addressvirtual(moduleBase):
def _query_check(self, ifaceobj, ifaceobjcurr):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
@@ -822,6 +843,25 @@ class addressvirtual(moduleBase):
return
ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
@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(obj)
else:
ip4.append(obj)
running_ipobj = []
for ip in running_addrs or []:
running_ipobj.append(IPNetwork(ip))
return running_ipobj == (ip4 + ip6)
def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
"""
macvlan_config_list = [
@@ -853,7 +893,7 @@ class addressvirtual(moduleBase):
macvlan_ifacename = config.get("ifname")
if not self.ipcmd.link_exists(macvlan_ifacename):
if not self.cache.link_exists(macvlan_ifacename):
ifaceobjcurr.update_config_with_status(attr_name, "", 1)
continue
@@ -861,26 +901,26 @@ class addressvirtual(moduleBase):
macvlan_hwaddress_int = config.get("hwaddress_int")
if user_config_address_virtual_ipv6_addr:
macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
macvlans_running_ipv6_addr.append(self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename))
# Check mac and ip address
rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
rhwaddress = ip4_macvlan_hwaddress = self.cache.get_link_address(macvlan_ifacename)
raddrs = ip4_running_addrs =[str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
ifname=macvlan_ifacename,
details=False,
addr_virtual_ifaceobj=ifaceobj
)
ifaceobj_list=[ifaceobj],
with_address_virtual=True
)]
if not is_vrr:
ips = config.get("ips")
if not raddrs or not rhwaddress:
if not rhwaddress:
ifaceobjcurr.update_config_with_status(attr_name, "", 1)
continue
try:
if self.ipcmd.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
if utils.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
and self.compare_user_config_vs_running_state(raddrs, ips) \
and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
ifaceobjcurr.update_config_with_status(
attr_name,
@@ -909,24 +949,25 @@ class addressvirtual(moduleBase):
ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
# check macvlan ip6 hwaddress (only if ip6 were provided by the user)
if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
if not ip6_config.get("ips") or self.cache.get_link_address_raw(ip6_macvlan_ifname) == ip6_config.get("hwaddress_int"):
# check all ip4
if self.ipcmd.compare_user_config_vs_running_state(
if self.compare_user_config_vs_running_state(
ip4_running_addrs,
ip4_config.get("ips")
) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
ip6_running_addrs = self.ipcmd.get_running_addrs(
ip6_running_addrs = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
ifname=ip6_macvlan_ifname,
details=False,
addr_virtual_ifaceobj=ifaceobj
)
ifaceobj_list=[ifaceobj],
with_address_virtual=True
)]
# check all ip6
if self.ipcmd.compare_user_config_vs_running_state(
if self.compare_user_config_vs_running_state(
ip6_running_addrs,
ip6_config.get("ips")
) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
) and self._check_addresses_in_bridge(ifaceobj, ip6_macvlan_hwaddress):
ifaceobjcurr.update_config_with_status(
attr_name,
"%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
@@ -945,19 +986,16 @@ class addressvirtual(moduleBase):
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
address_virtuals = []
for av in linkCache.links:
if av.startswith(macvlan_prefix):
address_virtuals.append(av)
address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
macvlans_ipv6_addrgen_list = []
for av in address_virtuals:
macvlan_ifacename = os.path.basename(av)
rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
raddress = []
for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
rhwaddress = self.cache.get_link_address(macvlan_ifacename)
raddress = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
ifaceobj_list=ifaceobj_getfunc(ifaceobjrunning.name) or [],
ifname=ifaceobjrunning.name,
with_address_virtual=True
)]
raddress = list(set(raddress))
@@ -968,7 +1006,7 @@ class addressvirtual(moduleBase):
ifaceobjrunning.update_config('address-virtual',
'%s %s' %(rhwaddress, ' '.join(raddress)))
macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename)))
macvlan_count = len(address_virtuals)
if not macvlan_count:
@@ -983,19 +1021,17 @@ class addressvirtual(moduleBase):
return
ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
_run_ops = {'up' : _up,
'down' : _down,
'query-checkcurr' : _query_check,
'query-running' : _query_running}
_run_ops = {
'up': _up,
'down': _down,
'query-checkcurr': _query_check,
'query-running': _query_running
}
def get_ops(self):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None,
ifaceobj_getfunc=None, **extra_args):
@@ -1019,7 +1055,7 @@ class addressvirtual(moduleBase):
op_handler = self._run_ops.get(operation)
if not op_handler:
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -5,20 +5,20 @@
#
try:
from ifupdown2.lib.addon import Addon
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.ifupdown.exceptions import moduleNotSupported
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
except:
from lib.addon import Addon
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdownaddons.modulebase import moduleBase
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdown.netlink import netlink
from ifupdown.exceptions import moduleNotSupported
import ifupdown.ifupdownflags as ifupdownflags
@@ -27,62 +27,56 @@ import re
import subprocess
import os
class batman_adv (moduleBase):
class batman_adv(Addon, moduleBase):
""" ifupdown2 addon module to configure B.A.T.M.A.N. advanced interfaces """
_modinfo = {
'mhelp' : 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
'interface to be creatable. You can specify a space separated list' +
'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
'is set for an interfaces this module will do the magic.',
'attrs' : {
'batman-ifaces' : {
'help' : 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
'validvals' : [ '<interface-list>' ],
'required' : True,
'mhelp': 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
'interface to be creatable. You can specify a space separated list' +
'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
'is set for an interfaces this module will do the magic.',
'attrs': {
'batman-ifaces': {
'help': 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
'validvals': ['<interface-list>'],
'required': True,
},
'batman-ifaces-ignore-regex' : {
'help' : 'Interfaces to ignore when verifying configuration (regexp)',
'required' : False,
'batman-ifaces-ignore-regex': {
'help': 'Interfaces to ignore when verifying configuration (regexp)',
'required': False,
},
'batman-distributed-arp-table' : {
'help' : 'B.A.T.M.A.N. distributed ARP table',
'validvals' : [ 'enabled', 'disabled' ],
'required' : False,
'batman-attr' : True,
'batman-distributed-arp-table': {
'help': 'B.A.T.M.A.N. distributed ARP table',
'validvals': ['enabled', 'disabled'],
'required': False,
'batman-attr': True,
},
'batman-gw-mode' : {
'help' : 'B.A.T.M.A.N. gateway mode',
'validvals' : [ 'off', 'client', 'server' ],
'required' : False,
'example' : [ 'batman-gw-mode client' ],
'batman-attr' : True,
'batman-gw-mode': {
'help': 'B.A.T.M.A.N. gateway mode',
'validvals': ['off', 'client', 'server'],
'required': False,
'example': ['batman-gw-mode client'],
'batman-attr': True,
},
'batman-hop-penalty' : {
'help' : 'B.A.T.M.A.N. hop penalty',
'validvals' : [ '<number>' ],
'required' : False,
'batman-attr' : True,
'batman-hop-penalty': {
'help': 'B.A.T.M.A.N. hop penalty',
'validvals': ['<number>'],
'required': False,
'batman-attr': True,
},
'batman-multicast-mode' : {
'help' : 'B.A.T.M.A.N. multicast mode',
'validvals' : [ 'enabled', 'disabled' ],
'required' : False,
'batman-attr' : True,
'batman-multicast-mode': {
'help': 'B.A.T.M.A.N. multicast mode',
'validvals': ['enabled', 'disabled'],
'required': False,
'batman-attr': True,
},
'batman-routing-algo' : {
'help' : 'B.A.T.M.A.N. routing algo',
'validvals' : [ 'BATMAN_IV', 'BATMAN_V' ],
'required' : False,
'batman-attr' : False,
'batman-routing-algo': {
'help': 'B.A.T.M.A.N. routing algo',
'validvals': ['BATMAN_IV', 'BATMAN_V'],
'required': False,
'batman-attr': False,
},
}
}
@@ -90,12 +84,11 @@ class batman_adv (moduleBase):
_batman_attrs = {
}
def __init__ (self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__ (self, *args, **kargs)
if not os.path.exists('/usr/sbin/batctl'):
raise moduleNotSupported('module init failed: no /usr/sbin/batctl found')
self.ipcmd = None
for longname, entry in self._modinfo['attrs'].items ():
if entry.get ('batman-attr', False) == False:
@@ -106,27 +99,23 @@ class batman_adv (moduleBase):
'filename' : attr.replace ("-", "_"),
}
def _is_batman_device (self, ifaceobj):
if ifaceobj.get_attr_value_first ('batman-ifaces'):
return True
return False
def _get_batman_ifaces (self, ifaceobj ):
batman_ifaces = ifaceobj.get_attr_value_first ('batman-ifaces')
if batman_ifaces:
return sorted (batman_ifaces.split ())
return None
def _get_batman_ifaces_ignore_regex (self, ifaceobj):
ifaces_ignore_regex = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
if ifaces_ignore_regex:
return re.compile (r"%s" % ifaces_ignore_regex)
return None
def _get_batman_attr (self, ifaceobj, attr):
if attr not in self._batman_attrs:
raise ValueError ("_get_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
@@ -137,7 +126,6 @@ class batman_adv (moduleBase):
return None
def _read_current_batman_attr (self, ifaceobj, attr, dont_map = False):
# 'routing_algo' needs special handling, D'oh.
if dont_map:
@@ -156,7 +144,6 @@ class batman_adv (moduleBase):
except ValueError:
raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value)
def _set_batman_attr (self, ifaceobj, attr, value):
if attr not in self._batman_attrs:
raise ValueError ("_set_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
@@ -168,7 +155,6 @@ class batman_adv (moduleBase):
except IOError as i:
raise Exception ("_set_batman_attr (%s): %s" % (attr, i))
def _batctl_if (self, bat_iface, mesh_iface, op):
if op not in [ 'add', 'del' ]:
raise Exception ("_batctl_if() called with invalid \"op\" value: %s" % op)
@@ -193,7 +179,6 @@ class batman_adv (moduleBase):
except Exception as e:
raise Exception ("_set_routing_algo: %s" % e)
def _find_member_ifaces (self, ifaceobj, ignore = True):
members = []
iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
@@ -208,7 +193,6 @@ class batman_adv (moduleBase):
return sorted (members)
def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
if not self._is_batman_device (ifaceobj):
return None
@@ -220,7 +204,6 @@ class batman_adv (moduleBase):
return [None]
def _up (self, ifaceobj):
if self._get_batman_ifaces (ifaceobj) == None:
raise Exception ('could not determine batman interfacaes')
@@ -228,7 +211,7 @@ class batman_adv (moduleBase):
# Verify existance of batman interfaces (should be present already)
batman_ifaces = []
for iface in self._get_batman_ifaces (ifaceobj):
if not self.ipcmd.link_exists (iface):
if not self.cache.link_exists (iface):
self.logger.warn ('batman iface %s not present' % iface)
continue
@@ -241,10 +224,9 @@ class batman_adv (moduleBase):
if routing_algo:
self._set_routing_algo (routing_algo)
if_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
# Is the batman main interface already present?
if self.ipcmd.link_exists (ifaceobj.name):
if self.cache.link_exists (ifaceobj.name):
# Verify which member interfaces are present
members = self._find_member_ifaces (ifaceobj)
for iface in members:
@@ -269,9 +251,8 @@ class batman_adv (moduleBase):
netlink.link_set_updown(ifaceobj.name, "up")
def _down (self, ifaceobj):
if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists (ifaceobj.name):
return
members = self._find_member_ifaces (ifaceobj)
@@ -283,7 +264,7 @@ class batman_adv (moduleBase):
def _query_check (self, ifaceobj, ifaceobjcurr):
if not self.ipcmd.link_exists (ifaceobj.name):
if not self.cache.link_exists (ifaceobj.name):
return
batman_ifaces_cfg = self._get_batman_ifaces (ifaceobj)
@@ -342,30 +323,16 @@ class batman_adv (moduleBase):
ifaceobjcurr.update_config_with_status ('batman-routing-algo', value_curr, value_ok)
def _query_running (self, ifaceobjrunning):
if not self.ipcmd.link_exists (ifaceobjrunning.name):
return
# XXX Now what?
_run_ops = {'pre-up' : _up,
'post-down' : _down,
'query-checkcurr' : _query_check}
# XXX 'query-running' : _query_running}
_run_ops = {
'pre-up': _up,
'post-down': _down,
'query-checkcurr': _query_check
}
def get_ops (self):
""" returns list of ops supported by this module """
return self._run_ops.keys ()
def _init_command_handlers (self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
""" run B.A.T.M.A.N. configuration on the interface object passed as argument
@@ -389,8 +356,6 @@ class batman_adv (moduleBase):
if (operation != 'query-running' and not self._is_batman_device (ifaceobj)):
return
self._init_command_handlers ()
if operation == 'query-checkcurr':
op_handler (self, ifaceobj, query_ifaceobj)
else:

View File

@@ -11,136 +11,162 @@ import os
from sets import Set
try:
from ifupdown2.lib.addon import Addon
from ifupdown2.nlmanager.nlmanager import Link
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
import ifupdown2.ifupdown.policymanager as policymanager
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
except ImportError:
from lib.addon import Addon
from nlmanager.nlmanager import Link
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdown.netlink import netlink
from ifupdown.statemanager import statemanager_api as statemanager
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
import ifupdown.policymanager as policymanager
import ifupdown.ifupdownflags as ifupdownflags
class bond(moduleBase):
class bond(Addon, moduleBase):
""" ifupdown2 addon module to configure bond interfaces """
overrides_ifupdown_scripts = ['ifenslave', ]
_modinfo = { 'mhelp' : 'bond configuration module',
'attrs' : {
'bond-use-carrier':
{'help' : 'bond use carrier',
'validvals' : ['yes', 'no', '0', '1'],
'default' : 'yes',
'example': ['bond-use-carrier yes']},
'bond-num-grat-arp':
{'help' : 'bond use carrier',
'validrange' : ['0', '255'],
'default' : '1',
'example' : ['bond-num-grat-arp 1']},
'bond-num-unsol-na' :
{'help' : 'bond slave devices',
'validrange' : ['0', '255'],
'default' : '1',
'example' : ['bond-num-unsol-na 1']},
'bond-xmit-hash-policy' :
{'help' : 'bond slave devices',
'validvals' : ['0', 'layer2',
'1', 'layer3+4',
'2', 'layer2+3',
'3', 'encap2+3',
'4', 'encap3+4'],
'default' : 'layer2',
'example' : ['bond-xmit-hash-policy layer2']},
'bond-miimon' :
{'help' : 'bond miimon',
'validrange' : ['0', '255'],
'default' : '0',
'example' : ['bond-miimon 0']},
'bond-mode' :
{'help': 'bond mode',
'validvals': ['0', 'balance-rr',
'1', 'active-backup',
'2', 'balance-xor',
'3', 'broadcast',
'4', '802.3ad',
'5', 'balance-tlb',
'6', 'balance-alb'],
'default': 'balance-rr',
'example': ['bond-mode 802.3ad']},
'bond-lacp-rate':
{'help' : 'bond lacp rate',
'validvals' : ['0', 'slow', '1', 'fast'],
'default' : '0',
'example' : ['bond-lacp-rate 0']},
'bond-min-links':
{'help' : 'bond min links',
'default' : '0',
'validrange' : ['0', '255'],
'example' : ['bond-min-links 0']},
'bond-ad-sys-priority':
{'help' : '802.3ad system priority',
'default' : '65535',
'validrange' : ['0', '65535'],
'example' : ['bond-ad-sys-priority 65535'],
'deprecated' : True,
'new-attribute' : 'bond-ad-actor-sys-prio'},
'bond-ad-actor-sys-prio':
{'help' : '802.3ad system priority',
'default' : '65535',
'validrange' : ['0', '65535'],
'example' : ['bond-ad-actor-sys-prio 65535']},
'bond-ad-sys-mac-addr':
{'help' : '802.3ad system mac address',
'validvals': ['<mac>', ],
'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00'],
'deprecated' : True,
'new-attribute' : 'bond-ad-actor-system'},
'bond-ad-actor-system':
{'help' : '802.3ad system mac address',
'validvals': ['<mac>', ],
'example' : ['bond-ad-actor-system 00:00:00:00:00:00'],},
'bond-lacp-bypass-allow':
{'help' : 'allow lacp bypass',
'validvals' : ['yes', 'no', '0', '1'],
'default' : 'no',
'example' : ['bond-lacp-bypass-allow no']},
'bond-slaves' :
{'help' : 'bond slaves',
'required' : True,
'multivalue' : True,
'validvals': ['<interface-list>'],
'example' : ['bond-slaves swp1 swp2',
'bond-slaves glob swp1-2',
'bond-slaves regex (swp[1|2)'],
'aliases': ['bond-ports']},
'bond-updelay' :
{'help' : 'bond updelay',
'default' : '0',
'validrange' : ['0', '65535'],
'example' : ['bond-updelay 100']},
'bond-downdelay':
{'help' : 'bond downdelay',
'default' : '0',
'validrange' : ['0', '65535'],
'example' : ['bond-downdelay 100']}
}}
_modinfo = {
"mhelp": "bond configuration module",
"attrs": {
"bond-use-carrier": {
"help": "bond use carrier",
"validvals": ["yes", "no", "0", "1"],
"default": "yes",
"example": ["bond-use-carrier yes"]},
"bond-num-grat-arp": {
"help": "bond use carrier",
"validrange": ["0", "255"],
"default": "1",
"example": ["bond-num-grat-arp 1"]
},
"bond-num-unsol-na": {
"help": "bond slave devices",
"validrange": ["0", "255"],
"default": "1",
"example": ["bond-num-unsol-na 1"]
},
"bond-xmit-hash-policy": {
"help": "bond slave devices",
"validvals": [
"0", "layer2",
"1", "layer3+4",
"2", "layer2+3",
"3", "encap2+3",
"4", "encap3+4"
],
"default": "layer2",
"example": ["bond-xmit-hash-policy layer2"]
},
"bond-miimon": {
"help": "bond miimon",
"validrange": ["0", "255"],
"default": "0",
"example": ["bond-miimon 0"]
},
"bond-mode": {
"help": "bond mode",
"validvals": [
"0", "balance-rr",
"1", "active-backup",
"2", "balance-xor",
"3", "broadcast",
"4", "802.3ad",
"5", "balance-tlb",
"6", "balance-alb"
],
"default": "balance-rr",
"example": ["bond-mode 802.3ad"]
},
"bond-lacp-rate": {
"help": "bond lacp rate",
"validvals": ["0", "slow", "1", "fast"],
"default": "0",
"example": ["bond-lacp-rate 0"]
},
"bond-min-links": {
"help": "bond min links",
"default": "0",
"validrange": ["0", "255"],
"example": ["bond-min-links 0"]
},
"bond-ad-sys-priority": {
"help": "802.3ad system priority",
"default": "65535",
"validrange": ["0", "65535"],
"example": ["bond-ad-sys-priority 65535"],
"deprecated": True,
"new-attribute": "bond-ad-actor-sys-prio"
},
"bond-ad-actor-sys-prio": {
"help": "802.3ad system priority",
"default": "65535",
"validrange": ["0", "65535"],
"example": ["bond-ad-actor-sys-prio 65535"]
},
"bond-ad-sys-mac-addr": {
"help": "802.3ad system mac address",
"validvals": ["<mac>", ],
"example": ["bond-ad-sys-mac-addr 00:00:00:00:00:00"],
"deprecated": True,
"new-attribute": "bond-ad-actor-system"
},
"bond-ad-actor-system": {
"help": "802.3ad system mac address",
"validvals": ["<mac>", ],
"example": ["bond-ad-actor-system 00:00:00:00:00:00"],
},
"bond-lacp-bypass-allow": {
"help": "allow lacp bypass",
"validvals": ["yes", "no", "0", "1"],
"default": "no",
"example": ["bond-lacp-bypass-allow no"]
},
"bond-slaves": {
"help": "bond slaves",
"required": True,
"multivalue": True,
"validvals": ["<interface-list>"],
"example": [
"bond-slaves swp1 swp2",
"bond-slaves glob swp1-2",
"bond-slaves regex (swp[1|2)"
],
"aliases": ["bond-ports"]
},
"bond-updelay": {
"help": "bond updelay",
"default": "0",
"validrange": ["0", "65535"],
"example": ["bond-updelay 100"]
},
"bond-downdelay": {
"help": "bond downdelay",
"default": "0",
"validrange": ["0", "65535"],
"example": ["bond-downdelay 100"]
},
"bond-primary": {
"help": "Control which slave interface is "
"preferred active member",
"example": ["bond-primary swp1"]
}
}
}
_bond_attr_netlink_map = {
'bond-mode': Link.IFLA_BOND_MODE,
@@ -157,7 +183,8 @@ class bond(moduleBase):
'bond-ad-actor-sys-prio': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
'bond-updelay': Link.IFLA_BOND_UPDELAY,
'bond-downdelay': Link.IFLA_BOND_DOWNDELAY
'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
'bond-primary': Link.IFLA_BOND_PRIMARY
}
# ifquery-check attr dictionary with callable object to translate user data to netlink format
@@ -173,7 +200,8 @@ class bond(moduleBase):
Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: int,
Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
Link.IFLA_BOND_UPDELAY: int,
Link.IFLA_BOND_DOWNDELAY: int
Link.IFLA_BOND_DOWNDELAY: int,
# Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
}
# ifup attr list with callable object to translate user data to netlink format
@@ -194,13 +222,13 @@ class bond(moduleBase):
('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str)
# ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
)
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
self.bondcmd = None
if not os.path.exists('/sys/class/net/bonding_masters'):
try:
@@ -208,6 +236,9 @@ class bond(moduleBase):
except Exception as e:
self.logger.info("bond: error while loading bonding module: %s" % str(e))
self._bond_attr_ifquery_check_translate_func[Link.IFLA_BOND_PRIMARY] = self.cache.get_ifindex
self._bond_attr_set_list = self._bond_attr_set_list + (('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex),)
@staticmethod
def get_bond_slaves(ifaceobj):
slaves = ifaceobj.get_attr_value_first('bond-slaves')
@@ -218,9 +249,7 @@ class bond(moduleBase):
def _is_bond(self, ifaceobj):
# at first link_kind is not set but once ifupdownmain
# calls get_dependent_ifacenames link_kind is set to BOND
if ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj):
return True
return False
return ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj)
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
""" Returns list of interfaces dependent on ifaceobj """
@@ -244,8 +273,7 @@ class bond(moduleBase):
return self.syntax_check_updown_delay(ifaceobj)
def get_dependent_ifacenames_running(self, ifaceobj):
self._init_command_handlers()
return self.bondcmd.bond_get_slaves(ifaceobj.name)
return self.cache.get_slaves(ifaceobj.name)
def _get_slave_list(self, ifaceobj):
""" Returns slave list present in ifaceobj config """
@@ -264,8 +292,10 @@ class bond(moduleBase):
If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
"""
try:
if os.path.exists("/sys/class/net/%s/brport" % ifname):
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
return
except Exception, e:
self.logger.info(str(e))
@@ -276,46 +306,44 @@ class bond(moduleBase):
return True
return False
def _add_slaves(self, ifaceobj, ifaceobj_getfunc=None):
runningslaves = []
def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None):
slaves = self._get_slave_list(ifaceobj)
if not slaves:
self.logger.debug('%s: no slaves found' %ifaceobj.name)
return
if not ifupdownflags.flags.PERFMODE:
runningslaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
clag_bond = self._is_clag_bond(ifaceobj)
for slave in Set(slaves).difference(Set(runningslaves)):
if (not ifupdownflags.flags.PERFMODE and
not self.ipcmd.link_exists(slave)):
not self.cache.link_exists(slave)):
self.log_error('%s: skipping slave %s, does not exist'
%(ifaceobj.name, slave), ifaceobj,
raise_error=False)
continue
link_up = False
if self.ipcmd.is_link_up(slave):
netlink.link_set_updown(slave, "down")
if self.cache.link_is_up(slave):
self.netlink.link_down_force(slave)
link_up = True
# If clag bond place the slave in a protodown state; clagd
# will protoup it when it is ready
if clag_bond:
try:
netlink.link_set_protodown(slave, "on")
self.netlink.link_set_protodown_on(slave)
except Exception, e:
self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
self.enable_ipv6_if_prev_brport(slave)
netlink.link_set_master(slave, ifaceobj.name)
self.netlink.link_set_master(slave, ifaceobj.name)
# TODO: if this fail we should switch to iproute2
# start a batch: down - set master - up
if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
try:
if (ifaceobj_getfunc(slave)[0].link_privflags &
ifaceLinkPrivFlags.KEEP_LINK_DOWN):
netlink.link_set_updown(slave, "down")
self.netlink.link_down_force(slave)
else:
netlink.link_set_updown(slave, "up")
self.netlink.link_up(slave)
except Exception, e:
self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
pass
@@ -323,22 +351,22 @@ class bond(moduleBase):
if runningslaves:
for s in runningslaves:
if s not in slaves:
self.bondcmd.bond_remove_slave(ifaceobj.name, s)
self.sysfs.bond_remove_slave(ifaceobj.name, s)
if clag_bond:
try:
netlink.link_set_protodown(s, "off")
self.netlink.link_set_protodown_off(s)
except Exception, e:
self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
else:
# apply link-down config changes on running slaves
try:
link_up = self.ipcmd.is_link_up(s)
link_up = self.cache.link_is_up(s)
config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
ifaceLinkPrivFlags.KEEP_LINK_DOWN)
if (config_link_down and link_up):
netlink.link_set_updown(s, "down")
self.netlink.link_down_force(s)
elif (not config_link_down and not link_up):
netlink.link_set_updown(s, "up")
self.netlink.link_up_force(s)
except Exception, e:
self.logger.warn('%s: %s' % (ifaceobj.name, str(e)))
@@ -396,7 +424,7 @@ class bond(moduleBase):
"""
ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
if link_exists and ifla_bond_miimon is None:
ifla_bond_miimon = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', Link.IFLA_BOND_MIIMON])
ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
if ifla_bond_miimon == 0:
for nl_attr, attr_name in self._bond_updown_delay_nl_list:
@@ -417,7 +445,7 @@ class bond(moduleBase):
def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
if ifla_bond_mode is None and link_exists:
ifla_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
ifla_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
# in this case the link already exists (we have a cached value):
# if IFLA_BOND_MODE is not present in ifla_info_data it means:
# - that bond-mode was present in the user config and didn't change
@@ -430,7 +458,7 @@ class bond(moduleBase):
if ifla_bond_mode == 4: # 802.3ad
min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
if min_links is None:
min_links = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
min_links = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MIN_LINKS)
# get_min_links_nl may return None so we need to strictly check 0
if min_links == 0:
self.logger.warn('%s: attribute bond-min-links is set to \'0\'' % ifname)
@@ -495,7 +523,7 @@ class bond(moduleBase):
nl_value = func_ptr(user_config.lower())
if link_exists:
cached_value = self.bondcmd.link_cache_get([ifname, 'linkinfo', netlink_attr])
cached_value = self.cache.get_link_info_data_attribute(ifname, netlink_attr)
if link_exists and cached_value is None:
# the link already exists but we don't have any value
@@ -539,10 +567,10 @@ class bond(moduleBase):
return True
return False
def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data):
def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data, bond_slaves):
# if bond-mode was changed the bond needs to be brought
# down and slaves un-slaved before bond mode is changed.
cached_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
cached_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
# bond-mode was changed or is not specified
@@ -551,24 +579,26 @@ class bond(moduleBase):
self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
% (ifname, ifla_bond_mode))
if is_link_up:
netlink.link_set_updown(ifname, 'down')
self.netlink.link_down(ifname)
is_link_up = False
for lower_dev in ifaceobj.lowerifaces:
netlink.link_set_nomaster(lower_dev)
self.netlink.link_set_nomaster(lower_dev)
try:
bond_slaves.remove(lower_dev)
except:
pass
self.bondcmd.cache_delete([ifname, 'linkinfo', 'slaves'])
else:
# bond-mode user config value is the current running(cached) value
# no need to reset it again we can ignore this attribute
del ifla_info_data[Link.IFLA_BOND_MODE]
return is_link_up
return is_link_up, bond_slaves
def create_or_set_bond_config(self, ifaceobj):
ifname = ifaceobj.name
link_exists = self.ipcmd.link_exists(ifname)
is_link_up = self.ipcmd.is_link_up(ifname) if link_exists else False
link_exists, is_link_up = self.cache.link_exists_and_up(ifname)
ifla_info_data = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
@@ -576,13 +606,21 @@ class bond(moduleBase):
# if link exists: down link if specific attributes are specified
if link_exists:
# did bond-mode changed?
is_link_up = self.should_update_bond_mode(ifaceobj, ifname, is_link_up, ifla_info_data)
is_link_up, bond_slaves = self.should_update_bond_mode(
ifaceobj,
ifname,
is_link_up,
ifla_info_data,
self.cache.get_slaves(ifname)
)
# if specific attributes need to be set we need to down the bond first
if ifla_info_data and is_link_up:
if self._should_down_bond(ifla_info_data):
netlink.link_set_updown(ifname, 'down')
self.netlink.link_down_force(ifname)
is_link_up = False
else:
bond_slaves = []
if link_exists and not ifla_info_data:
# if the bond already exists and no attrs need to be set
@@ -590,7 +628,7 @@ class bond(moduleBase):
self.logger.info('%s: already exists, no change detected' % ifname)
else:
try:
netlink.link_add_set(kind='bond', ifname=ifname, ifla_info_data=ifla_info_data)
self.netlink.link_add_bond_with_info_data(ifname, ifla_info_data)
except Exception as e:
# defensive code
# if anything happens, we try to set up the bond with the sysfs api
@@ -603,30 +641,30 @@ class bond(moduleBase):
ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
# if link_add doesn't raise we can update the cache, the future
# netlink listener will update the cache based on the kernel response
for key, value in ifla_info_data.items():
self.bondcmd.cache_update([ifname, 'linkinfo', key], value)
if link_exists and ifla_info_data and not is_link_up:
netlink.link_set_updown(ifname, 'up')
self.netlink.link_up_force(ifname)
return bond_slaves
def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
if not self.ipcmd.link_exists(ifaceobj.name):
self.bondcmd.create_bond(ifaceobj.name)
self.bondcmd.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
if not self.cache.link_exists(ifaceobj.name):
self.sysfs.bond_create(ifaceobj.name)
self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
def _up(self, ifaceobj, ifaceobj_getfunc=None):
try:
self.create_or_set_bond_config(ifaceobj)
self._add_slaves(ifaceobj, ifaceobj_getfunc)
bond_slaves = self.create_or_set_bond_config(ifaceobj)
self._add_slaves(
ifaceobj,
bond_slaves,
ifaceobj_getfunc,
)
except Exception, e:
self.log_error(str(e), ifaceobj)
def _down(self, ifaceobj, ifaceobj_getfunc=None):
try:
netlink.link_del(ifaceobj.name)
self.bondcmd.cache_delete([ifaceobj.name])
self.netlink.link_del(ifaceobj.name)
except Exception as e:
self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
@@ -651,7 +689,7 @@ class bond(moduleBase):
ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
if not self.bondcmd.bond_exists(ifaceobj.name):
if not self.cache.bond_exists(ifaceobj.name):
self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
return
@@ -673,7 +711,7 @@ class bond(moduleBase):
query_slaves = True
if not user_bond_slaves:
user_bond_slaves = self._get_slave_list(ifaceobj)
running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
except:
@@ -684,7 +722,7 @@ class bond(moduleBase):
# if user specified bond-ports we need to display it
if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
user_bond_slaves = self._get_slave_list(ifaceobj)
running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
except:
@@ -693,7 +731,7 @@ class bond(moduleBase):
for attr in iface_attrs:
nl_attr = self._bond_attr_netlink_map[attr]
translate_func = self._bond_attr_ifquery_check_translate_func[nl_attr]
current_config = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', nl_attr])
current_config = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
user_config = ifaceobj.get_attr_value_first(attr)
if current_config == translate_func(user_config):
@@ -710,28 +748,35 @@ class bond(moduleBase):
return 'fast' if value else 'slow'
def _query_running_attrs(self, bondname):
cached_vxlan_ifla_info_data = self.cache.get_link_info_data(bondname)
bond_attrs = {
'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])),
'bond-miimon': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON]),
'bond-use-carrier': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])),
'bond-lacp-rate': self.translate_nl_value_slowfast(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])),
'bond-min-links': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS]),
'bond-ad-actor-system': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM]),
'bond-ad-actor-sys-prio': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO]),
'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])),
'bond-lacp-bypass-allow': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])),
'bond-num-unsol-na': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
'bond-num-grat-arp': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
'bond-updelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY]),
'bond-downdelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MODE)),
'bond-miimon': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIIMON),
'bond-use-carrier': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_USE_CARRIER)),
'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_XMIT_HASH_POLICY)),
'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),
'bond-num-unsol-na': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
'bond-num-grat-arp': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
'bond-updelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_UPDELAY),
'bond-downdelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_DOWNDELAY)
}
slaves = self.bondcmd.bond_get_slaves(bondname)
cached_bond_primary = cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_PRIMARY)
if cached_bond_primary:
bond_attrs['bond-primary'] = self.cache.get_ifname(cached_bond_primary)
slaves = self.cache.get_slaves(bondname)
if slaves:
bond_attrs['bond-slaves'] = slaves
return bond_attrs
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
if not self.bondcmd.bond_exists(ifaceobjrunning.name):
if not self.cache.bond_exists(ifaceobjrunning.name):
return
bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
if bond_attrs.get('bond-slaves'):
@@ -752,10 +797,6 @@ class bond(moduleBase):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = self.bondcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None,
ifaceobj_getfunc=None):
""" run bond configuration on the interface object passed as argument
@@ -779,7 +820,6 @@ class bond(moduleBase):
return
if operation != 'query-running' and not self._is_bond(ifaceobj):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj,
ifaceobj_getfunc=ifaceobj_getfunc)

File diff suppressed because it is too large Load Diff

View File

@@ -5,54 +5,59 @@
#
try:
from ifupdown2.lib.addon import Addon
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
except ImportError:
from lib.addon import Addon
from ifupdown.iface import *
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
import ifupdown.ifupdownflags as ifupdownflags
class bridgevlan(moduleBase):
class bridgevlan(Addon, moduleBase):
""" ifupdown2 addon module to configure vlan attributes on a vlan
aware bridge """
_modinfo = {'mhelp' : 'bridgevlan module configures vlan attributes ' +
'on a vlan aware bridge. This module only ' +
'understands vlan interface name ' +
'with dot notations. eg br0.100. where br0 is the ' +
'vlan aware bridge this config is for',
'attrs' : {
'bridge-igmp-querier-src' :
{ 'help' : 'bridge igmp querier src. Must be ' +
'specified under the vlan interface',
'validvals' : ['<ipv4>', ],
'example' : ['bridge-igmp-querier-src 172.16.101.1']}}}
_modinfo = {
"mhelp": "bridgevlan module configures vlan attributes on a vlan aware "
"bridge. This module only understands vlan interface name "
"with dot notations. eg br0.100. where br0 is the vlan aware "
"bridge this config is for",
"attrs": {
"bridge-igmp-querier-src": {
"help": "bridge igmp querier src. Must be specified under "
"the vlan interface",
"validvals": ["<ipv4>"],
"example": ["bridge-igmp-querier-src 172.16.101.1"]
}
}
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.brctlcmd = None
self.ipcmd = None
def _is_bridge_vlan_device(self, ifaceobj):
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
return True
return False
@staticmethod
def _is_bridge_vlan_device(ifaceobj):
return ifaceobj.type == ifaceType.BRIDGE_VLAN
def _get_bridge_n_vlan(self, ifaceobj):
@staticmethod
def _get_bridge_n_vlan(ifaceobj):
vlist = ifaceobj.name.split('.', 1)
if len(vlist) == 2:
return (vlist[0], vlist[1])
return vlist[0], vlist[1]
return None
def _get_bridgename(self, ifaceobj):
@staticmethod
def _get_bridgename(ifaceobj):
vlist = ifaceobj.name.split('.', 1)
if len(vlist) == 2:
return vlist[0]
@@ -68,18 +73,18 @@ class bridgevlan(moduleBase):
(bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
vlanid = int(vlan, 10)
except:
self.log_error('%s: bridge vlan interface name ' %ifaceobj.name +
'does not correspond to format (eg. br0.100)', ifaceobj)
self.log_error("%s: bridge vlan interface name does not correspond "
"to format (eg. br0.100)" % ifaceobj.name, ifaceobj)
raise
if not self.ipcmd.link_exists(bridgename):
if not self.cache.link_exists(bridgename):
#self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
# bridgename))
return
running_mcqv4src = {}
if not ifupdownflags.flags.PERFMODE:
running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(bridgename)
running_mcqv4src = self.sysfs.bridge_get_mcqv4src(bridgename)
if running_mcqv4src:
r_mcqv4src = running_mcqv4src.get(vlan)
else:
@@ -87,37 +92,37 @@ class bridgevlan(moduleBase):
mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
if not mcqv4src:
if r_mcqv4src:
self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
return
if r_mcqv4src and r_mcqv4src != mcqv4src:
self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
self.iproute2.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
else:
self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
self.iproute2.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
def _down(self, ifaceobj):
try:
(bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
vlanid = int(vlan, 10)
except:
self.logger.warn('%s: bridge vlan interface name ' %ifaceobj.name +
'does not correspond to format (eg. br0.100)')
self.logger.warn("%s: bridge vlan interface name does not "
"correspond to format (eg. br0.100)" % ifaceobj.name)
raise
if not self.ipcmd.link_exists(bridgename):
if not self.cache.link_exists(bridgename):
#self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
# bridgename))
return
mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
if mcqv4src:
self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
def _query_running_bridge_igmp_querier_src(self, ifaceobj):
(bridgename, vlanid) = ifaceobj.name.split('.')
running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(bridgename)
running_mcqv4src = self.sysfs.bridge_get_mcqv4src(bridgename)
if running_mcqv4src:
return running_mcqv4src.get(vlanid)
return running_mcqv4src.get(vlanid)
return None
def _query_check(self, ifaceobj, ifaceobjcurr):
@@ -133,32 +138,24 @@ class bridgevlan(moduleBase):
ifaceobjcurr.status = ifaceStatus.SUCCESS
return
def _query_running(self, ifaceobjrunning):
# XXX not supported
return
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
ret = True
bvlan_intf = self._is_bridge_vlan_device(ifaceobj)
if (ifaceobj.get_attr_value_first('bridge-igmp-querier-src') and
not bvlan_intf):
self.logger.error('%s: bridge-igmp-querier-src only allowed under vlan stanza' %ifaceobj.name)
if (ifaceobj.get_attr_value_first('bridge-igmp-querier-src') and not bvlan_intf):
self.logger.error('%s: bridge-igmp-querier-src only allowed under vlan stanza' % ifaceobj.name)
ret = False
return ret
_run_ops = {'pre-up' : _up,
'post-down' : _down,
'query-checkcurr' : _query_check,
'query-running' : _query_running}
_run_ops = {
"pre-up": _up,
"post-down": _down,
"query-checkcurr": _query_check,
}
def get_ops(self):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = self.brctlcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
""" run vlan configuration on the interface object passed as argument
@@ -178,18 +175,15 @@ class bridgevlan(moduleBase):
op_handler = self._run_ops.get(operation)
if not op_handler:
return
if (operation != 'query-running' and
not self._is_bridge_vlan_device(ifaceobj)):
if (operation != 'query-running' and not self._is_bridge_vlan_device(ifaceobj)):
# most common problem is people specify BRIDGE_VLAN
# attribute on a bridge or a vlan device, which
# is incorrect. So, catch them here and warn before
# giving up processing the interface
if ((ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
ifaceobj.link_kind & ifaceLinkKind.VLAN) and
not self.syntax_check(ifaceobj, None)):
if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or ifaceobj.link_kind & ifaceLinkKind.VLAN) \
and not self.syntax_check(ifaceobj, None):
ifaceobj.status = ifaceStatus.ERROR
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -6,8 +6,11 @@
import re
import time
import socket
try:
from ifupdown2.lib.addon import Addon
import ifupdown2.ifupdown.policymanager as policymanager
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
@@ -15,9 +18,10 @@ try:
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdownaddons.dhclient import dhclient
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
except ImportError:
from lib.addon import Addon
import ifupdown.policymanager as policymanager
import ifupdown.ifupdownflags as ifupdownflags
@@ -25,46 +29,16 @@ except ImportError:
from ifupdown.utils import utils
from ifupdownaddons.dhclient import dhclient
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
class dhcp(moduleBase):
class dhcp(Addon, moduleBase):
""" ifupdown2 addon module to configure dhcp on interface """
_modinfo = {
"mhelp": "Configure dhcp",
"policies": {
"dhcp6-duid": {
"help": "Override the default when selecting the type of DUID to use. By default, DHCPv6 dhclient "
"creates an identifier based on the link-layer address (DUID-LL) if it is running in stateless "
"mode (with -S, not requesting an address), or it creates an identifier based on the "
"link-layer address plus a timestamp (DUID-LLT) if it is running in stateful mode (without -S, "
"requesting an address). When DHCPv4 is configured to use a DUID using -i option the default "
"is to use a DUID-LLT. -D overrides these default, with a value of either LL or LLT.",
"validvals": ["LL", "LLT"],
"example": ["dhcp6-duid LL"]
},
"dhcp-wait": {
"help": "Wait or not wait and become a daemon immediately (nowait) rather than waiting until an "
"IP address has been acquired. If not specified default value is true, that is to wait.",
"validvals": ["true", "false"],
"example": ["dhcp-wait false"]
},
"dhcp6-ll-wait": {
"help": "Overrides the default wait time before DHCPv6 client is started. During this wait time, "
"ifupdown2 checks if the interface requesting an address has a valid link-local address. "
"If not specified default value used is 10 seconds.",
"validvals": ["whole numbers"],
"example": ["dhcp6-ll-wait 0"]
}
}
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.dhclientcmd = dhclient(**kargs)
self.ipcmd = None
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
@@ -99,10 +73,9 @@ class dhcp(moduleBase):
pass
dhcp6_duid = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
ifname=ifaceobj.name, attr='dhcp6-duid')
vrf = ifaceobj.get_attr_value_first('vrf')
if (vrf and self.vrf_exec_cmd_prefix and
self.ipcmd.link_exists(vrf)):
self.cache.link_exists(vrf)):
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
if 'inet' in ifaceobj.addr_family:
@@ -140,8 +113,7 @@ class dhcp(moduleBase):
#add delay before starting IPv6 dhclient to
#make sure the configured interface/link is up.
if timeout > 1:
time.sleep(1)
time.sleep(1)
while timeout:
addr_output = utils.exec_command('%s -6 addr show %s'
%(utils.ip_cmd, ifaceobj.name))
@@ -173,18 +145,20 @@ class dhcp(moduleBase):
dhclient_cmd_prefix = None
vrf = ifaceobj.get_attr_value_first('vrf')
if (vrf and self.vrf_exec_cmd_prefix and
self.ipcmd.link_exists(vrf)):
self.cache.link_exists(vrf)):
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
dhcp6_duid = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
ifname=ifaceobj.name, attr='dhcp6-duid')
ifname=ifaceobj.name, attr='dhcp6-duid')
if 'inet6' in ifaceobj.addr_family:
self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix, duid=dhcp6_duid)
self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6)
if 'inet' in ifaceobj.addr_family:
self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET)
def _down(self, ifaceobj):
self._dhcp_down(ifaceobj)
self.ipcmd.link_down(ifaceobj.name)
self.netlink.link_down(ifaceobj.name)
def _query_check(self, ifaceobj, ifaceobjcurr):
status = ifaceStatus.SUCCESS
@@ -210,7 +184,7 @@ class dhcp(moduleBase):
ifaceobjcurr.status = status
def _query_running(self, ifaceobjrunning):
if not self.ipcmd.link_exists(ifaceobjrunning.name):
if not self.cache.link_exists(ifaceobjrunning.name):
return
if self.dhclientcmd.is_running(ifaceobjrunning.name):
ifaceobjrunning.addr_family.append('inet')
@@ -229,10 +203,6 @@ class dhcp(moduleBase):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
""" run dhcp configuration on the interface object passed as argument
@@ -262,7 +232,6 @@ class dhcp(moduleBase):
return
if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -7,6 +7,8 @@
import os
try:
from ifupdown2.lib.addon import Addon
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
import ifupdown2.ifupdown.policymanager as policymanager
@@ -15,9 +17,10 @@ try:
from ifupdown2.ifupdown.exceptions import moduleNotSupported
from ifupdown2.ifupdownaddons.utilsbase import *
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
except ImportError:
from lib.addon import Addon
import ifupdown.ifupdownflags as ifupdownflags
import ifupdown.policymanager as policymanager
@@ -26,51 +29,64 @@ except ImportError:
from ifupdown.exceptions import moduleNotSupported
from ifupdownaddons.utilsbase import *
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
class ethtool(moduleBase,utilsBase):
class ethtool(Addon, moduleBase):
""" ifupdown2 addon module to configure ethtool attributes """
_modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
'attrs': {
'link-speed' :
{'help' : 'set link speed',
'validvals' : ['10',
'100',
'1000',
'10000',
'25000',
'40000',
'50000',
'100000'],
'example' : ['link-speed 1000'],
'default' : 'varies by platform and port'},
'link-duplex' :
{'help': 'set link duplex',
'example' : ['link-duplex full'],
'validvals' : ['half', 'full'],
'default' : 'full'},
'link-autoneg' :
{'help': 'set autonegotiation',
'example' : ['link-autoneg on'],
'validvals' : ['yes', 'no', 'on', 'off'],
'default' : 'varies by platform and port'},
'link-fec' :
{'help': 'set forward error correction mode',
'example' : ['link-fec rs'],
'validvals' : ['rs', 'baser', 'auto', 'off'],
'default' : 'varies by platform and port'}}}
_modinfo = {
"mhelp": "ethtool configuration module for interfaces",
"attrs": {
"link-speed": {
"help": "set link speed",
"validvals": [
"10",
"100",
"1000",
"10000",
"25000",
"40000",
"50000",
"100000"
],
"example": ["link-speed 1000"],
"default": "varies by platform and port"
},
"link-duplex": {
"help": "set link duplex",
"example": ["link-duplex full"],
"validvals": ["half", "full"],
"default": "full"
},
"link-autoneg": {
"help": "set autonegotiation",
"example": ["link-autoneg on"],
"validvals": ["yes", "no", "on", "off"],
"default": "varies by platform and port"
},
"link-fec": {
"help": "set forward error correction mode",
"example": ["link-fec rs"],
"validvals": ["rs", "baser", "auto", "off"],
"default": "varies by platform and port"
}
}
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
if not os.path.exists(utils.ethtool_cmd):
raise moduleNotSupported('module init failed: %s: not found' % utils.ethtool_cmd)
self.ipcmd = None
# keep a list of iface objects who have modified link attributes
self.ifaceobjs_modified_configs = []
self.ethtool_ignore_errors = policymanager.policymanager_api.get_module_globals(
module_name=self.__class__.__name__,
attr='ethtool_ignore_errors'
)
def do_fec_settings(self, ifaceobj):
feccmd = ''
@@ -94,10 +110,6 @@ class ethtool(moduleBase,utilsBase):
if default_val:
default_val = default_val.lower()
if running_val in ["none", "notsupported"]:
# None and NotSupported ethtool FEC values mean "off"
running_val = "off"
# check running values
if config_val and config_val == running_val:
return
@@ -120,7 +132,8 @@ class ethtool(moduleBase,utilsBase):
(utils.ethtool_cmd, ifaceobj.name, feccmd))
utils.exec_command(feccmd)
except Exception, e:
self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
if not self.ethtool_ignore_errors:
self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
else:
pass
@@ -214,14 +227,15 @@ class ethtool(moduleBase,utilsBase):
cmd = ('%s -s %s %s' % (utils.ethtool_cmd, ifaceobj.name, cmd))
utils.exec_command(cmd)
except Exception, e:
self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
if not self.ethtool_ignore_errors:
self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
def _pre_up(self, ifaceobj, operation='post_up'):
"""
_pre_up and _pre_down will reset the layer 2 attributes to default policy
settings.
"""
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
self.do_speed_settings(ifaceobj)
@@ -306,9 +320,9 @@ class ethtool(moduleBase,utilsBase):
"""
try:
for attr in ethtool_output.splitlines():
if attr.startswith('FEC encodings'):
if attr.startswith('Configured FEC encodings:'):
fec_attrs = attr.split()
return(fec_attrs[fec_attrs.index(':')+1])
return(fec_attrs[fec_attrs.index('encodings:')+1])
except Exception as e:
self.logger.debug('ethtool: problems in ethtool set-fec output'
' %s: %s' %(ethtool_output.splitlines(), str(e)))
@@ -328,13 +342,14 @@ class ethtool(moduleBase,utilsBase):
(utils.ethtool_cmd, ifaceobj.name))
running_attr = self.get_fec_encoding(ethtool_output=output)
else:
running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \
running_attr = self.io.read_file_oneline('/sys/class/net/%s/%s' % \
(ifaceobj.name, attr))
except Exception as e:
# for nonexistent interfaces, we get an error (rc = 256 or 19200)
self.logger.debug('ethtool: problems calling ethtool or reading'
' /sys/class on iface %s for attr %s: %s' %
(ifaceobj.name, attr, str(e)))
if not self.ethtool_ignore_errors:
# for nonexistent interfaces, we get an error (rc = 256 or 19200)
self.logger.debug('ethtool: problems calling ethtool or reading'
' /sys/class on iface %s for attr %s: %s' %
(ifaceobj.name, attr, str(e)))
return running_attr
@@ -347,7 +362,7 @@ class ethtool(moduleBase,utilsBase):
"""
# do not bother showing swp ifaces that are not up for the speed
# duplex and autoneg are not reliable.
if not self.ipcmd.is_link_up(ifaceobj.name):
if not self.cache.link_is_up(ifaceobj.name):
return
for attr in ['speed', 'duplex', 'autoneg']:
default_val = policymanager.policymanager_api.get_iface_default(
@@ -396,10 +411,6 @@ class ethtool(moduleBase,utilsBase):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
""" run ethtool configuration on the interface object passed as
argument
@@ -423,7 +434,6 @@ class ethtool(moduleBase,utilsBase):
op_handler = self._run_ops.get(operation)
if not op_handler:
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -10,59 +10,64 @@
# loopback or dummy.
try:
from ifupdown2.lib.addon import Addon
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
import ifupdown2.ifupdown.policymanager as policymanager
except ImportError:
from lib.addon import Addon
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
import ifupdown.ifupdownflags as ifupdownflags
import ifupdown.policymanager as policymanager
class link(moduleBase):
_modinfo = {'mhelp' : 'create/configure link types. similar to ip-link',
'attrs' : {
'link-type' :
{'help' : 'type of link as in \'ip link\' command.',
'validvals' : ['dummy', 'veth'],
'example' : ['link-type <dummy|veth>']},
'link-down' :
{'help': 'keep link down',
'example' : ['link-down yes/no'],
'default' : 'no',
'validvals' : ['yes', 'no']}}}
class link(Addon, moduleBase):
_modinfo = {
"mhelp": "create/configure link types. similar to ip-link",
"attrs": {
"link-type": {
"help": "type of link as in 'ip link' command.",
"validvals": ["dummy", "veth"],
"example": ["link-type <dummy|veth>"]
},
"link-down": {
"help": "keep link down",
"example": ["link-down yes/no"],
"default": "no",
"validvals": ["yes", "no"]
}
}
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
self.check_physical_port_existance = utils.get_boolean_from_string(policymanager.policymanager_api.get_module_globals(
self.__class__.__name__,
'warn_on_physdev_not_present'
))
self.check_physical_port_existance = utils.get_boolean_from_string(
policymanager.policymanager_api.get_module_globals(
self.__class__.__name__,
'warn_on_physdev_not_present'
)
)
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
if self.check_physical_port_existance:
if not ifaceobj.link_kind and not LinkUtils.link_exists(ifaceobj.name):
if not ifaceobj.link_kind and not self.cache.link_exists(ifaceobj.name):
self.logger.warning('%s: interface does not exist' % ifaceobj.name)
return False
return True
def _is_my_interface(self, ifaceobj):
if (ifaceobj.get_attr_value_first('link-type')
or ifaceobj.get_attr_value_first('link-down')):
return True
return False
@staticmethod
def _is_my_interface(ifaceobj):
return ifaceobj.get_attr_value_first('link-type') or ifaceobj.get_attr_value_first('link-down')
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
if ifaceobj.get_attr_value_first('link-down') == 'yes':
@@ -73,36 +78,32 @@ class link(moduleBase):
def _up(self, ifaceobj):
link_type = ifaceobj.get_attr_value_first('link-type')
if link_type:
self.ipcmd.link_create(ifaceobj.name,
ifaceobj.get_attr_value_first('link-type'))
self.netlink.link_add(ifname=ifaceobj.name, kind=link_type)
def _down(self, ifaceobj):
if not ifaceobj.get_attr_value_first('link-type'):
return
if (not ifupdownflags.flags.PERFMODE and
not self.ipcmd.link_exists(ifaceobj.name)):
return
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
return
try:
self.ipcmd.link_delete(ifaceobj.name)
self.netlink.link_del(ifaceobj.name)
except Exception, e:
self.log_warn(str(e))
def _query_check(self, ifaceobj, ifaceobjcurr):
if ifaceobj.get_attr_value('link-type'):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
ifaceobjcurr.update_config_with_status('link-type', 'None', 1)
else:
link_type = ifaceobj.get_attr_value_first('link-type')
if self.ipcmd.link_get_kind(ifaceobj.name) == link_type:
ifaceobjcurr.update_config_with_status('link-type',
link_type, 0)
if self.cache.get_link_kind(ifaceobj.name) == link_type:
ifaceobjcurr.update_config_with_status('link-type', link_type, 0)
else:
ifaceobjcurr.update_config_with_status('link-type',
link_type, 1)
ifaceobjcurr.update_config_with_status('link-type', link_type, 1)
link_down = ifaceobj.get_attr_value_first('link-down')
if link_down:
link_up = self.ipcmd.is_link_up(ifaceobj.name)
link_up = self.cache.link_is_up(ifaceobj.name)
link_should_be_down = utils.get_boolean_from_string(link_down)
if link_should_be_down and link_up:
@@ -117,17 +118,15 @@ class link(moduleBase):
ifaceobjcurr.update_config_with_status('link-down', link_down, status)
_run_ops = {'pre-up' : _up,
'post-down' : _down,
'query-checkcurr' : _query_check}
_run_ops = {
"pre-up": _up,
"post-down": _down,
"query-checkcurr": _query_check
}
def get_ops(self):
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
op_handler = self._run_ops.get(operation)
if not op_handler:
@@ -135,7 +134,6 @@ class link(moduleBase):
if (operation != 'query-running' and
not self._is_my_interface(ifaceobj)):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -9,27 +9,27 @@ import os
from sets import Set
try:
from ifupdown2.lib.addon import Addon
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.netlink import netlink
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
import ifupdown2.ifupdown.policymanager as policymanager
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
from ifupdown2.ifupdownaddons.mstpctlutil import mstpctlutil
from ifupdown2.ifupdownaddons.systemutils import systemUtils
from ifupdown2.ifupdown.exceptions import moduleNotSupported
except ImportError:
from lib.addon import Addon
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdown.netlink import netlink
import ifupdown.ifupdownflags as ifupdownflags
import ifupdown.policymanager as policymanager
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
from ifupdownaddons.mstpctlutil import mstpctlutil
from ifupdownaddons.systemutils import systemUtils
@@ -39,176 +39,221 @@ except ImportError:
class mstpctlFlags:
PORT_PROCESSED = 0x1
class mstpctl(moduleBase):
class mstpctl(Addon, moduleBase):
""" ifupdown2 addon module to configure mstp attributes """
_modinfo = {'mhelp' : 'mstp configuration module for bridges',
'attrs' : {
'mstpctl-ports' :
{'help' : 'mstp ports',
'compat' : True,
'deprecated': True,
'new-attribute': 'bridge-ports'},
'mstpctl-stp' :
{'help': 'bridge stp yes/no',
'validvals' : ['yes', 'no', 'on', 'off'],
'compat' : True,
'default' : 'no',
'deprecated': True,
'new-attribute': 'bridge-stp'},
'mstpctl-treeprio' :
{'help': 'tree priority',
'default' : '32768',
'validvals' : ['0', '4096', '8192', '12288', '16384',
'20480', '24576', '28672', '32768',
'36864', '40960', '45056', '49152',
'53248', '57344', '61440'],
'required' : False,
'example' : ['mstpctl-treeprio 32768']},
'mstpctl-ageing' :
{'help': 'ageing time',
'validrange' : ['0', '4096'],
'default' : '300',
'required' : False,
'jsonAttr': 'ageingTime',
'example' : ['mstpctl-ageing 300']},
'mstpctl-maxage' :
{ 'help' : 'max message age',
'validrange' : ['0', '255'],
'default' : '20',
'jsonAttr': 'bridgeMaxAge',
'required' : False,
'example' : ['mstpctl-maxage 20']},
'mstpctl-fdelay' :
{ 'help' : 'set forwarding delay',
'validrange' : ['0', '255'],
'default' : '15',
'jsonAttr': 'bridgeFwdDelay',
'required' : False,
'example' : ['mstpctl-fdelay 15']},
'mstpctl-maxhops' :
{ 'help' : 'bridge max hops',
'validrange' : ['0', '255'],
'default' : '20',
'jsonAttr': 'maxHops',
'required' : False,
'example' : ['mstpctl-maxhops 15']},
'mstpctl-txholdcount' :
{ 'help' : 'bridge transmit holdcount',
'validrange' : ['0', '255'],
'default' : '6',
'jsonAttr': 'txHoldCounter',
'required' : False,
'example' : ['mstpctl-txholdcount 6']},
'mstpctl-forcevers' :
{ 'help' : 'bridge force stp version',
'validvals' : ['rstp', ],
'default' : 'rstp',
'required' : False,
'jsonAttr': 'forceProtocolVersion',
'example' : ['mstpctl-forcevers rstp']},
'mstpctl-portpathcost' :
{ 'help' : 'bridge port path cost',
'validvals': ['<interface-range-list>'],
'validrange' : ['0', '65535'],
'default' : '0',
'jsonAttr' : 'adminExtPortCost',
'required' : False,
'example' : ['under the bridge: mstpctl-portpathcost swp1=0 swp2=1',
'under the port (recommended): mstpctl-portpathcost 0']},
'mstpctl-portp2p' :
{ 'help' : 'bridge port p2p detection mode',
'default' : 'auto',
'jsonAttr' : 'adminPointToPoint',
'validvals' : ['<interface-yes-no-auto-list>'],
'required' : False,
'example' : ['under the bridge: mstpctl-portp2p swp1=yes swp2=no',
'under the port (recommended): mstpctl-portp2p yes']},
'mstpctl-portrestrrole' :
{ 'help' :
'enable/disable port ability to take root role of the port',
'default' : 'no',
'jsonAttr' : 'restrictedRole',
'validvals' : ['<interface-yes-no-list>'],
'required' : False,
'example' : ['under the bridge: mstpctl-portrestrrole swp1=yes swp2=no',
'under the port (recommended): mstpctl-portrestrrole yes']},
'mstpctl-portrestrtcn' :
{ 'help' :
'enable/disable port ability to propagate received topology change notification of the port',
'default' : 'no',
'jsonAttr' : 'restrictedTcn',
'validvals' : ['<interface-yes-no-list>'],
'required' : False,
'example' : ['under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no',
'under the port (recommended): mstpctl-portrestrtcn yes']},
'mstpctl-bpduguard' :
{ 'help' :
'enable/disable bpduguard',
'default' : 'no',
'jsonAttr' : 'bpduGuardPort',
'validvals' : ['<interface-yes-no-list>'],
'required' : False,
'example' : ['under the bridge: mstpctl-bpduguard swp1=yes swp2=no',
'under the port (recommended): mstpctl-bpduguard yes']},
'mstpctl-treeportprio' :
{ 'help': 'Sets the <port>\'s priority MSTI instance. '
'The priority value must be a number between 0 and 240 and a multiple of 16.',
'default' : '128',
'validvals': ['<interface-range-list-multiple-of-16>'],
'validrange' : ['0', '240'],
'jsonAttr': 'treeportprio',
'required' : False,
'example' : ['under the bridge: mstpctl-treeportprio swp1=128 swp2=128',
'under the port (recommended): mstpctl-treeportprio 128']},
'mstpctl-hello' :
{ 'help' : 'set hello time',
'validrange' : ['0', '255'],
'default' : '2',
'required' : False,
'jsonAttr': 'helloTime',
'example' : ['mstpctl-hello 2']},
'mstpctl-portnetwork' :
{ 'help' : 'enable/disable bridge assurance capability for a port',
'validvals' : ['<interface-yes-no-list>'],
'default' : 'no',
'jsonAttr' : 'networkPort',
'required' : False,
'example' : ['under the bridge: mstpctl-portnetwork swp1=yes swp2=no',
'under the port (recommended): mstpctl-portnetwork yes']},
'mstpctl-portadminedge' :
{ 'help' : 'enable/disable initial edge state of the port',
'validvals' : ['<interface-yes-no-list>'],
'default' : 'no',
'jsonAttr' : 'adminEdgePort',
'required' : False,
'example' : ['under the bridge: mstpctl-portadminedge swp1=yes swp2=no',
'under the port (recommended): mstpctl-portadminedge yes']},
'mstpctl-portautoedge' :
{ 'help' : 'enable/disable auto transition to/from edge state of the port',
'validvals' : ['<interface-yes-no-list>'],
'default' : 'yes',
'jsonAttr' : 'autoEdgePort',
'required' : False,
'example' : ['under the bridge: mstpctl-portautoedge swp1=yes swp2=no',
'under the port (recommended): mstpctl-portautoedge yes']},
'mstpctl-treeportcost' :
{ 'help' : 'port tree cost',
'validrange' : ['0', '255'],
'required' : False,
'jsonAttr': 'extPortCost',
},
'mstpctl-portbpdufilter' :
{ 'help' : 'enable/disable bpdu filter on a port. ' +
'syntax varies when defined under a bridge ' +
'vs under a port',
'validvals' : ['<interface-yes-no-list>'],
'jsonAttr' : 'bpduFilterPort',
'default' : 'no',
'required' : False,
'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no',
'under a port: mstpctl-portbpdufilter yes']},
}}
_modinfo = {
"mhelp": "mstp configuration module for bridges",
"attrs": {
"mstpctl-ports": {
"help": "mstp ports",
"compat": True,
"deprecated": True,
"new-attribute": "bridge-ports"
},
"mstpctl-stp": {
"help": "bridge stp yes/no",
"validvals": ["yes", "no", "on", "off"],
"compat": True,
"default": "no",
"deprecated": True,
"new-attribute": "bridge-stp"
},
"mstpctl-treeprio": {
"help": "tree priority",
"default": "32768",
"validvals": [
"0", "4096", "8192", "12288", "16384",
"20480", "24576", "28672", "32768",
"36864", "40960", "45056", "49152",
"53248", "57344", "61440"
],
"required": False,
"example": ["mstpctl-treeprio 32768"]
},
"mstpctl-ageing": {
"help": "ageing time",
"validrange": ["0", "4096"],
"default": "300",
"required": False,
"jsonAttr": "ageingTime",
"example": ["mstpctl-ageing 300"]
},
"mstpctl-maxage": {
"help": "max message age",
"validrange": ["0", "255"],
"default": "20",
"jsonAttr": "bridgeMaxAge",
"required": False,
"example": ["mstpctl-maxage 20"]
},
"mstpctl-fdelay": {
"help": "set forwarding delay",
"validrange": ["0", "255"],
"default": "15",
"jsonAttr": "bridgeFwdDelay",
"required": False,
"example": ["mstpctl-fdelay 15"]
},
"mstpctl-maxhops": {
"help": "bridge max hops",
"validrange": ["0", "255"],
"default": "20",
"jsonAttr": "maxHops",
"required": False,
"example": ["mstpctl-maxhops 15"]
},
"mstpctl-txholdcount": {
"help": "bridge transmit holdcount",
"validrange": ["0", "255"],
"default": "6",
"jsonAttr": "txHoldCounter",
"required": False,
"example": ["mstpctl-txholdcount 6"]
},
"mstpctl-forcevers": {
"help": "bridge force stp version",
"validvals": ["rstp", ],
"default": "rstp",
"required": False,
"jsonAttr": "forceProtocolVersion",
"example": ["mstpctl-forcevers rstp"]
},
"mstpctl-portpathcost": {
"help": "bridge port path cost",
"validvals": ["<interface-range-list>"],
"validrange": ["0", "65535"],
"default": "0",
"jsonAttr": "adminExtPortCost",
"required": False,
"example": [
"under the bridge: mstpctl-portpathcost swp1=0 swp2=1",
"under the port (recommended): mstpctl-portpathcost 0"
]
},
"mstpctl-portp2p": {
"help": "bridge port p2p detection mode",
"default": "auto",
"jsonAttr": "adminPointToPoint",
"validvals": ["<interface-yes-no-auto-list>"],
"required": False,
"example": [
"under the bridge: mstpctl-portp2p swp1=yes swp2=no",
"under the port (recommended): mstpctl-portp2p yes"
]
},
"mstpctl-portrestrrole": {
"help":
"enable/disable port ability to take root role of the port",
"default": "no",
"jsonAttr": "restrictedRole",
"validvals": ["<interface-yes-no-list>"],
"required": False,
"example": [
"under the bridge: mstpctl-portrestrrole swp1=yes swp2=no",
"under the port (recommended): mstpctl-portrestrrole yes"
]
},
"mstpctl-portrestrtcn": {
"help":
"enable/disable port ability to propagate received "
"topology change notification of the port",
"default": "no",
"jsonAttr": "restrictedTcn",
"validvals": ["<interface-yes-no-list>"],
"required": False,
"example": [
"under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no",
"under the port (recommended): mstpctl-portrestrtcn yes"
]
},
"mstpctl-bpduguard": {
"help":
"enable/disable bpduguard",
"default": "no",
"jsonAttr": "bpduGuardPort",
"validvals": ["<interface-yes-no-list>"],
"required": False,
"example": [
"under the bridge: mstpctl-bpduguard swp1=yes swp2=no",
"under the port (recommended): mstpctl-bpduguard yes"
]
},
"mstpctl-treeportprio": {
"help": "Sets the <port>'s priority MSTI instance. "
"The priority value must be a number between 0 and 240 "
"and a multiple of 16.",
"default": "128",
"validvals": ["<interface-range-list-multiple-of-16>"],
"validrange": ["0", "240"],
"jsonAttr": "treeportprio",
"required": False,
"example": [
"under the bridge: mstpctl-treeportprio swp1=128 swp2=128",
"under the port (recommended): mstpctl-treeportprio 128"
]
},
"mstpctl-hello": {
"help": "set hello time",
"validrange": ["0", "255"],
"default": "2",
"required": False,
"jsonAttr": "helloTime",
"example": ["mstpctl-hello 2"]
},
"mstpctl-portnetwork": {
"help": "enable/disable bridge assurance capability for a port",
"validvals": ["<interface-yes-no-list>"],
"default": "no",
"jsonAttr": "networkPort",
"required": False,
"example": [
"under the bridge: mstpctl-portnetwork swp1=yes swp2=no",
"under the port (recommended): mstpctl-portnetwork yes"
]
},
"mstpctl-portadminedge": {
"help": "enable/disable initial edge state of the port",
"validvals": ["<interface-yes-no-list>"],
"default": "no",
"jsonAttr": "adminEdgePort",
"required": False,
"example": [
"under the bridge: mstpctl-portadminedge swp1=yes swp2=no",
"under the port (recommended): mstpctl-portadminedge yes"
]
},
"mstpctl-portautoedge": {
"help": "enable/disable auto transition to/from edge state of the port",
"validvals": ["<interface-yes-no-list>"],
"default": "yes",
"jsonAttr": "autoEdgePort",
"required": False,
"example": [
"under the bridge: mstpctl-portautoedge swp1=yes swp2=no",
"under the port (recommended): mstpctl-portautoedge yes"
]
},
"mstpctl-treeportcost": {
"help": "port tree cost",
# "validrange": ["0", "255"],
"required": False,
"jsonAttr": "extPortCost",
},
"mstpctl-portbpdufilter": {
"help": "enable/disable bpdu filter on a port. syntax varies "
"when defined under a bridge vs under a port",
"validvals": ["<interface-yes-no-list>"],
"jsonAttr": "bpduFilterPort",
"default": "no",
"required": False,
"example": [
"under a bridge: mstpctl-portbpdufilter swp1=no swp2=no",
"under a port: mstpctl-portbpdufilter yes"
]
},
}
}
# Maps mstp bridge attribute names to corresponding mstpctl commands
# XXX: This can be encoded in the modules dict above
@@ -236,12 +281,11 @@ class mstpctl(moduleBase):
'mstpctl-portbpdufilter' : 'portbpdufilter'}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
if not os.path.exists('/sbin/mstpctl'):
raise moduleNotSupported('module init failed: no /sbin/mstpctl found')
self.ipcmd = None
self.name = self.__class__.__name__
self.brctlcmd = None
self.mstpctlcmd = None
self.mstpd_running = (True if systemUtils.is_process_running('mstpd')
else False)
@@ -290,15 +334,9 @@ class mstpctl(moduleBase):
return True
def _is_bridge(self, ifaceobj):
if (ifaceobj.get_attr_value_first('mstpctl-ports') or
ifaceobj.get_attr_value_first('bridge-ports')):
return True
return False
def _is_bridge_port(self, ifaceobj):
if self.brctlcmd.is_bridge_port(ifaceobj.name):
return True
return False
return ifaceobj.link_kind & ifaceLinkKind.BRIDGE \
or ifaceobj.get_attr_value_first('mstpctl-ports') \
or ifaceobj.get_attr_value_first('bridge-ports')
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
if not self._is_bridge(ifaceobj):
@@ -309,10 +347,10 @@ class mstpctl(moduleBase):
def get_dependent_ifacenames_running(self, ifaceobj):
self._init_command_handlers()
if (self.brctlcmd.bridge_exists(ifaceobj.name) and
if (self.cache.bridge_exists(ifaceobj.name) and
not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
return None
return self.brctlcmd.get_bridge_ports(ifaceobj.name)
return self.cache.get_slaves(ifaceobj.name)
def _get_bridge_port_attr_value(self, bridgename, portname, attr):
json_attr = self.get_mod_subattr(attr, 'jsonAttr')
@@ -349,11 +387,11 @@ class mstpctl(moduleBase):
runningbridgeports = []
# Delete active ports not in the new port list
if not ifupdownflags.flags.PERFMODE:
runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
runningbridgeports = self.cache.get_slaves(ifaceobj.name)
if runningbridgeports:
[self.ipcmd.link_set(bport, 'nomaster')
for bport in runningbridgeports
if not bridgeports or bport not in bridgeports]
for bport in runningbridgeports:
if not bridgeports or bport not in bridgeports:
self.netlink.link_set_nomaster(bport)
else:
runningbridgeports = []
if not bridgeports:
@@ -362,13 +400,13 @@ class mstpctl(moduleBase):
for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
try:
if (not ifupdownflags.flags.DRYRUN and
not self.ipcmd.link_exists(bridgeport)):
not self.cache.link_exists(bridgeport)):
self.log_warn('%s: bridge port %s does not exist'
%(ifaceobj.name, bridgeport))
err += 1
continue
self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
self.ipcmd.addr_flush(bridgeport)
self.netlink.link_set_master(bridgeport, ifaceobj.name)
self.netlink.addr_flush(bridgeport)
except Exception, e:
self.log_error(str(e), ifaceobj)
@@ -410,7 +448,10 @@ class mstpctl(moduleBase):
self.logger.warn('%s' %str(e))
pass
if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
if self.cache.bridge_is_vlan_aware(ifaceobj.name):
return
bridgeports = self._get_bridge_port_list(ifaceobj)
if not bridgeports:
return
# set bridge port attributes
for attrname, dstattrname in self._port_attrs_map.items():
@@ -422,9 +463,8 @@ class mstpctl(moduleBase):
try:
jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
if default_val and jsonAttr:
bridgeports = self._get_bridge_port_list(ifaceobj)
for port in bridgeports:
if not self.brctlcmd.is_bridge_port(port):
if not self.cache.link_is_bridge_port(port):
continue
bport_ifaceobjs = ifaceobj_getfunc(port)
@@ -502,7 +542,7 @@ class mstpctl(moduleBase):
return self.get_mod_subattr(attr,'default')
return default_val
def _apply_bridge_port_settings(self, ifaceobj, bridgename=None,
def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None,
bridgeifaceobj=None,
stp_running_on=True,
mstpd_running=True):
@@ -517,7 +557,6 @@ class mstpctl(moduleBase):
%(ifaceobj.name) +
' (stp on bridge %s is not on yet)' %bridgename)
return applied
bvlan_aware = self.ipcmd.bridge_is_vlan_aware(bridgename)
if (not mstpd_running or
not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or
not bvlan_aware):
@@ -602,15 +641,18 @@ class mstpctl(moduleBase):
self.logger.info('%s: applying mstp configuration '
%ifaceobj.name + 'specific to ports')
# Query running bridge ports. and only apply attributes on them
bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
bridgeports = self.cache.get_slaves(ifaceobj.name)
if not bridgeports:
self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
return
bvlan_aware = self.cache.bridge_is_vlan_aware(ifaceobj.name)
for bport in bridgeports:
self.logger.info('%s: processing mstp config for port %s'
%(ifaceobj.name, bport))
if not self.ipcmd.link_exists(bport):
continue
if not self.cache.link_exists(bport):
continue
if not os.path.exists('/sys/class/net/%s/brport' %bport):
continue
bportifaceobjlist = ifaceobj_getfunc(bport)
@@ -622,7 +664,7 @@ class mstpctl(moduleBase):
mstpctlFlags.PORT_PROCESSED):
continue
try:
self._apply_bridge_port_settings(bportifaceobj,
self._apply_bridge_port_settings(bportifaceobj, bvlan_aware,
ifaceobj.name, ifaceobj)
except Exception, e:
pass
@@ -638,12 +680,17 @@ class mstpctl(moduleBase):
return False
def _up(self, ifaceobj, ifaceobj_getfunc=None):
# Check if bridge port
bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
if bridgename:
# bridge port specific:
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
bridgename = self.cache.get_master(ifaceobj.name)
if not bridgename:
return
bvlan_aware = self.cache.bridge_is_vlan_aware(bridgename)
mstpd_running = self.mstpd_running
stp_running_on = self._is_running_userspace_stp_state_on(bridgename)
applied = self._apply_bridge_port_settings(ifaceobj, bridgename,
stp_running_on = bool(self.cache.get_bridge_stp(bridgename))
applied = self._apply_bridge_port_settings(ifaceobj, bvlan_aware, bridgename,
None, stp_running_on,
mstpd_running)
if applied:
@@ -651,7 +698,8 @@ class mstpctl(moduleBase):
ifaceobj.module_flags.setdefault(self.name,0) | \
mstpctlFlags.PORT_PROCESSED
return
if not self._is_bridge(ifaceobj):
elif not self._is_bridge(ifaceobj):
return
# we are now here because the ifaceobj is a bridge
stp = None
@@ -661,21 +709,17 @@ class mstpctl(moduleBase):
if ifaceobj.get_attr_value_first('mstpctl-ports'):
# If bridge ports specified with mstpctl attr, create the
# bridge and also add its ports
self.ipcmd.batch_start()
if not ifupdownflags.flags.PERFMODE:
if not self.ipcmd.link_exists(ifaceobj.name):
self.ipcmd.link_create(ifaceobj.name, 'bridge')
else:
self.ipcmd.link_create(ifaceobj.name, 'bridge')
if not self.cache.link_exists(ifaceobj.name):
self.netlink.link_add_bridge(ifaceobj.name)
try:
self._add_ports(ifaceobj)
except Exception, e:
porterr = True
porterrstr = str(e)
pass
finally:
self.ipcmd.batch_commit()
running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
running_ports = self.cache.get_slaves(ifaceobj.name)
if running_ports:
# disable ipv6 for ports that were added to bridge
self._ports_enable_disable_ipv6(running_ports, '1')
@@ -683,14 +727,16 @@ class mstpctl(moduleBase):
stp = ifaceobj.get_attr_value_first('mstpctl-stp')
if stp:
self.set_iface_attr(ifaceobj, 'mstpctl-stp',
self.brctlcmd.bridge_set_stp)
self.iproute2.bridge_set_stp)
stp = utils.get_boolean_from_string(stp)
else:
stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
if (self.mstpd_running and
(stp == 'yes' or stp == 'on')):
stp = self.cache.get_bridge_stp(ifaceobj.name)
if self.mstpd_running and stp:
self.mstpctlcmd.batch_start()
self._apply_bridge_settings(ifaceobj, ifaceobj_getfunc)
self._apply_bridge_port_settings_all(ifaceobj,
ifaceobj_getfunc=ifaceobj_getfunc)
self.mstpctlcmd.batch_commit()
except Exception, e:
self.log_error(str(e), ifaceobj)
if porterr:
@@ -703,10 +749,10 @@ class mstpctl(moduleBase):
if ifaceobj.get_attr_value_first('mstpctl-ports'):
# If bridge ports specified with mstpctl attr, delete the
# bridge
ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
ports = self.cache.get_slaves(ifaceobj.name)
if ports:
self._ports_enable_disable_ipv6(ports, '0')
self.brctlcmd.delete_bridge(ifaceobj.name)
self.netlink.link_del(ifaceobj.name)
except Exception, e:
self.log_error(str(e), ifaceobj)
@@ -729,7 +775,7 @@ class mstpctl(moduleBase):
and attrname != 'mstpctl-maxhops'):
bridgeattrdict[attrname] = [v]
ports = self.brctlcmd.get_bridge_ports(ifaceobjrunning.name)
ports = self.cache.get_slaves(ifaceobjrunning.name)
# Do this only for vlan-UNAWARE-bridge
if ports and not bridge_vlan_aware:
portconfig = {'mstpctl-portautoedge' : '',
@@ -778,16 +824,12 @@ class mstpctl(moduleBase):
policymanager.policymanager_api.get_iface_default(module_name='bridge', ifname=ifaceobj.name, attr='bridge-stp'))
return utils.get_boolean_from_string(stp)
def _get_running_stp(self, ifaceobj):
stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
return utils.get_boolean_from_string(stp)
def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
ifaceobj_getfunc=None):
# list of attributes that are not supported currently
blacklistedattrs = ['mstpctl-portpathcost',
'mstpctl-treeportprio', 'mstpctl-treeportcost']
if not self.brctlcmd.bridge_exists(ifaceobj.name):
if not self.cache.bridge_exists(ifaceobj.name):
self.logger.debug('bridge %s does not exist' %ifaceobj.name)
return
ifaceattrs = self.dict_key_subset(ifaceobj.config,
@@ -803,8 +845,8 @@ class mstpctl(moduleBase):
if not runningattrs:
runningattrs = {}
config_stp = self._get_config_stp(ifaceobj)
running_stp = self._get_running_stp(ifaceobj)
running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name)
running_stp = self.cache.get_bridge_stp(ifaceobj.name)
running_port_list = self.cache.get_slaves(ifaceobj.name)
for k in ifaceattrs:
# for all mstpctl options
if k in blacklistedattrs:
@@ -875,7 +917,7 @@ class mstpctl(moduleBase):
# contain more than one valid values
stp_on_vals = ['on', 'yes']
stp_off_vals = ['off']
rv = self.brctlcmd.bridge_get_stp(ifaceobj.name)
rv = self.sysfs.bridge_get_stp(ifaceobj.name)
if ((v in stp_on_vals and rv in stp_on_vals) or
(v in stp_off_vals and rv in stp_off_vals)):
ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
@@ -945,7 +987,7 @@ class mstpctl(moduleBase):
self.set_default_mstp_vxlan_bridge_config and
(bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
config_stp = self._get_config_stp(bifaceobj)
running_stp = self._get_running_stp(bifaceobj)
running_stp = self.cache.get_bridge_stp(bifaceobj.name)
if (not config_stp or not running_stp):
continue
for attr in (
@@ -981,17 +1023,17 @@ class mstpctl(moduleBase):
def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
#self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
ifaceobjcurr.status = ifaceStatus.NOTFOUND
return
# Check if this is a bridge port
if not self._is_bridge_port(ifaceobj):
if not self.cache.link_is_bridge_port(ifaceobj.name):
# mark all the bridge attributes as error
ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
self._port_attrs_map.keys(), 0)
return
bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
bridgename = self.cache.get_master(ifaceobj.name)
# list of attributes that are not supported currently
blacklistedattrs = ['mstpctl-portpathcost',
'mstpctl-treeportprio', 'mstpctl-treeportcost']
@@ -1038,13 +1080,12 @@ class mstpctl(moduleBase):
ifaceobjrunning.update_config(attr, v)
def _query_running_bridge_port(self, ifaceobjrunning):
bridgename = self.ipcmd.bridge_port_get_bridge_name(
ifaceobjrunning.name)
bridgename = self.cache.get_master(ifaceobjrunning.name)
if not bridgename:
self.logger.warn('%s: unable to determine bridgename'
%ifaceobjrunning.name)
return
if self.brctlcmd.bridge_get_stp(bridgename) == 'no':
if self.sysfs.bridge_get_stp(bridgename) == 'no':
# This bridge does not run stp, return
return
# if userspace stp not set, return
@@ -1103,7 +1144,7 @@ class mstpctl(moduleBase):
# portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
def _query_running_bridge(self, ifaceobjrunning):
if self.brctlcmd.bridge_get_stp(ifaceobjrunning.name) == 'no':
if self.sysfs.bridge_get_stp(ifaceobjrunning.name) == 'no':
# This bridge does not run stp, return
return
# if userspace stp not set, return
@@ -1120,9 +1161,9 @@ class mstpctl(moduleBase):
bridge_vlan_aware))
def _query_running(self, ifaceobjrunning, **extra_args):
if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
if self.cache.bridge_exists(ifaceobjrunning.name):
self._query_running_bridge(ifaceobjrunning)
elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
self._query_running_bridge_port(ifaceobjrunning)
def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None):
@@ -1235,21 +1276,19 @@ class mstpctl(moduleBase):
if config:
ifaceobj.replace_config(attr, config)
_run_ops = {'pre-up' : _up,
'post-down' : _down,
'query-checkcurr' : _query_check,
'query-running' : _query_running,
'query' : _query}
_run_ops = {
"pre-up": _up,
"post-down": _down,
"query-checkcurr": _query_check,
"query-running": _query_running,
"query": _query
}
def get_ops(self):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = self.brctlcmd = LinkUtils()
if not self.mstpctlcmd:
self.mstpctlcmd = mstpctlutil()

View File

@@ -4,28 +4,28 @@ import os
import hashlib
try:
from ifupdown2.lib.addon import Addon
import ifupdown2.ifupdown.statemanager as statemanager
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
from ifupdown2.ifupdown.exceptions import moduleNotSupported
except ImportError:
from lib.addon import Addon
import ifupdown.statemanager as statemanager
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
from ifupdown.exceptions import moduleNotSupported
class ppp(moduleBase):
class ppp(Addon, moduleBase):
"""
ifupdown2 addon module to configure ppp
"""
@@ -48,10 +48,10 @@ class ppp(moduleBase):
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
if not os.path.exists('/usr/bin/pon'):
raise moduleNotSupported('module init failed: no /usr/bin/pon found')
self.ipcmd = None
@staticmethod
def _is_my_interface(ifaceobj):
@@ -81,7 +81,7 @@ class ppp(moduleBase):
# Always save the current config files hash
ifaceobj.update_config('provider_file', config)
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
try:
# This fails if not running
utils.exec_user_command('/bin/ps ax | /bin/grep pppd | /bin/grep -v grep | /bin/grep ' + provider)
@@ -97,7 +97,7 @@ class ppp(moduleBase):
utils.exec_commandl(['/usr/bin/poff', old_provider], stdout=None, stderr=None)
utils.exec_commandl(['/usr/bin/pon', provider], stdout=None, stderr=None)
except Exception, e:
except Exception as e:
self.log_warn(str(e))
def _down(self, ifaceobj):
@@ -109,7 +109,7 @@ class ppp(moduleBase):
# This fails if not running
utils.exec_user_command('/bin/ps ax | /bin/grep pppd | /bin/grep -v grep | /bin/grep ' + provider)
utils.exec_commandl(['/usr/bin/poff', provider], stdout=None, stderr=None)
except Exception, e:
except Exception as e:
self.log_warn(str(e))
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
@@ -124,12 +124,12 @@ class ppp(moduleBase):
return None
def _query_check(self, ifaceobj, ifaceobjcurr):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
ifaceobjcurr.status = ifaceStatus.SUCCESS
def _query_running(self, ifaceobjrunning):
if not self.ipcmd.link_exists(ifaceobjrunning.name):
if not self.cache.link_exists(ifaceobjrunning.name):
return
# Operations supported by this addon (yet).
@@ -143,10 +143,6 @@ class ppp(moduleBase):
def get_ops(self):
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
op_handler = self._run_ops.get(operation)
@@ -156,7 +152,6 @@ class ppp(moduleBase):
if operation != 'query-running' and not self._is_my_interface(ifaceobj):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -4,18 +4,20 @@
# -- Mon 10 Oct 2016 10:53:13 PM CEST
#
try:
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.lib.addon import Addon
from ifupdown2.nlmanager.nlmanager import Link
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
except ImportError:
from ifupdown.iface import *
from ifupdown.netlink import netlink
from lib.addon import Addon
from nlmanager.nlmanager import Link
from ifupdown.iface import *
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
import ifupdown.ifupdownflags as ifupdownflags
@@ -24,38 +26,38 @@ except ImportError:
#
# TODO: Add checks for ipip tunnels.
#
class tunnel(moduleBase):
class tunnel(Addon, moduleBase):
"""
ifupdown2 addon module to configure tunnels
"""
_modinfo = {
'mhelp': 'create/configure GRE/IPIP/SIT and GRETAP tunnel interfaces',
'attrs': {
'mode': {
'tunnel-mode': {
'help': 'type of tunnel as in \'ip link\' command.',
'validvals': ['gre', 'gretap', 'ipip', 'sit', 'vti', 'ip6gre', 'ipip6', 'ip6ip6', 'vti6'],
'required': True,
'example': ['mode gre']
},
'local': {
'tunnel-local': {
'help': 'IP of local tunnel endpoint',
'validvals': ['<ipv4>', '<ipv6>'],
'required': True,
'example': ['local 192.2.0.42']
},
'endpoint': {
'tunnel-endpoint': {
'help': 'IP of remote tunnel endpoint',
'validvals': ['<ipv4>', '<ipv6>'],
'required': True,
'example': ['endpoint 192.2.0.23']
},
'ttl': {
'tunnel-ttl': {
'help': 'TTL for tunnel packets',
'validvals': ['<number>'],
'required': False,
'example': ['ttl 64']
},
'tunnel-physdev': {
'tunnel-dev': {
'help': 'Physical underlay device to use for tunnel packets',
'validvals': ['<interface>'],
'required': False,
@@ -66,28 +68,72 @@ class tunnel(moduleBase):
def __init__(self, *args, **kargs):
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
Addon.__init__(self)
@staticmethod
def _is_my_interface(ifaceobj):
return ifaceobj.addr_method == "tunnel" and ifaceobj.get_attr_value_first('mode')
return ifaceobj.get_attr_value_first("tunnel-mode")
def _has_config_changed(self, attrs_present, attrs_configured):
@staticmethod
def _has_config_changed(attrs_present, attrs_configured):
for key, value in attrs_configured.iteritems():
if attrs_present.get(key) != value:
return True
return False
def __get_info_data_gre_tunnel(self, info_data):
tunnel_link_ifindex = info_data.get(Link.IFLA_GRE_LINK)
return {
"tunnel-endpoint": str(info_data.get(Link.IFLA_GRE_REMOTE)),
"tunnel-local": str(info_data.get(Link.IFLA_GRE_LOCAL)),
"tunnel-ttl": str(info_data.get(Link.IFLA_GRE_TTL)),
"tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
}
def __get_info_data_iptun_tunnel(self, info_data):
tunnel_link_ifindex = info_data.get(Link.IFLA_IPTUN_LINK)
return {
"tunnel-endpoint": str(info_data.get(Link.IFLA_IPTUN_REMOTE)),
"tunnel-local": str(info_data.get(Link.IFLA_IPTUN_LOCAL)),
"tunnel-ttl": str(info_data.get(Link.IFLA_IPTUN_TTL)),
"tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
}
def __get_info_data_vti_tunnel(self, info_data):
tunnel_link_ifindex = info_data.get(Link.IFLA_VTI_LINK)
return {
"tunnel-endpoint": str(info_data.get(Link.IFLA_VTI_REMOTE)),
"tunnel-local": str(info_data.get(Link.IFLA_VTI_LOCAL)),
"tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
}
def get_linkinfo_attrs(self, ifname, link_kind):
return {
"gre": self.__get_info_data_gre_tunnel,
"gretap": self.__get_info_data_gre_tunnel,
"ip6gre": self.__get_info_data_gre_tunnel,
"ip6gretap": self.__get_info_data_gre_tunnel,
"ip6erspan": self.__get_info_data_gre_tunnel,
"ipip": self.__get_info_data_iptun_tunnel,
"sit": self.__get_info_data_iptun_tunnel,
"ip6tnl": self.__get_info_data_iptun_tunnel,
"vti": self.__get_info_data_vti_tunnel,
"vti6": self.__get_info_data_vti_tunnel,
}.get(link_kind, lambda x: {})(self.cache.get_link_info_data(ifname))
def _up(self, ifaceobj):
attr_map = {
# attr_name -> ip route param name
'local': 'local',
'endpoint': 'remote',
'ttl': 'ttl',
'tunnel-physdev': 'dev',
'tunnel-local': 'local',
'tunnel-endpoint': 'remote',
'tunnel-ttl': 'ttl',
'tunnel-dev': 'dev',
}
mode = ifaceobj.get_attr_value_first('mode')
mode = ifaceobj.get_attr_value_first('tunnel-mode')
attrs = {}
attrs_mapped = {}
@@ -95,33 +141,33 @@ class tunnel(moduleBase):
# to attribute names expected by iproute
for attr, iproute_attr in attr_map.items():
attr_val = ifaceobj.get_attr_value_first(attr)
if attr_val != None:
if attr_val is not None:
attrs_mapped[iproute_attr] = attr_val
attrs[attr] = attr_val
# Create the tunnel if it doesn't exist yet...
if not self.ipcmd.link_exists(ifaceobj.name):
self.ipcmd.tunnel_create(ifaceobj.name, mode, attrs_mapped)
if not self.cache.link_exists(ifaceobj.name):
self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped)
return
# If it's present, check if there were changes
current_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
current_mode = self.ipcmd.link_cache_get([ifaceobj.name, 'kind'])
current_mode = self.cache.get_link_kind(ifaceobj.name)
current_attrs = self.get_linkinfo_attrs(ifaceobj.name, current_mode)
try:
if current_attrs and current_mode != mode or self._has_config_changed(current_attrs, attrs):
# Mode and some other changes are not possible without recreating the interface,
# so just recreate it IFF there have been changes.
self.ipcmd.link_delete(ifaceobj.name)
self.ipcmd.tunnel_create(ifaceobj.name, mode, attrs_mapped)
self.netlink.link_del(ifaceobj.name)
self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped)
except Exception, e:
self.log_warn(str(e))
def _down(self, ifaceobj):
if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists(ifaceobj.name):
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
return
try:
self.ipcmd.link_delete(ifaceobj.name)
self.netlink.link_del(ifaceobj.name)
except Exception, e:
self.log_warn(str(e))
@@ -129,7 +175,7 @@ class tunnel(moduleBase):
if not self._is_my_interface(ifaceobj):
return None
device = ifaceobj.get_attr_value_first('tunnel-physdev')
device = ifaceobj.get_attr_value_first('tunnel-dev')
if device:
return [device]
@@ -147,19 +193,21 @@ class tunnel(moduleBase):
def _query_check(self, ifaceobj, ifaceobjcurr):
ifname = ifaceobj.name
if not self.ipcmd.link_exists(ifname):
if not self.cache.link_exists(ifname):
return
tunattrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
link_kind = self.cache.get_link_kind(ifname)
tunattrs = self.get_linkinfo_attrs(ifaceobj.name, link_kind)
if not tunattrs:
ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1)
return
tunattrs["mode"] = self.ipcmd.link_get_kind(ifname)
tunattrs["tunnel-mode"] = link_kind
user_config_mode = ifaceobj.get_attr_value_first("mode")
user_config_mode = ifaceobj.get_attr_value_first("tunnel-mode")
if user_config_mode in ('ipip6', 'ip6ip6'):
ifaceobj.replace_config("mode", "ip6tnl")
ifaceobj.replace_config("tunnel-mode", "ip6tnl")
for attr in self.get_mod_attrs():
if not ifaceobj.get_attr_value_first(attr):
@@ -182,10 +230,6 @@ class tunnel(moduleBase):
def get_ops(self):
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
op_handler = self._run_ops.get(operation)
if not op_handler:
@@ -194,7 +238,6 @@ class tunnel(moduleBase):
if operation != 'query-running' and not self._is_my_interface(ifaceobj):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -5,52 +5,51 @@
#
try:
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
from ifupdown2.lib.addon import Addon
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.nlmanager.nlmanager import Link
from ifupdown2.ifupdownaddons.modulebase import moduleBase
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
except ImportError:
from lib.addon import Addon
from ifupdown.iface import *
from nlmanager.nlmanager import Link
from ifupdownaddons.modulebase import moduleBase
import ifupdown.ifupdownflags as ifupdownflags
from ifupdown.iface import *
from ifupdown.netlink import netlink
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
class vlan(moduleBase):
class vlan(Addon, moduleBase):
""" ifupdown2 addon module to configure vlans """
_modinfo = {'mhelp' : 'vlan module configures vlan interfaces.' +
'This module understands vlan interfaces with dot ' +
'notations. eg swp1.100. Vlan interfaces with any ' +
'other names need to have raw device and vlan id ' +
'attributes',
'attrs' : {
'vlan-raw-device' :
{'help' : 'vlan raw device',
'validvals': ['<interface>']},
'vlan-id' :
{'help' : 'vlan id',
'validrange' : ['0', '4096']},
'vlan-protocol' :
{'help' : 'vlan protocol',
'default' : '802.1q',
'validvals': ['802.1q', '802.1ad'],
'example' : ['vlan-protocol 802.1q']},
}}
_modinfo = {
"mhelp": "vlan module configures vlan interfaces. "
"This module understands vlan interfaces with dot "
"notations. eg swp1.100. Vlan interfaces with any "
"other names need to have raw device and vlan id attributes",
"attrs": {
"vlan-raw-device": {
"help": "vlan raw device",
"validvals": ["<interface>"]
},
"vlan-id": {
"help": "vlan id",
"validrange": ["0", "4096"]
},
"vlan-protocol": {
"help": "vlan protocol",
"default": "802.1q",
"validvals": ["802.1q", "802.1ad"],
"example": ["vlan-protocol 802.1q"]
},
}
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
def _is_vlan_device(self, ifaceobj):
@staticmethod
def _is_vlan_device(ifaceobj):
vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
if vlan_raw_device:
return True
@@ -58,10 +57,12 @@ class vlan(moduleBase):
return True
return False
def _is_vlan_by_name(self, ifacename):
@staticmethod
def _is_vlan_by_name(ifacename):
return '.' in ifacename
def _get_vlan_raw_device_from_ifacename(self, ifacename):
@staticmethod
def _get_vlan_raw_device_from_ifacename(ifacename):
""" Returns vlan raw device from ifname
Example:
Returns eth0 for ifname eth0.100
@@ -88,22 +89,21 @@ class vlan(moduleBase):
ifaceobj.link_kind |= ifaceLinkKind.VLAN
return [self._get_vlan_raw_device(ifaceobj)]
def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
add=True):
def _bridge_vid_add_del(self, bridgename, vlanid, add=True):
""" If the lower device is a vlan aware bridge, add/del the vlanid
to the bridge """
if self.ipcmd.bridge_is_vlan_aware(bridgename):
if add:
netlink.link_add_bridge_vlan(bridgename, vlanid)
else:
netlink.link_del_bridge_vlan(bridgename, vlanid)
if self.cache.bridge_is_vlan_aware(bridgename):
if add:
self.netlink.link_add_bridge_vlan(bridgename, vlanid)
else:
self.netlink.link_del_bridge_vlan(bridgename, vlanid)
def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
def _bridge_vid_check(self, ifaceobjcurr, bridgename, vlanid):
""" If the lower device is a vlan aware bridge, check if the vlanid
is configured on the bridge """
if not self.ipcmd.bridge_is_vlan_aware(bridgename):
if not self.cache.bridge_is_vlan_aware(bridgename):
return
vids = self.ipcmd.bridge_vlan_get_vids(bridgename)
_, vids = self.cache.get_pvid_and_vids(bridgename)
if not vids or vlanid not in vids:
ifaceobjcurr.status = ifaceStatus.ERROR
ifaceobjcurr.status_str = 'bridge vid error'
@@ -116,8 +116,15 @@ class vlan(moduleBase):
if not vlanrawdevice:
raise Exception('could not determine vlan raw device')
vlan_protocol = ifaceobj.get_attr_value_first('vlan-protocol')
cached_vlan_protocol = self.ipcmd.get_vlan_protocol(ifaceobj.name)
ifname = ifaceobj.name
if ifupdownflags.flags.PERFMODE:
cached_vlan_ifla_info_data = {}
else:
cached_vlan_ifla_info_data = self.cache.get_link_info_data(ifname)
vlan_protocol = ifaceobj.get_attr_value_first('vlan-protocol')
cached_vlan_protocol = cached_vlan_ifla_info_data.get(Link.IFLA_VLAN_PROTOCOL)
if not vlan_protocol:
vlan_protocol = self.get_attr_default_value('vlan-protocol')
@@ -130,25 +137,28 @@ class vlan(moduleBase):
if not ifupdownflags.flags.PERFMODE:
vlan_exists = self.ipcmd.link_exists(ifaceobj.name)
vlan_exists = self.cache.link_exists(ifaceobj.name)
if vlan_exists:
user_vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
cached_vlan_raw_device = self.ipcmd.cache_get('link', [ifaceobj.name, 'link'])
cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
if cached_vlan_raw_device and user_vlan_raw_device and cached_vlan_raw_device != user_vlan_raw_device:
raise Exception('%s: cannot change vlan-raw-device from %s to %s: operation not supported. '
'Please delete the device with \'ifdown %s\' and recreate it to apply the change.'
% (ifaceobj.name, cached_vlan_raw_device, user_vlan_raw_device, ifaceobj.name))
if not self.ipcmd.link_exists(vlanrawdevice):
raise Exception('rawdevice %s not present' %vlanrawdevice)
if not self.cache.link_exists(vlanrawdevice):
if ifupdownflags.flags.DRYRUN:
return
else:
raise Exception('rawdevice %s not present' % vlanrawdevice)
if vlan_exists:
self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
self._bridge_vid_add_del(vlanrawdevice, vlanid)
return
netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
self.netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
self._bridge_vid_add_del(vlanrawdevice, vlanid)
def _down(self, ifaceobj):
vlanid = self._get_vlan_id(ifaceobj)
@@ -157,72 +167,103 @@ class vlan(moduleBase):
vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
if not vlanrawdevice:
raise Exception('could not determine vlan raw device')
if (not ifupdownflags.flags.PERFMODE and
not self.ipcmd.link_exists(ifaceobj.name)):
return
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
return
try:
netlink.link_del(ifaceobj.name)
self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
self.netlink.link_del(ifaceobj.name)
self._bridge_vid_add_del(vlanrawdevice, vlanid, add=False)
except Exception, e:
self.log_warn(str(e))
def _query_check(self, ifaceobj, ifaceobjcurr):
if not self.ipcmd.link_exists(ifaceobj.name):
return
if not '.' in ifaceobj.name:
if not self.cache.link_exists(ifaceobj.name):
return
if '.' not in ifaceobj.name:
# if vlan name is not in the dot format, check its running state
(vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
ifaceobjcurr.update_config_with_status('vlan-raw-device',
vlanrawdev, 1)
else:
ifaceobjcurr.update_config_with_status('vlan-raw-device',
vlanrawdev, 0)
ifname = ifaceobj.name
cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
#
# vlan-raw-device
#
ifaceobjcurr.update_config_with_status(
'vlan-raw-device',
cached_vlan_raw_device,
cached_vlan_raw_device != ifaceobj.get_attr_value_first('vlan-raw-device')
)
cached_vlan_info_data = self.cache.get_link_info_data(ifname)
#
# vlan-id
#
vlanid_config = ifaceobj.get_attr_value_first('vlan-id')
if not vlanid_config:
vlanid_config = str(self._get_vlan_id(ifaceobj))
if vlanid != vlanid_config:
ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
else:
ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0)
cached_vlan_id = cached_vlan_info_data.get(Link.IFLA_VLAN_ID)
cached_vlan_id_str = str(cached_vlan_id)
ifaceobjcurr.update_config_with_status('vlan-id', cached_vlan_id_str, vlanid_config != cached_vlan_id_str)
#
# vlan-protocol
#
protocol_config = ifaceobj.get_attr_value_first('vlan-protocol')
if protocol_config:
if protocol_config.upper() != protocol.upper():
ifaceobjcurr.update_config_with_status('vlan-protocol',
protocol, 1)
cached_vlan_protocol = cached_vlan_info_data.get(Link.IFLA_VLAN_PROTOCOL)
if protocol_config.upper() != cached_vlan_protocol.upper():
ifaceobjcurr.update_config_with_status(
'vlan-protocol',
cached_vlan_protocol,
1
)
else:
ifaceobjcurr.update_config_with_status('vlan-protocol',
protocol, 0)
self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, int(vlanid))
ifaceobjcurr.update_config_with_status(
'vlan-protocol',
protocol_config,
0
)
self._bridge_vid_check(ifaceobjcurr, cached_vlan_raw_device, cached_vlan_id)
def _query_running(self, ifaceobjrunning):
if not self.ipcmd.link_exists(ifaceobjrunning.name):
ifname = ifaceobjrunning.name
if not self.cache.link_exists(ifname):
return
(vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
if not vlanid:
if not self.cache.get_link_kind(ifname) == 'vlan':
return
# If vlan name is not in the dot format, get the
# vlan dev and vlan id
if not '.' in ifaceobjrunning.name:
ifaceobjrunning.update_config_dict({k: [v] for k, v in
{'vlan-raw-device' : vlanrawdev,
'vlan-id' : vlanid,
'vlan-protocol' : protocol}.items()
if v})
if '.' in ifname:
return
_run_ops = {'pre-up' : _up,
'post-down' : _down,
'query-checkcurr' : _query_check,
'query-running' : _query_running}
cached_vlan_info_data = self.cache.get_link_info_data(ifname)
for attr_name, nl_attr in (
('vlan-id', Link.IFLA_VLAN_ID),
('vlan-protocol', Link.IFLA_VLAN_PROTOCOL)
):
ifaceobjrunning.update_config(attr_name, str(cached_vlan_info_data.get(nl_attr)))
ifaceobjrunning.update_config('vlan-raw-device', self.cache.get_lower_device_ifname(ifname))
_run_ops = {
"pre-up": _up,
"post-down": _down,
"query-checkcurr": _query_check,
"query-running": _query_running
}
def get_ops(self):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
""" run vlan configuration on the interface object passed as argument
@@ -247,7 +288,6 @@ class vlan(moduleBase):
if (operation != 'query-running' and
not self._is_vlan_device(ifaceobj)):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -13,69 +13,86 @@ import signal
from sets import Set
try:
from ifupdown2.lib.addon import Addon
import ifupdown2.ifupdown.policymanager as policymanager
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.nlmanager.nlmanager import Link
from ifupdown2.ifupdownaddons.dhclient import dhclient
from ifupdown2.ifupdownaddons.utilsbase import *
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
except ImportError:
from lib.addon import Addon
import ifupdown.policymanager as policymanager
import ifupdown.ifupdownflags as ifupdownflags
from ifupdown.statemanager import statemanager_api as statemanager
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdown.netlink import netlink
from nlmanager.nlmanager import Link
from ifupdownaddons.dhclient import dhclient
from ifupdownaddons.utilsbase import *
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
class vrfPrivFlags:
PROCESSED = 0x1
class vrf(moduleBase):
""" ifupdown2 addon module to configure vrfs """
_modinfo = { 'mhelp' : 'vrf configuration module',
'attrs' : {
'vrf-table':
{'help' : 'vrf device routing table id. key to ' +
'creating a vrf device. ' +
'Table id is either \'auto\' or '+
'\'valid routing table id\'',
'validvals': ['auto', '<number>'],
'example': ['vrf-table auto', 'vrf-table 1001']},
'vrf':
{'help' : 'vrf the interface is part of.',
'validvals': ['<text>'],
'example': ['vrf blue']}}}
iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
'# It contains the vrf name to table mapping.\n' + \
'# Reserved table range %s %s\n'
class vrf(Addon, moduleBase):
""" ifupdown2 addon module to configure vrfs """
_modinfo = {
"mhelp": "vrf configuration module",
"attrs": {
"vrf-table": {
"help": "vrf device routing table id. key to creating a vrf device. "
"Table id is either 'auto' or 'valid routing table id'",
"validvals": ["auto", "<number>"],
"example": ["vrf-table auto", "vrf-table 1001"]
},
"vrf": {
"help": "vrf the interface is part of",
"validvals": ["<text>"],
"example": ["vrf blue"]
}
}
}
iproute2_vrf_filename = "/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf"
iproute2_vrf_filehdr = "# This file is autogenerated by ifupdown2.\n" \
"# It contains the vrf name to table mapping.\n" \
"# Reserved table range %s %s\n"
VRF_TABLE_START = 1001
VRF_TABLE_END = 5000
system_reserved_rt_tables = {'255' : 'local', '254' : 'main',
'253' : 'default', '0' : 'unspec'}
system_reserved_rt_tables = {
"255": "local",
"254": "main",
"253": "default",
"0": "unspec"
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
self.bondcmd = None
self.dhclientcmd = None
self.name = self.__class__.__name__
self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(
module_name=self.__class__.__name__,
attr="vrf-mgmt-devname"
)
self.at_exit = False
self.user_reserved_vrf_table = []
@@ -130,7 +147,6 @@ class vrf(moduleBase):
self.l3mdev_checked = True
self._iproute2_vrf_map_initialized = False
self.iproute2_vrf_map = {}
self.iproute2_vrf_map_fd = None
self.iproute2_vrf_map_sync_to_disk = False
self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
@@ -196,7 +212,7 @@ class vrf(moduleBase):
iproute2_vrf_map_force_rewrite = False
# read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
if os.path.exists(self.iproute2_vrf_filename):
with open(self.iproute2_vrf_filename, 'r+') as vrf_map_fd:
with open(self.iproute2_vrf_filename, "r+" if writetodisk else "r") as vrf_map_fd:
lines = vrf_map_fd.readlines()
for l in lines:
l = l.strip()
@@ -215,19 +231,12 @@ class vrf(moduleBase):
self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e)))
pass
vrfs = self.ipcmd.link_get_vrfs()
running_vrf_map = {}
if vrfs:
for v, lattrs in vrfs.iteritems():
table = lattrs.get('table', None)
if table:
running_vrf_map[int(table)] = v
running_vrf_map = self.cache.get_vrf_table_map()
if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
self.iproute2_vrf_map = running_vrf_map
iproute2_vrf_map_force_rewrite = True
self.iproute2_vrf_map_fd = None
if writetodisk:
if iproute2_vrf_map_force_rewrite:
# reopen the file and rewrite the map
@@ -236,7 +245,8 @@ class vrf(moduleBase):
self._iproute2_vrf_map_open(False, True)
self.iproute2_vrf_map_sync_to_disk = False
atexit.register(self._iproute2_vrf_map_sync_to_disk)
self.at_exit = True
#atexit.register(self._iproute2_vrf_map_sync_to_disk)
self.logger.info("vrf: dumping iproute2_vrf_map")
self.logger.info(self.iproute2_vrf_map)
@@ -287,22 +297,15 @@ class vrf(moduleBase):
if ifupdownflags.flags.DRYRUN:
return
fmode = 'a+' if append else 'w'
try:
self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
'%s' %fmode)
fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
except Exception, e:
self._iproute2_map_warn(str(e))
return
if not append:
# write file header
self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
%(self.vrf_table_id_start,
self.vrf_table_id_end))
for t, v in self.iproute2_vrf_map.iteritems():
self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
self.iproute2_vrf_map_fd.flush()
with open(self.iproute2_vrf_filename, fmode) as vrf_map_fd:
vrf_map_fd.write(self.iproute2_vrf_filehdr
%(self.vrf_table_id_start,
self.vrf_table_id_end))
for t, v in self.iproute2_vrf_map.iteritems():
vrf_map_fd.write('%s %s\n' %(t, v))
vrf_map_fd.flush()
def _is_vrf(self, ifaceobj):
if ifaceobj.get_attr_value_first('vrf-table'):
@@ -364,10 +367,10 @@ class vrf(moduleBase):
old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
if not old_vrf_name:
self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
if self.iproute2_vrf_map_fd:
self.iproute2_vrf_map_fd.write('%s %s\n'
%(table_id, vrfifaceobj.name))
self.iproute2_vrf_map_fd.flush()
with open(self.iproute2_vrf_filename, "a+") as vrf_map_fd:
vrf_map_fd.write('%s %s\n'
% (table_id, vrfifaceobj.name))
vrf_map_fd.flush()
self.vrf_count += 1
return
if old_vrf_name != vrfifaceobj.name:
@@ -441,7 +444,7 @@ class vrf(moduleBase):
break
self._handle_existing_connections(ifaceobj, vrfname)
self.enable_ipv6_if_prev_brport(ifacename)
self.ipcmd.link_set(ifacename, 'master', vrfname)
self.netlink.link_set_master(ifacename, vrfname)
return
def enable_ipv6_if_prev_brport(self, ifname):
@@ -449,8 +452,10 @@ class vrf(moduleBase):
If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
"""
try:
if os.path.exists("/sys/class/net/%s/brport" % ifname):
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
return
except Exception, e:
self.logger.info(str(e))
@@ -458,7 +463,7 @@ class vrf(moduleBase):
try:
dhclient_cmd_prefix = None
if (vrfname and self.vrf_exec_cmd_prefix and
self.ipcmd.link_exists(vrfname)):
self.cache.link_exists(vrfname)):
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
vrfname)
self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
@@ -469,9 +474,10 @@ class vrf(moduleBase):
def _handle_existing_connections(self, ifaceobj, vrfname):
if not ifaceobj or ifupdownflags.flags.PERFMODE:
return
if (self.vrf_mgmt_devname and
self.vrf_mgmt_devname == vrfname):
self._kill_ssh_connections(ifaceobj.name)
# XXX: re-evaluate kill_ssh_sessions
if (self.vrf_mgmt_devname and self.vrf_mgmt_devname == vrfname):
self._kill_ssh_connections(ifaceobj.name, ifaceobj)
self._close_sockets(ifaceobj.name)
if self._is_dhcp_slave(ifaceobj):
self._down_dhcp_slave(ifaceobj, vrfname)
@@ -479,12 +485,12 @@ class vrf(moduleBase):
ifaceobj_getfunc=None, vrf_exists=False):
try:
master_exists = True
if vrf_exists or self.ipcmd.link_exists(vrfname):
uppers = self.ipcmd.link_get_uppers(ifacename)
if vrf_exists or self.cache.link_exists(vrfname):
uppers = self.sysfs.link_get_uppers(ifacename)
if not uppers or vrfname not in uppers:
self._handle_existing_connections(ifaceobj, vrfname)
self.enable_ipv6_if_prev_brport(ifacename)
self.ipcmd.link_set(ifacename, 'master', vrfname)
self.netlink.link_set_master(ifacename, vrfname)
elif ifaceobj:
vrf_master_objs = ifaceobj_getfunc(vrfname)
if not vrf_master_objs:
@@ -492,7 +498,7 @@ class vrf(moduleBase):
# but user has not provided a vrf interface.
# people expect you to warn them but go ahead with the
# rest of the config on that interface
netlink.link_set_updown(ifacename, "up")
self.netlink.link_up(ifacename)
self.log_error('vrf master ifaceobj %s not found'
%vrfname)
return
@@ -511,7 +517,7 @@ class vrf(moduleBase):
master_exists = False
if master_exists:
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
netlink.link_set_updown(ifacename, "up")
self.netlink.link_up(ifacename)
else:
self.log_error('vrf %s not around, skipping vrf config'
%(vrfname), ifaceobj)
@@ -641,14 +647,14 @@ class vrf(moduleBase):
# - check if it is also a macvlan device of the
# format <vrf_slave>-v<int> created by the
# address virtual module
vrfslave_lowers = self.ipcmd.link_get_lowers(vrfslave)
vrfslave_lowers = self.sysfs.link_get_lowers(vrfslave)
if vrfslave_lowers:
if vrfslave_lowers[0] in config_vrfslaves:
return True
return False
def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
config_slaves = ifaceobj.lowerifaces
if not config_slaves and not running_slaves:
return
@@ -660,7 +666,7 @@ class vrf(moduleBase):
if add_slaves:
for s in add_slaves:
try:
if not self.ipcmd.link_exists(s):
if not self.cache.link_exists(s):
continue
sobj = None
if ifaceobj_getfunc:
@@ -691,7 +697,7 @@ class vrf(moduleBase):
for slave_ifaceobj in ifaceobj_getfunc(s) or []:
if slave_ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
raise Exception("link-down yes: keeping VRF slave down")
netlink.link_set_updown(s, "up")
self.netlink.link_up(s)
except Exception, e:
self.logger.debug("%s: %s" % (s, str(e)))
pass
@@ -707,7 +713,7 @@ class vrf(moduleBase):
return False
def _create_vrf_dev(self, ifaceobj, vrf_table):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
self._check_vrf_system_reserved_names(ifaceobj)
if self.vrf_count == self.vrf_max_count:
@@ -739,8 +745,7 @@ class vrf(moduleBase):
self.vrf_table_id_start,
self.vrf_table_id_end), ifaceobj)
try:
self.ipcmd.link_create(ifaceobj.name, 'vrf',
{'table' : '%s' %vrf_table})
self.netlink.link_add_vrf(ifaceobj.name, vrf_table)
except Exception, e:
self.log_error('create failed (%s)\n' % str(e), ifaceobj)
if vrf_table != 'auto':
@@ -752,13 +757,12 @@ class vrf(moduleBase):
self.log_error('unable to get vrf table id', ifaceobj)
# if the device exists, check if table id is same
vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
if vrfdev_attrs:
running_table = vrfdev_attrs.get('table', None)
if vrf_table != running_table:
self.log_error('cannot change vrf table id,running table id'
' %s is different from config id %s'
% (running_table, vrf_table), ifaceobj)
running_table = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE)
if running_table is not None and vrf_table != str(running_table):
self.log_error('cannot change vrf table id,running table id'
' %s is different from config id %s'
% (running_table, vrf_table), ifaceobj)
return vrf_table
def _up_vrf_helper(self, ifaceobj, vrf_table):
@@ -794,16 +798,22 @@ class vrf(moduleBase):
self._set_vrf_dev_processed_flag(ifaceobj)
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
netlink.link_set_updown(ifaceobj.name, "up")
self.netlink.link_up(ifaceobj.name)
except Exception, e:
self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
def _kill_ssh_connections(self, ifacename):
def _kill_ssh_connections(self, ifacename, ifaceobj):
try:
runningaddrsdict = self.ipcmd.get_running_addrs(None, ifacename)
if not runningaddrsdict:
running_addrs_list = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
ifaceobj_list=[ifaceobj],
ifname=ifacename,
)]
if not running_addrs_list:
return
iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
iplist = [i.split('/', 1)[0] for i in running_addrs_list]
if not iplist:
return
proc=[]
@@ -884,13 +894,17 @@ class vrf(moduleBase):
else:
vrf = ifaceobj.get_attr_value_first('vrf')
if vrf:
if not self.cache.link_exists(ifaceobj.name):
self.logger.warning("%s: device not found - please check your configuration" % ifaceobj.name)
return
self._iproute2_vrf_map_initialize()
# This is a vrf slave
self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
ifaceobj_getfunc)
elif not ifupdownflags.flags.PERFMODE:
# check if we were a slave before
master = self.ipcmd.link_get_master(ifaceobj.name)
master = self.cache.get_master(ifaceobj.name)
if master:
self._iproute2_vrf_map_initialize()
if self._is_vrf_dev(master):
@@ -910,27 +924,36 @@ class vrf(moduleBase):
vrf_table,
mode))
def _close_sockets(self, ifaceobj, ifindex):
def _close_sockets(self, ifacename):
if not self.vrf_close_socks_on_down:
return
try:
ifindex = self.cache.get_ifindex(ifacename)
except Exception as e:
self.logger.debug("%s: vrf: close sockets error: %s" % str(e))
ifindex = 0
if not ifindex:
return
try:
utils.exec_command('%s -aK \"dev == %s\"'
%(utils.ss_cmd, ifindex))
except Exception, e:
self.logger.info('%s: closing socks using ss'
' failed (%s)\n' %(ifaceobj.name, str(e)))
' failed (%s)\n' %(ifacename, str(e)))
pass
def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
if vrf_table == 'auto':
vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
if running_slaves:
for s in running_slaves:
if ifaceobj_getfunc:
@@ -943,10 +966,10 @@ class vrf(moduleBase):
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
pass
try:
self.ipcmd.addr_flush(s)
netlink.link_set_updown(s, "down")
self.netlink.addr_flush(s)
self.netlink.link_down(s)
except Exception, e:
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
self.logger.info('%s: %s' %(s, str(e)))
pass
try:
@@ -961,16 +984,13 @@ class vrf(moduleBase):
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
pass
ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
self._close_sockets(ifaceobj.name)
if ifindex:
try:
self.ipcmd.link_delete(ifaceobj.name)
except Exception, e:
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
pass
self._close_sockets(ifaceobj, ifindex)
try:
self.netlink.link_del(ifaceobj.name)
except Exception, e:
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
pass
try:
self._iproute2_vrf_table_entry_del(vrf_table)
@@ -982,13 +1002,13 @@ class vrf(moduleBase):
def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
try:
self._handle_existing_connections(ifaceobj, vrfname)
self.ipcmd.link_set(ifacename, 'nomaster')
self.netlink.link_set_nomaster(ifacename)
# Down this slave only if it is a slave ifupdown2 manages.
# we dont want to down slaves that maybe up'ed by
# somebody else. One such example is a macvlan device
# which ifupdown2 addressvirtual addon module auto creates
if ifaceobj:
netlink.link_set_updown(ifacename, "down")
self.netlink.link_down(ifacename)
except Exception, e:
self.logger.warn('%s: %s' %(ifacename, str(e)))
@@ -1008,7 +1028,7 @@ class vrf(moduleBase):
def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
try:
master = self.ipcmd.link_get_master(ifaceobj.name)
master = self.cache.get_master(ifaceobj.name)
if not master or master != vrf:
ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
else:
@@ -1018,27 +1038,18 @@ class vrf(moduleBase):
def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
try:
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
return
if vrf_table == 'auto':
config_table = self._get_iproute2_vrf_table(ifaceobj.name)
config_table = str(self._get_iproute2_vrf_table(ifaceobj.name) or 0)
else:
config_table = vrf_table
vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
if not vrfdev_attrs:
ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
return
running_table = vrfdev_attrs.get('table')
if not running_table:
ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
return
if config_table != running_table:
ifaceobjcurr.update_config_with_status('vrf-table',
running_table, 1)
else:
ifaceobjcurr.update_config_with_status('vrf-table',
running_table, 0)
running_vrf_table = str(self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE))
ifaceobjcurr.update_config_with_status('vrf-table', running_vrf_table, config_table != running_vrf_table)
if not ifupdownflags.flags.WITHDEFAULTS:
return
if self.vrf_helper:
@@ -1077,18 +1088,17 @@ class vrf(moduleBase):
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
try:
kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
kind = self.cache.get_link_kind(ifaceobjrunning.name)
if kind == 'vrf':
vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
if vrfdev_attrs:
running_table = vrfdev_attrs.get('table')
if running_table:
ifaceobjrunning.update_config('vrf-table',
running_table)
return
slave_kind = self.ipcmd.link_get_slave_kind(ifaceobjrunning.name)
running_table = self.cache.get_link_info_data_attribute(ifaceobjrunning.name, Link.IFLA_VRF_TABLE)
if running_table is not None:
ifaceobjrunning.update_config('vrf-table', str(running_table))
return
slave_kind = self.cache.get_link_slave_kind(ifaceobjrunning.name)
if slave_kind == 'vrf_slave':
vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
vrf = self.cache.get_master(ifaceobjrunning.name)
if vrf:
ifaceobjrunning.update_config('vrf', vrf)
except Exception, e:
@@ -1101,19 +1111,19 @@ class vrf(moduleBase):
ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
ifaceobj.name))
_run_ops = {'pre-up' : _up,
'post-down' : _down,
'query-running' : _query_running,
'query-checkcurr' : _query_check,
'query' : _query}
_run_ops = {
"pre-up": _up,
"post-down": _down,
"query-running": _query_running,
"query-checkcurr": _query_check,
"query": _query
}
def get_ops(self):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = self.bondcmd = LinkUtils()
if not self.dhclientcmd:
self.dhclientcmd = dhclient()
@@ -1143,3 +1153,6 @@ class vrf(moduleBase):
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
if self.at_exit:
self._iproute2_vrf_map_sync_to_disk()
self.at_exit = False

File diff suppressed because it is too large Load Diff

View File

@@ -11,11 +11,13 @@ import socket
from ipaddr import IPNetwork, IPv6Network
try:
from ifupdown2.lib.addon import Addon
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.nlmanager.nlpacket import Link
from ifupdown2.ifupdownaddons.modulebase import moduleBase
import ifupdown2.ifupdown.statemanager as statemanager
@@ -23,11 +25,13 @@ try:
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
except ImportError:
from lib.addon import Addon
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdown.netlink import netlink
from ifupdownaddons.LinkUtils import LinkUtils
from nlmanager.nlpacket import Link
from ifupdownaddons.modulebase import moduleBase
import ifupdown.statemanager as statemanager
@@ -36,26 +40,26 @@ except ImportError:
import ifupdown.ifupdownconfig as ifupdownconfig
class xfrm(moduleBase):
class xfrm(Addon, moduleBase):
""" ifupdown2 addon module to create a xfrm interface """
_modinfo = {'mhelp' : 'xfrm module creates a xfrm interface for',
'attrs' : {
'xfrm-id' :
{ 'help' : 'xfrm id',
'validrange' : ['1', '65535'],
'example': ['xfrm-id 1']
},
'xfrm-physdev':
{'help': 'xfrm physical device',
'example': ['xfrm-physdev lo']
},
},
}
_modinfo = {
'mhelp': 'xfrm module creates a xfrm interface for',
'attrs': {
'xfrm-id': {
'help': 'xfrm id',
'validrange': ['1', '65535'],
'example': ['xfrm-id 1']
},
'xfrm-physdev': {
'help': 'xfrm physical device',
'example': ['xfrm-physdev lo']
},
},
}
def __init__(self, *args, **kargs):
Addon.__init__(self)
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
@@ -96,25 +100,29 @@ class xfrm(moduleBase):
xfrm_ifacename = self._get_xfrm_name(ifaceobj)
physdev = self._get_parent_ifacename(ifaceobj)
xfrmid = self._get_xfrmid(ifaceobj)
if not self.ipcmd.link_exists(xfrm_ifacename):
try:
netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
except:
self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
link_created = True
if not self.cache.link_exists(xfrm_ifacename):
self.iproute2.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
else:
current_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
xfrmid_cur = current_attrs.get('xfrm-id', None)
physdev_cur = current_attrs.get('xfrm-physdev', None)
xfrmid_cur = str(
self.cache.get_link_info_data_attribute(
xfrm_ifacename,
Link.IFLA_XFRM_IF_ID,
0
)
)
physdev_cur = self.cache.get_ifname(
self.cache.get_link_info_data_attribute(
xfrm_ifacename,
Link.IFLA_XFRM_LINK,
0
)
)
# Check XFRM Values
if xfrmid != xfrmid_cur or physdev != physdev_cur:
# Delete and recreate
self.ipcmd.link_delete(xfrm_ifacename)
try:
netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
except:
self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
link_created = True
self.netlink.link_del(xfrm_ifacename)
self.iproute2.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
def _down(self, ifaceobj, ifaceobj_getfunc=None):
"""
@@ -122,17 +130,17 @@ class xfrm(moduleBase):
"""
try:
xfrm_ifacename = self._get_xfrm_name(ifaceobj)
self.ipcmd.link_delete(xfrm_ifacename)
except Exception, e:
self.netlink.link_del(xfrm_ifacename)
except Exception as e:
self.log_warn(str(e))
def _query_check(self, ifaceobj, ifaceobjcurr):
if not self.ipcmd.link_exists(ifaceobj.name):
if not self.cache.link_exists(ifaceobj.name):
return
ifaceobjcurr.status = ifaceStatus.SUCCESS
def _query_running(self, ifaceobjrunning):
if not self.ipcmd.link_exists(ifaceobjrunning.name):
if not self.cache.link_exists(ifaceobjrunning.name):
return
# Operations supported by this addon (yet).
@@ -146,10 +154,6 @@ class xfrm(moduleBase):
def get_ops(self):
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
op_handler = self._run_ops.get(operation)
@@ -159,7 +163,6 @@ class xfrm(moduleBase):
if operation != 'query-running' and not self._is_my_interface(ifaceobj):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:

View File

@@ -12,10 +12,10 @@ import argcomplete
try:
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.exceptions import ArgvParseError
from ifupdown2.ifupdown.exceptions import ArgvParseError, ArgvParseHelp
except:
from ifupdown.utils import utils
from ifupdown.exceptions import ArgvParseError
from ifupdown.exceptions import ArgvParseError, ArgvParseHelp
class VersionAction(argparse.Action):
@@ -78,7 +78,18 @@ class Parse:
self.update_ifquery_argparser(argparser)
self.update_common_argparser(argparser)
argcomplete.autocomplete(argparser)
self.args = argparser.parse_args(self.argv)
try:
self.args = argparser.parse_args(self.argv)
except SystemExit as e:
# on "--help" parse_args will raise SystemExit.
# We need to catch this behavior and raise a custom
# exception to return 0 properly
#raise ArgvParseHelp()
for help_str in ('-h', '--help'):
if help_str in argv:
raise ArgvParseHelp()
raise
def validate(self):
if self.op == 'query' and (self.args.syntaxhelp or self.args.list):
@@ -102,8 +113,9 @@ class Parse:
for key, value in self.valid_ops.iteritems():
if self.executable_name.endswith(key):
return value
raise ArgvParseError("Unexpected executable. Should be '%s'" % "' or '".join(self.valid_ops.keys()))
except:
raise ArgvParseError("Unexpected executable. Should be 'ifup' or 'ifdown' or 'ifquery'")
raise ArgvParseError("Unexpected executable. Should be '%s'" % "' or '".join(self.valid_ops.keys()))
def get_args(self):
return self.args
@@ -140,7 +152,7 @@ class Parse:
def update_ifupdown_argparser(self, argparser):
""" common arg parser for ifup and ifdown """
argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true')
group = argparser.add_mutually_exclusive_group(required=False)
group.add_argument('-n', '--no-act', dest='noact', action='store_true',
help="print out what would happen, but don't do it")
@@ -220,7 +232,7 @@ class Parse:
'With this option ifreload will only look at the current interfaces file. '
'Useful when your state file is corrupted or you want down to use the latest '
'from the interfaces file')
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true')
argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck', action='store_true',
help='Only run the interfaces file parser')

View File

@@ -0,0 +1,232 @@
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# ifupdown2 client-side
#
import struct
import pickle
import SocketServer
import logging
import logging.handlers
import os
import re
import sys
import json
import socket
import signal
try:
from ifupdown2.lib.io import SocketIO
from ifupdown2.lib.status import Status
from ifupdown2.lib.log import LogManager, root_logger
from ifupdown2.lib.exceptions import ExitWithStatus, ExitWithStatusAndError
from ifupdown2.ifupdown.argv import Parse
except:
from lib.status import Status
from lib.io import SocketIO
from lib.log import LogManager, root_logger
from lib.exceptions import ExitWithStatus, ExitWithStatusAndError
from ifupdown.argv import Parse
class LogRecordStreamHandler(SocketServer.StreamRequestHandler):
"""
Handler for a streaming logging request.
This basically logs the record using whatever logging policy is configured
locally.
"""
def handle(self):
"""
Handle multiple requests - each expected to be a 4-byte length,
followed by the LogRecord in pickle format.
"""
while True:
chunk = self.connection.recv(4)
if len(chunk) < 4:
break
slen = struct.unpack(">L", chunk)[0]
chunk = self.connection.recv(slen)
while len(chunk) < slen:
chunk = chunk + self.connection.recv(slen - len(chunk))
record = logging.makeLogRecord(pickle.loads(chunk))
logging.getLogger(record.name).handle(record)
class LogRecordSocketReceiver(SocketServer.TCPServer):
"""
Simple TCP socket-based logging receiver. In ifupdown2d context, the running
daemon is the "sender" and the client is the "receiver". The TCPServer is
setup on the client/receiver side, the daemon will connect to the server to
transmit and stream LogRecord to a socket.
"""
allow_reuse_address = True
def __init__(
self,
host="localhost",
handler=LogRecordStreamHandler,
port=LogManager.DEFAULT_TCP_LOGGING_PORT
):
SocketServer.TCPServer.__init__(self, (host, port), handler)
class Client(SocketIO):
def __init__(self, argv):
SocketIO.__init__(self)
# we setup our log receiver which reads LogRecord from a socket
# thus handing the logging-handling to the logging module and it's
# dedicated classes.
self.socket_receiver = LogRecordSocketReceiver()
self.stdin = None
self.argv = argv
# First we need to set the correct log level for the client.
# Unfortunately the only reliable way to do this is to use our main
# argument parser. We can't simply have a parse to catch -v and -d, a
# simple command like "ifup -av" wouldn't be recognized...
# Ideally it would be great to be able to send the Namespace returned by
# parse_args, and send it on the socket in pickle format. Unfortunately
# this might be a serious security issue. It needs to be studied and
# evaluated a bit more, that way we would save time and resources only
# parsing argv once.
args_parse = Parse(argv)
args_parse.validate()
# store the args namespace to send it to the daemon, we don't want
# the daemon to spend time to parsing argv again...
self.args = args_parse.get_args()
LogManager.get_instance().start_client_logging(self.args)
root_logger.info("starting ifupdown2 client...")
self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
self.uds.connect("/var/run/ifupdown2d/uds")
except socket.error:
self.__shutdown()
sys.stderr.write("""
ERROR: %s could not connect to ifupdown2 daemon
Try starting ifupdown2 daemon with:
sudo systemctl start ifupdown2
To configure ifupdown2d to start when the box boots:
sudo systemctl enable ifupdown2\n\n""" % argv[0])
raise ExitWithStatus(status=Status.Client.STATUS_COULD_NOT_CONNECT)
signal.signal(signal.SIGINT, self.__signal_handler)
signal.signal(signal.SIGTERM, self.__signal_handler)
signal.signal(signal.SIGQUIT, self.__signal_handler)
try:
self.SO_PEERCRED = socket.SO_PEERCRED
except AttributeError:
# powerpc is the only non-generic we care about. alpha, mips,
# sparc, and parisc also have non-generic values.
machine = os.uname()[4]
if re.search(r"^(ppc|powerpc)", machine):
self.SO_PASSCRED = 20
self.SO_PEERCRED = 21
else:
self.SO_PASSCRED = 16
self.SO_PEERCRED = 17
try:
self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
except Exception as e:
self.__shutdown()
raise Exception("setsockopt: %s" % str(e))
self.daemon_pid, _, _ = self.get_socket_peer_cred(self.uds)
if self.daemon_pid < 0:
self.__shutdown()
raise ExitWithStatusAndError(
status=Status.Client.STATUS_NO_PID,
message="could not get ifupdown2 daemon PID"
)
root_logger.info("connection to ifupdown2d successful (server pid %s)" % self.daemon_pid)
def __shutdown(self):
try:
self.uds.close()
self.uds = None
except:
pass
try:
self.socket_receiver.server_close()
self.socket_receiver = None
except:
pass
def __signal_handler(self, sig, frame):
""" Forward all signals to daemon """
if self.daemon_pid > 0:
os.kill(self.daemon_pid, sig)
def __get_stdin(self):
"""
If stdin data is provided we need to store it to forward it to the
daemon
"""
if hasattr(self.args, "interfacesfile") and self.args.interfacesfile == "-":
return sys.stdin.read()
def run(self):
try:
# First we need to send the user request to the daemon (argv + stdin)
self.tx_data(self.uds, json.dumps({
"argv": self.argv,
"stdin": self.__get_stdin()
}))
# Then "handle_request" will block until the daemon closes
# the channel, meaning that the request was processed.
self.socket_receiver.handle_request()
self.socket_receiver.server_close()
# Next the daemon should send us a dictionary containing stdout and
# stderr buffers as well as the request's exit status. We print those
# buffers in the correct channel and exit with the request status.
response = self.rx_json_packet(self.uds)
if response:
sys.stdout.write(response.get("stdout", ""))
sys.stderr.write(response.get("stderr", ""))
status = response.get("status", Status.Client.STATUS_EMPTY)
else:
status = Status.Client.STATUS_EMPTY
return status
finally:
self.__shutdown()

View File

@@ -1,5 +1,3 @@
#!/usr/bin/python
#
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
# Authors:
# Roopa Prabhu, roopa@cumulusnetworks.com
@@ -9,10 +7,9 @@
# exceptions
#
try:
from ifupdown2.ifupdown.log import log
except:
from ifupdown.log import log
import logging
log = logging.getLogger()
class Error(Exception):
@@ -38,6 +35,14 @@ class ArgvParseError(Error):
pass
class ArgvParseHelp(Error):
"""
When ifupdown2 is called with --help argparse raise SystemExit
we need to catch this to properly print the help and exit 0 not 1
"""
pass
class ifaceNotFoundError(Error):
pass

View File

@@ -14,9 +14,12 @@ file. But can be extended to include any other network interface format
"""
import json
import logging
from collections import OrderedDict
log = logging.getLogger()
class ifaceStatusUserStrs():
""" This class declares strings user can see during an ifquery --check
@@ -47,16 +50,16 @@ class ifaceLinkKind():
bond have an ifaceobj.role attribute of SLAVE and the bridge or
bond itself has ifaceobj.role of MASTER.
"""
UNKNOWN = 0x0000000
BRIDGE = 0x0000001
BOND = 0x0000010
VLAN = 0x0000100
VXLAN = 0x0001000
VRF = 0x0010000
BATMAN_ADV = 0x0100000
UNKNOWN = 0x000000
BRIDGE = 0x000001
BOND = 0x000010
VLAN = 0x000100
VXLAN = 0x001000
VRF = 0x010000
BATMAN_ADV = 0x0100000
# to indicate logical interface created by an external entity.
# the 'kind' of which ifupdown2 does not really understand
OTHER = 0x1000000
OTHER = 0x100000
@classmethod
def to_str(cls, kind):
@@ -70,6 +73,8 @@ class ifaceLinkKind():
return "vxlan"
elif kind == cls.VRF:
return "vrf"
else:
return "OTHER"
class ifaceLinkPrivFlags():
""" This corresponds to kernel netdev->priv_flags
@@ -86,33 +91,34 @@ class ifaceLinkPrivFlags():
@classmethod
def get_str(cls, flag):
if flag == cls.UNKNOWN:
return 'unknown'
elif flag == cls.BRIDGE_PORT:
return 'bridge port'
elif flag == cls.BOND_SLAVE:
return 'bond slave'
elif flag == cls.VRF_SLAVE:
return 'vrf slave'
elif flag == cls.BRIDGE_VLAN_AWARE:
return 'vlan aware bridge'
elif flag == cls.BRIDGE_VXLAN:
return 'vxlan bridge'
string_list = []
if flag & cls.BRIDGE_PORT:
string_list.append("bridge port")
if flag & cls.BOND_SLAVE:
string_list.append("bond slave")
if flag & cls.VRF_SLAVE:
string_list.append("vrf slave")
if flag & cls.BRIDGE_VLAN_AWARE:
string_list.append("vlan aware bridge")
if flag & cls.BRIDGE_VXLAN:
string_list.append("vxlan bridge")
if flag & cls.ADDRESS_VIRTUAL_SLAVE:
string_list.append("address virtual slave")
if flag & cls.LOOPBACK:
string_list.append("loopback")
if flag & cls.KEEP_LINK_DOWN:
string_list.append("keep ling down")
return ", ".join(string_list)
@classmethod
def get_all_str(cls, flags):
str = ''
if flags & cls.BRIDGE_PORT:
str += 'bridgeport '
if flags & cls.BOND_SLAVE:
str += 'bondslave '
if flags & cls.VRF_SLAVE:
str += 'vrfslave '
if flags & cls.BRIDGE_VLAN_AWARE:
str += 'vlanawarebridge '
if flags & cls.BRIDGE_VXLAN:
str += 'vxlanbridge '
return str
class ifaceLinkType():
LINK_UNKNOWN = 0x0
@@ -647,14 +653,13 @@ class iface():
del odict['env']
del odict['link_type']
del odict['link_kind']
del odict['link_privflags']
#del odict['link_privflags']
del odict['role']
del odict['dependency_type']
del odict['blacklisted']
return odict
def __setstate__(self, dict):
self.__dict__.update(dict)
self._config_status = {}
self.state = ifaceState.NEW
self.status = ifaceStatus.UNKNOWN
@@ -674,6 +679,7 @@ class iface():
self.link_privflags = ifaceLinkPrivFlags.UNKNOWN
self.dependency_type = ifaceDependencyType.UNKNOWN
self.blacklisted = False
self.__dict__.update(dict)
def dump_raw(self, logger):
indent = ' '
@@ -706,6 +712,24 @@ class iface():
else:
logger.info(indent + 'upperdevs: None')
logger.info("%srole: %s" % (indent, {
ifaceRole.UNKNOWN: "UNKNOWN",
ifaceRole.SLAVE: "SLAVE",
ifaceRole.MASTER: "MASTER"}.get(self.role)))
logger.info("%stype: %s" % (indent, {
ifaceType.UNKNOWN: "UNKNOWN",
ifaceType.IFACE: "IFACE",
ifaceType.BRIDGE_VLAN: "BRIDGE_VLAN"}.get(self.type)))
logger.info("%slink_kind: %s" % (indent, ifaceLinkKind.to_str(self.link_kind)))
logger.info("%slink_privflags: %s" % (indent, ifaceLinkPrivFlags.get_str(self.link_privflags)))
if self.priv_flags:
if self.priv_flags.BUILTIN:
logger.info("%spriv_flags: BUILTIN" % indent)
logger.info(indent + 'config: ')
config = self.config
if config:
@@ -768,7 +792,8 @@ class iface():
outbuf += (indent + '{0:55} {1:>10}'.format(
'%s %s' %(cname, cv), status_str)) + '\n'
else:
outbuf += indent + '%s %s\n' %(cname, cv)
if cv:
outbuf += indent + '%s %s\n' % (cname, cv)
idx += 1
if with_status:
outbuf = (outbuf.encode('utf8')

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env python
#
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
#
# Author: Scott Feldman, sfeldma@cumulusnetworks.com
#
#
# from /usr/include/linux/if.h
#
# Standard interface flags (netdevice->flags).
IFF_UP = 0x1 # interface is up
IFF_BROADCAST = 0x2 # broadcast address valid
IFF_DEBUG = 0x4 # turn on debugging
IFF_LOOPBACK = 0x8 # is a loopback net
IFF_POINTOPOINT = 0x10 # interface is has p-p link
IFF_NOTRAILERS = 0x20 # avoid use of trailers
IFF_RUNNING = 0x40 # interface RFC2863 OPER_UP
IFF_NOARP = 0x80 # no ARP protocol
IFF_PROMISC = 0x100 # receive all packets
IFF_ALLMULTI = 0x200 # receive all multicast packets
IFF_MASTER = 0x400 # master of a load balancer
IFF_SLAVE = 0x800 # slave of a load balancer
IFF_MULTICAST = 0x1000 # Supports multicast
IFF_PORTSEL = 0x2000 # can set media type
IFF_AUTOMEDIA = 0x4000 # auto media select active
IFF_DYNAMIC = 0x8000 # dialup device with changing addresses
IFF_LOWER_UP = 0x10000 # driver signals L1 up
IFF_DORMANT = 0x20000 # driver signals dormant
IFF_ECHO = 0x40000 # echo sent packets

View File

@@ -1,58 +0,0 @@
#!/usr/bin/python
#
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
# ifupdownBase --
# base object for various ifupdown objects
#
import re
import os
import logging
import traceback
try:
from ifupdown2.ifupdown.netlink import netlink
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
except ImportError:
from ifupdown.netlink import netlink
import ifupdown.ifupdownflags as ifupdownflags
class ifupdownBase(object):
def __init__(self):
modulename = self.__class__.__name__
self.logger = logging.getLogger('ifupdown.' + modulename)
def ignore_error(self, errmsg):
if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
re.IGNORECASE | re.MULTILINE) is not None):
return True
return False
def log_warn(self, str):
if self.ignore_error(str) == False:
if self.logger.getEffectiveLevel() == logging.DEBUG:
traceback.print_stack()
traceback.print_exc()
self.logger.warn(str)
pass
def log_error(self, str):
if self.ignore_error(str) == False:
raise Exception(str)
else:
pass
def link_exists(self, ifacename):
return os.path.exists('/sys/class/net/%s' %ifacename)
def link_up(self, ifacename):
netlink.link_set_updown(ifacename, "up")
def link_down(self, ifacename):
netlink.link_set_updown(ifacename, "down")

View File

@@ -7,6 +7,10 @@
# ifupdown main module
#
import re
import os
import logging
import traceback
import pprint
from collections import OrderedDict
@@ -14,12 +18,11 @@ from collections import OrderedDict
from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPAddress, IPv4Address, IPv6Address
try:
import ifupdown2.ifupdownaddons.cache
import ifupdown2.ifupdownaddons.LinkUtils
import ifupdown2.lib.nlcache as nlcache
import ifupdown2.ifupdownaddons.mstpctlutil
import ifupdown2.ifupdown.policymanager
import ifupdown2.ifupdown.ifupdownflags
import ifupdown2.ifupdown.statemanager as statemanager
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
import ifupdown2.ifupdown.ifupdownconfig as ifupdownConfig
@@ -31,8 +34,8 @@ try:
from ifupdown2.ifupdown.networkinterfaces import *
from ifupdown2.ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
except ImportError:
import ifupdownaddons.cache
import ifupdownaddons.LinkUtils
import lib.nlcache as nlcache
import ifupdownaddons.mstpctlutil
import ifupdown.ifupdownflags
@@ -80,7 +83,7 @@ class ifacePrivFlags():
self.BUILTIN = builtin
self.NOCONFIG = noconfig
class ifupdownMain(ifupdownBase):
class ifupdownMain:
""" ifupdown2 main class """
scripts_dir = '/etc/network'
@@ -111,7 +114,7 @@ class ifupdownMain(ifupdownBase):
if self._keep_link_down(ifaceobj):
return
try:
self.link_up(ifaceobj.name)
self.netlink.link_up(ifaceobj.name)
except:
if ifaceobj.addr_method == 'manual':
pass
@@ -123,7 +126,7 @@ class ifupdownMain(ifupdownBase):
# user has asked to explicitly keep the link down,
# so, force link down
self.logger.info('%s: keeping link down due to user config' %ifaceobj.name)
self.link_down(ifaceobj.name)
self.netlink.link_down(ifaceobj.name)
return True
return False
@@ -147,7 +150,11 @@ class ifupdownMain(ifupdownBase):
if not self.link_exists(ifaceobj.name):
return
try:
self.link_down(ifaceobj.name)
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
# set intf down (except loopback)
self.netlink.link_down(ifaceobj.name)
else:
self.logger.info("%s: ifupdown2 cannot bring loopback interface down" % ifaceobj.name)
except:
if ifaceobj.addr_method == 'manual':
pass
@@ -169,17 +176,48 @@ class ifupdownMain(ifupdownBase):
sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
def reset_ifupdown2(self):
self.modules = OrderedDict({})
self.module_attrs = {}
ifaceScheduler.reset()
try:
ifupdown2.ifupdown.statemanager.reset()
ifupdown2.ifupdown.policymanager.reset()
ifupdown2.ifupdown.ifupdownflags.reset()
ifupdownConfig.reset()
ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
except:
try:
ifupdown.statemanager.reset()
ifupdown.policymanager.reset()
ifupdown.ifupdownflags.reset()
ifupdownConfig.reset()
ifupdownaddons.mstpctlutil.mstpctlutil.reset()
except:
pass
ifupdown2.ifupdown.statemanager.reset()
ifupdown2.ifupdown.policymanager.reset()
ifupdown2.ifupdown.ifupdownflags.reset()
ifupdownConfig.reset()
ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
ifupdown2.ifupdownaddons.LinkUtils.LinkUtils.reset()
def ignore_error(self, errmsg):
if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
re.IGNORECASE | re.MULTILINE) is not None):
return True
return False
ifupdown2.ifupdownaddons.cache.linkCache.reset()
ifupdown2.ifupdownaddons.cache.MSTPAttrsCache.invalidate()
def log_warn(self, str):
if self.ignore_error(str) == False:
if self.logger.getEffectiveLevel() == logging.DEBUG:
traceback.print_stack()
traceback.print_exc()
self.logger.warn(str)
pass
def log_error(self, str):
if self.ignore_error(str) == False:
raise Exception(str)
else:
pass
def link_exists(self, ifacename):
return os.path.exists('/sys/class/net/%s' %ifacename)
def __init__(self, config={},
daemon=False, force=False, dryrun=False, nowait=False,
@@ -202,8 +240,21 @@ class ifupdownMain(ifupdownBase):
Raises:
AttributeError, KeyError """
modulename = self.__class__.__name__
self.logger = logging.getLogger('ifupdown.' + modulename)
if daemon:
self.reset_ifupdown2()
else:
# init nlcache with appropriate log level
nlcache.NetlinkListenerWithCache.init(logging.root.level)
# start netlink listener and cache link/addr/netconf dumps
nlcache.NetlinkListenerWithCache.get_instance().start()
# save reference to nlcache
self.netlink = nlcache.NetlinkListenerWithCache.get_instance()
self.netlink.reset_errorq()
# iface dictionary in the below format:
# { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
@@ -444,9 +495,6 @@ class ifupdownMain(ifupdownBase):
else:
return ifaceobjlist[idx]
def get_ifaceobjrunning(self, ifacename):
return self.ifaceobjrunningdict.get(ifacename)
def get_iface_refcnt(self, ifacename):
""" Return iface ref count """
max = 0
@@ -522,7 +570,7 @@ class ifupdownMain(ifupdownBase):
(role == ifaceRole.SLAVE) and (upperifaceobj.role & ifaceRole.MASTER)):
self.logger.error("misconfig..? %s %s is enslaved to multiple interfaces %s"
%(ifaceobj.name,
ifaceLinkPrivFlags.get_all_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
ifaceLinkPrivFlags.get_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
ifaceobj.set_status(ifaceStatus.ERROR)
return
ifaceobj.role = role
@@ -855,13 +903,12 @@ class ifupdownMain(ifupdownBase):
def _keyword_mac_ip_prefixlen_list(self, value, validrange=None):
"""
<mac> <ip> [<ip> ...]
MAC address followed by optional list of ip addresses
<mac> [<ip> <ip> ...]
ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
"""
try:
res = value.split()
if len(res) < 2:
return False
if not self._keyword_mac(res[0]):
return False
for ip in res[1:]:
@@ -1254,7 +1301,7 @@ class ifupdownMain(ifupdownBase):
continue
return ret
def read_iface_config(self):
def read_iface_config(self, raw=False):
""" Reads default network interface config /etc/network/interfaces. """
ret = True
nifaces = networkInterfaces(self.interfacesfile,
@@ -1262,7 +1309,8 @@ class ifupdownMain(ifupdownBase):
self.interfacesfileformat,
template_enable=self.config.get('template_enable', 0),
template_engine=self.config.get('template_engine'),
template_lookuppath=self.config.get('template_lookuppath'))
template_lookuppath=self.config.get('template_lookuppath'),
raw=raw)
if self._ifaceobj_squash or self._ifaceobj_squash_internal:
nifaces.subscribe('iface_found', self._save_iface_squash)
else:
@@ -1331,7 +1379,7 @@ class ifupdownMain(ifupdownBase):
script_override = minstance.get_overrides_ifupdown_scripts()
self.overridden_ifupdown_scripts.extend(script_override)
except moduleNotSupported, e:
self.logger.info('module %s not loaded (%s)\n'
self.logger.info('module %s not loaded (%s)'
%(mname, str(e)))
continue
except:
@@ -1598,9 +1646,9 @@ class ifupdownMain(ifupdownBase):
if not self._delay_admin_state_iface_queue:
return
if op == 'up':
func = self.link_up
func = self.netlink.link_up
elif op == 'down':
func = self.link_down
func = self.netlink.link_down
else:
return
for i in self._delay_admin_state_iface_queue:
@@ -1611,6 +1659,38 @@ class ifupdownMain(ifupdownBase):
self.logger.warn(str(e))
pass
def _get_iface_exclude_companion(self, ifacename):
try:
return ifupdown2.ifupdown.policymanager.policymanager_api.get_iface_default(
module_name='main', ifname=ifacename,
attr='exclude-companion')
except:
return ifupdown.policymanager.policymanager_api.get_iface_default(
module_name='main', ifname=ifacename,
attr='exclude-companion')
def _preprocess_excludepats(self, excludepats):
new_excludepats = excludepats
for e in excludepats:
ifaceobjs = self.get_ifaceobjs(e)
for iobj in ifaceobjs or []:
ec = iobj.get_attr_value_first('exclude-companion')
if not ec:
ec = self._get_iface_exclude_companion(e)
if not ec:
continue
else:
ec = ec.encode('ascii','ignore')
for ee in ec.split():
if ee in new_excludepats:
continue
if self.get_ifaceobjs(ee):
# if we know the object add it to the new
# excludepats list
new_excludepats.append(ee)
self.logger.info('excludepats after processing companions [%s]' %' '.join(new_excludepats))
return new_excludepats
def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
excludepats=None, printdependency=None, syntaxcheck=False,
type=None, skipupperifaces=False):
@@ -1642,6 +1722,9 @@ class ifupdownMain(ifupdownBase):
except Exception:
raise
if excludepats:
excludepats = self._preprocess_excludepats(excludepats)
filtered_ifacenames = None
if ifacenames:
ifacenames = self._preprocess_ifacenames(ifacenames)
@@ -1695,6 +1778,35 @@ class ifupdownMain(ifupdownBase):
if not iface_read_ret or not ret:
raise Exception()
self.check_running_configuration(filtered_ifacenames)
def check_running_configuration(self, filtered_ifacenames, all=False):
"""
Print warning and better info message when we don't recognize an interface
AKA when the interface wasn't created.
:param filtered_ifacenames:
:param all:
:return:
"""
if not filtered_ifacenames:
filtered_ifacenames = []
for ifname, ifaceobj_list in self.ifaceobjdict.iteritems():
if not all and ifname not in filtered_ifacenames:
continue
auto = True
for ifaceobj in ifaceobj_list:
if not ifaceobj.auto:
auto = False
break
if auto and not os.path.exists("/sys/class/net/%s" % ifname):
self.logger.warning("%s: interface not recognized - please check interface configuration" % ifname)
def _get_filtered_ifacenames_with_classes(self, auto, allow_classes, excludepats, ifacenames):
# if user has specified ifacelist and allow_classes
# append the allow_classes interfaces to user
@@ -1738,6 +1850,10 @@ class ifupdownMain(ifupdownBase):
self.read_iface_config()
except Exception, e:
raise Exception('error reading iface config (%s)' %str(e))
if excludepats:
excludepats = self._preprocess_excludepats(excludepats)
filtered_ifacenames = None
if ifacenames:
# If iface list is given by the caller, always check if iface
@@ -1817,7 +1933,7 @@ class ifupdownMain(ifupdownBase):
ifacePrivFlags(False, True)), ifacenames)
else:
try:
iface_read_ret = self.read_iface_config()
iface_read_ret = self.read_iface_config(raw=ops[0] == "query-raw")
except Exception:
raise
@@ -1859,7 +1975,7 @@ class ifupdownMain(ifupdownBase):
if ops[0] == 'query' and not ifupdownflags.flags.WITHDEFAULTS:
return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
elif ops[0] == 'query-raw':
return self.print_ifaceobjs_raw(filtered_ifacenames)
return self.print_ifaceobjs_raw(filtered_ifacenames, format)
ret = self._sched_ifaces(filtered_ifacenames, ops,
followdependents=True
@@ -2028,6 +2144,9 @@ class ifupdownMain(ifupdownBase):
new_ifaceobjdict = self.ifaceobjdict
new_dependency_graph = self.dependency_graph
if excludepats:
excludepats = self._preprocess_excludepats(excludepats)
if op == 'reload' and ifacenames:
ifacenames = self.ifaceobjdict.keys()
old_filtered_ifacenames = [i for i in ifacenames
@@ -2236,6 +2355,8 @@ class ifupdownMain(ifupdownBase):
else:
self._reload_default(*args, **kargs)
self.check_running_configuration([], all=True)
def _any_iface_errors(self, ifacenames):
for i in ifacenames:
ifaceobjs = self.get_ifaceobjs(i)
@@ -2271,9 +2392,13 @@ class ifupdownMain(ifupdownBase):
for i in ifacenames:
print i
def print_ifaceobjs_raw(self, ifacenames):
def print_ifaceobjs_raw(self, ifacenames, format=None):
""" prints raw lines for ifaces from config file """
if format == "json":
self.print_ifaceobjs_pretty(ifacenames, format)
return
for i in ifacenames:
for ifaceobj in self.get_ifaceobjs(i):
if self.is_ifaceobj_builtin(ifaceobj):
@@ -2310,7 +2435,7 @@ class ifupdownMain(ifupdownBase):
if not ifaceobjs: return
if format == 'json':
print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
indent=4, separators=(',', ': '))
indent=2, separators=(',', ': '))
else:
expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
for i in ifaceobjs:

View File

@@ -1,169 +0,0 @@
#!/usr/bin/env python
#
# Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved.
# Author: Julien Fortin, julien@cumulusnetworks.com
#
import sys
import json
import struct
import select
import logging
import logging.handlers
from cStringIO import StringIO
class Log:
LOGGER_NAME = sys.argv[0].split('/')[-1]
def __init__(self):
"""
- On start the daemon will log on syslog.
- For each client commands we might need to adjust the target
(stderr/stdout):
if -v --verbose or -d --debug are provided we override
sys.stdout and sys.stderr with string buffers to be able to send
back the content of these buffer on the UNIX socket back to the
client.
if -l or --syslog we make sure to use syslog.
"""
self.stdout_buffer = None
self.stderr_buffer = None
self.root = logging.getLogger()
self.root.name = Log.LOGGER_NAME
self.root.setLevel(logging.INFO)
self.root_info = self.root.info
self.root_debug = self.root.debug
self.root_error = self.root.error
self.root_warning = self.root.warning
self.root_critical = self.root.critical
self.root.info = self.info
self.root.debug = self.debug
self.root.error = self.error
self.root.warning = self.warning
self.root.critical = self.critical
logging.addLevelName(logging.CRITICAL, 'critical')
logging.addLevelName(logging.WARNING, 'warning')
logging.addLevelName(logging.ERROR, 'error')
logging.addLevelName(logging.DEBUG, 'debug')
logging.addLevelName(logging.INFO, 'info')
self.syslog = True
self.socket = None
# syslog
facility = logging.handlers.SysLogHandler.LOG_DAEMON
address = '/dev/log'
format = '%(name)s: %(levelname)s: %(message)s'
try:
self.syslog_handler = logging.handlers.SysLogHandler(address=address, facility=facility)
self.syslog_handler.setFormatter(logging.Formatter(format))
except Exception as e:
sys.stderr.write("warning: syslogs: %s\n" % str(e))
self.syslog_handler = None
# console
format = '%(levelname)s: %(message)s'
self.console_handler = logging.StreamHandler(sys.stderr)
self.console_handler.setFormatter(logging.Formatter(format))
if self.syslog_handler and self.LOGGER_NAME[-1] == 'd':
self.update_current_logger(syslog=True, verbose=True, debug=False)
else:
self.update_current_logger(syslog=False, verbose=False, debug=False)
def update_current_logger(self, syslog, verbose, debug):
self.syslog = syslog
self.root.setLevel(self.get_log_level(verbose=verbose, debug=debug))
self.root.handlers = [self.syslog_handler if self.syslog and self.syslog_handler else self.console_handler]
self.flush()
def flush(self):
if self.socket:
result = dict()
stdout = self._flush_buffer('stdout', self.stdout_buffer, result)
stderr = self._flush_buffer('stderr', self.stderr_buffer, result)
if stdout or stderr:
try:
self.tx_data(json.dumps(result))
self.redirect_stdouput()
except select.error as e:
# haven't seen the case yet
self.socket = None
self.update_current_logger(syslog=True, verbose=True)
self.critical(str(e))
exit(84)
self.console_handler.flush()
if self.syslog_handler:
self.syslog_handler.flush()
def tx_data(self, data, socket=None):
socket_obj = socket if socket else self.socket
ready = select.select([], [socket_obj], [])
if ready and ready[1] and ready[1][0] == socket_obj:
frmt = "=%ds" % len(data)
packed_msg = struct.pack(frmt, data)
packed_hdr = struct.pack('=I', len(packed_msg))
socket_obj.sendall(packed_hdr)
socket_obj.sendall(packed_msg)
def set_socket(self, socket):
self.socket = socket
self.redirect_stdouput()
def redirect_stdouput(self):
self.stdout_buffer = sys.stdout = StringIO()
self.stderr_buffer = self.console_handler.stream = sys.stderr = StringIO()
def error(self, msg, *args, **kwargs):
self.root_error(msg, *args, **kwargs)
self.flush()
def critical(self, msg, *args, **kwargs):
self.root_critical(msg, *args, **kwargs)
self.flush()
def warning(self, msg, *args, **kwargs):
self.root_warning(msg, *args, **kwargs)
self.flush()
def info(self, msg, *args, **kwargs):
self.root_info(msg, *args, **kwargs)
self.flush()
def debug(self, msg, *args, **kwargs):
self.root_debug(msg, *args, **kwargs)
self.flush()
def get_current_log_level(self):
return self.root.level
def is_syslog(self): return self.syslog
@staticmethod
def get_log_level(verbose=False, debug=False):
log_level = logging.WARNING
if debug:
log_level = logging.DEBUG
elif verbose:
log_level = logging.INFO
return log_level
@staticmethod
def _flush_buffer(stream, buff, dictionary):
if buff:
data = buff.getvalue()
if data:
dictionary[stream] = data
return True
log = Log()

View File

@@ -1,6 +1,6 @@
#!/usr/bin/python
#
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
# Copyright 2014-2019 Cumulus Networks, Inc. All rights reserved.
# Authors:
# Roopa Prabhu, roopa@cumulusnetworks.com
# Julien Fortin, julien@cumulusnetworks.com
@@ -10,26 +10,26 @@
import os
import sys
import signal
import logging
import StringIO
import ConfigParser
try:
from ifupdown2.ifupdown.log import log
from ifupdown2.ifupdown.argv import Parse
from ifupdown2.ifupdown.config import IFUPDOWN2_CONF_PATH
from ifupdown2.ifupdown.ifupdownmain import ifupdownMain
from ifupdown2.lib.dry_run import DryRunManager
except ImportError:
from ifupdown.log import log
from ifupdown.argv import Parse
from ifupdown.config import IFUPDOWN2_CONF_PATH
from ifupdown.ifupdownmain import ifupdownMain
from lib.dry_run import DryRunManager
_SIGINT = signal.getsignal(signal.SIGINT)
_SIGTERM = signal.getsignal(signal.SIGTERM)
_SIGQUIT = signal.getsignal(signal.SIGQUIT)
log = logging.getLogger()
configmap_g = None
@@ -57,14 +57,6 @@ class Ifupdown2:
self.args = args_parse.get_args()
self.op = args_parse.get_op()
def update_logger(self, socket=None):
syslog = self.args.syslog if hasattr(self.args, 'syslog') else False
log.update_current_logger(syslog=syslog,
verbose=self.args.verbose,
debug=self.args.debug)
if socket:
log.set_socket(socket)
def main(self, stdin_buffer=None):
if self.op != 'query' and self.uid != 0:
raise Exception('must be root to run this command')
@@ -80,7 +72,7 @@ class Ifupdown2:
raise
# else:
if log:
log.error(str(e))
log.error('main exception: ' + str(e))
else:
print str(e)
# if args and not args.debug:
@@ -89,6 +81,23 @@ class Ifupdown2:
return 0
def init(self, stdin_buffer):
##############
# dry run mode
##############
dry_run_mode_on = DryRunManager.get_instance().is_dry_mode_on()
if hasattr(self.args, 'noact'):
if self.args.noact and not dry_run_mode_on:
DryRunManager.get_instance().dry_run_mode_on()
elif not self.args.noact and dry_run_mode_on:
DryRunManager.get_instance().dry_run_mode_off()
elif dry_run_mode_on:
# if noact is not in self.args we are probably in
# ifquery mode so we need to turn off dry run mode.
DryRunManager.get_instance().dry_run_mode_off()
###
if hasattr(self.args, 'interfacesfile') and self.args.interfacesfile != None:
# Check to see if -i option is allowed by config file
# But for ifquery, we will not check this
@@ -269,9 +278,3 @@ class Ifupdown2:
currentlyup=args.currentlyup)
except:
raise
@staticmethod
def set_signal_handlers():
signal.signal(signal.SIGQUIT, _SIGQUIT)
signal.signal(signal.SIGTERM, _SIGTERM)
signal.signal(signal.SIGINT, _SIGINT)

View File

@@ -1,700 +0,0 @@
#!/usr/bin/python
#
# Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved.
# Author: Julien Fortin, julien@cumulusnetworks.com
#
import sys
import socket
import logging
from collections import OrderedDict
try:
import ifupdown2.nlmanager.nlpacket
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
from ifupdown2.ifupdownaddons.cache import *
from ifupdown2.ifupdownaddons.utilsbase import utilsBase
from ifupdown2.nlmanager.nlmanager import Link, Address, Route, NetlinkPacket
except ImportError:
import nlmanager.nlpacket
import ifupdown.ifupdownflags as ifupdownflags
from ifupdownaddons.cache import *
from ifupdownaddons.utilsbase import utilsBase
from nlmanager.nlmanager import Link, Address, Route, NetlinkPacket
class Netlink(utilsBase):
VXLAN_UDP_PORT = 4789
def __init__(self, *args, **kargs):
utilsBase.__init__(self, *args, **kargs)
try:
sys.path.insert(0, '/usr/share/ifupdown2/')
try:
from ifupdown2.nlmanager.nlmanager import NetlinkManager
# Override the nlmanager's mac_int_to_str function to print the MACs
# like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
ifupdown2.nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str
except ImportError:
from nlmanager.nlmanager import NetlinkManager
# Override the nlmanager's mac_int_to_str function to print the MACs
# like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str
# this should force the use of the local nlmanager
self._nlmanager_api = NetlinkManager(log_level=logging.WARNING)
self.link_kind_handlers = {
'vlan': self._link_dump_info_data_vlan,
'vrf': self._link_dump_info_data_vrf,
'vxlan': self._link_dump_info_data_vxlan,
'bond': self._link_dump_info_data_bond,
'bridge': self._link_dump_info_data_bridge,
'gre': self._link_dump_info_data_gre_tunnel,
'gretap': self._link_dump_info_data_gre_tunnel,
"ip6gre": self._link_dump_info_data_gre_tunnel,
"ip6gretap": self._link_dump_info_data_gre_tunnel,
"ip6erspan": self._link_dump_info_data_gre_tunnel,
'ipip': self._link_dump_info_data_iptun_tunnel,
'sit': self._link_dump_info_data_iptun_tunnel,
'ip6tnl': self._link_dump_info_data_iptun_tunnel,
'vti': self._link_dump_info_data_vti_tunnel,
'vti6': self._link_dump_info_data_vti_tunnel,
'xfrm': self._link_dump_info_data_xfrm
}
except Exception as e:
self.logger.error('cannot initialize ifupdown2\'s '
'netlink manager: %s' % str(e))
raise
@staticmethod
def IN_MULTICAST(a):
"""
/include/uapi/linux/in.h
#define IN_CLASSD(a) ((((long int) (a)) & 0xf0000000) == 0xe0000000)
#define IN_MULTICAST(a) IN_CLASSD(a)
"""
return (int(a) & 0xf0000000) == 0xe0000000
@staticmethod
def mac_int_to_str(mac_int):
"""
Return an integer in MAC string format: xx:xx:xx:xx:xx:xx
"""
return ':'.join(("%012x" % mac_int)[i:i + 2] for i in range(0, 12, 2))
def get_iface_index(self, ifacename):
if ifupdownflags.flags.DRYRUN: return
try:
return self._nlmanager_api.get_iface_index(ifacename)
except Exception as e:
raise Exception('%s: netlink: %s: cannot get ifindex: %s'
% (ifacename, ifacename, str(e)))
def get_iface_name(self, ifindex):
if ifupdownflags.flags.DRYRUN: return
try:
return self._nlmanager_api.get_iface_name(ifindex)
except Exception as e:
raise Exception('netlink: cannot get ifname for index %s: %s' % (ifindex, str(e)))
def get_bridge_vlan(self, ifname):
self.logger.info('%s: netlink: /sbin/bridge -d -c -json vlan show' % ifname)
if ifupdownflags.flags.DRYRUN: return
try:
return self._nlmanager_api.vlan_get()
except Exception as e:
raise Exception('netlink: get bridge vlan: %s' % str(e))
def bridge_set_vlan_filtering(self, ifname, vlan_filtering):
self.logger.info('%s: netlink: ip link set dev %s type bridge vlan_filtering %s'
% (ifname, ifname, vlan_filtering))
if ifupdownflags.flags.DRYRUN: return
try:
ifla_info_data = {Link.IFLA_BR_VLAN_FILTERING: int(vlan_filtering)}
return self._nlmanager_api.link_set_attrs(ifname, 'bridge', ifla_info_data=ifla_info_data)
except Exception as e:
raise Exception('%s: cannot set %s vlan_filtering %s' % (ifname, ifname, vlan_filtering))
def link_add_set(self,
ifname=None, ifindex=0,
kind=None, slave_kind=None,
ifla={},
ifla_info_data={},
ifla_info_slave_data={},
link_exists=False):
action = 'set' if ifindex or link_exists else 'add'
if slave_kind:
self.logger.info('%s: netlink: ip link set dev %s: %s slave attributes' % (ifname, ifname, slave_kind))
else:
self.logger.info('%s: netlink: ip link %s %s type %s with attributes' % (ifname, action, ifname, kind))
if ifla:
self.logger.debug('%s: ifla attributes a %s' % (ifname, ifla))
if ifla_info_data:
self.logger.debug('%s: ifla_info_data %s' % (ifname, ifla_info_data))
if ifla_info_slave_data:
self.logger.debug('%s: ifla_info_slave_data %s' % (ifname, ifla_info_slave_data))
if ifupdownflags.flags.DRYRUN: return
try:
self._nlmanager_api.link_add_set(ifname=ifname,
ifindex=ifindex,
kind=kind,
slave_kind=slave_kind,
ifla=ifla,
ifla_info_data=ifla_info_data,
ifla_info_slave_data=ifla_info_slave_data)
except Exception as e:
if kind and not slave_kind:
kind_str = kind
elif kind and slave_kind:
kind_str = '%s (%s slave)' % (kind, slave_kind)
else:
kind_str = '(%s slave)' % slave_kind
raise Exception('netlink: cannot %s %s %s with options: %s' % (action, kind_str, ifname, str(e)))
def link_del(self, ifname):
self.logger.info('%s: netlink: ip link del %s' % (ifname, ifname))
if ifupdownflags.flags.DRYRUN: return
try:
self._nlmanager_api.link_del(ifname=ifname)
except Exception as e:
raise Exception('netlink: cannot delete link %s: %s' % (ifname, str(e)))
def link_set_master(self, ifacename, master_dev, state=None):
self.logger.info('%s: netlink: ip link set dev %s master %s %s'
% (ifacename, ifacename, master_dev,
state if state else ''))
if ifupdownflags.flags.DRYRUN: return
try:
master = 0 if not master_dev else self.get_iface_index(master_dev)
return self._nlmanager_api.link_set_master(ifacename,
master_ifindex=master,
state=state)
except Exception as e:
raise Exception('netlink: %s: cannot set %s master %s: %s'
% (ifacename, ifacename, master_dev, str(e)))
def link_set_nomaster(self, ifacename, state=None):
self.logger.info('%s: netlink: ip link set dev %s nomaster %s'
% (ifacename, ifacename, state if state else ''))
if ifupdownflags.flags.DRYRUN: return
try:
return self._nlmanager_api.link_set_master(ifacename,
master_ifindex=0,
state=state)
except Exception as e:
raise Exception('netlink: %s: cannot set %s nomaster: %s'
% (ifacename, ifacename, str(e)))
def link_add_vlan(self, vlanrawdevice, ifacename, vlanid, vlan_protocol):
if vlan_protocol:
self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s protocol %s'
% (ifacename, vlanrawdevice, ifacename, vlanid, vlan_protocol))
else:
self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s'
% (ifacename, vlanrawdevice, ifacename, vlanid))
if ifupdownflags.flags.DRYRUN: return
ifindex = self.get_iface_index(vlanrawdevice)
try:
return self._nlmanager_api.link_add_vlan(ifindex, ifacename, vlanid, vlan_protocol)
except Exception as e:
raise Exception('netlink: %s: cannot create vlan %s: %s'
% (vlanrawdevice, vlanid, str(e)))
def link_add_macvlan(self, ifacename, macvlan_ifacename):
self.logger.info('%s: netlink: ip link add link %s name %s type macvlan mode private'
% (ifacename, ifacename, macvlan_ifacename))
if ifupdownflags.flags.DRYRUN: return
ifindex = self.get_iface_index(ifacename)
try:
return self._nlmanager_api.link_add_macvlan(ifindex, macvlan_ifacename)
except Exception as e:
raise Exception('netlink: %s: cannot create macvlan %s: %s'
% (ifacename, macvlan_ifacename, str(e)))
def link_add_xfrm(self, ifacename, xfrm_ifacename, xfrm_id):
self.logger.info('%s: netlink: ip link add %s type xfrm dev %s if_id %s'
% (xfrm_ifacename, xfrm_ifacename, ifacename, xfrm_id))
if ifupdownflags.flags.DRYRUN: return
ifindex = self.get_iface_index(ifacename)
try:
return self._nlmanager_api.link_add_xfrm(ifindex, xfrm_ifacename, xfrm_id)
except Exception as e:
raise Exception('netlink: %s: cannot create xfrm %s id %s: %s'
% (ifacename, xfrm_ifacename, xfrm_id, str(e)))
def link_set_updown(self, ifacename, state):
self.logger.info('%s: netlink: ip link set dev %s %s'
% (ifacename, ifacename, state))
if ifupdownflags.flags.DRYRUN: return
try:
return self._nlmanager_api.link_set_updown(ifacename, state)
except Exception as e:
raise Exception('netlink: cannot set link %s %s: %s'
% (ifacename, state, str(e)))
def link_set_protodown(self, ifacename, state):
self.logger.info('%s: netlink: set link %s protodown %s'
% (ifacename, ifacename, state))
if ifupdownflags.flags.DRYRUN: return
try:
return self._nlmanager_api.link_set_protodown(ifacename, state)
except Exception as e:
raise Exception('netlink: cannot set link %s protodown %s: %s'
% (ifacename, state, str(e)))
def link_add_bridge(self, ifname, mtu=None):
self.logger.info('%s: netlink: ip link add %s type bridge' % (ifname, ifname))
if ifupdownflags.flags.DRYRUN: return
try:
return self._nlmanager_api.link_add_bridge(ifname, mtu=mtu)
except Exception as e:
raise Exception('netlink: cannot create bridge %s: %s' % (ifname, str(e)))
def link_add_bridge_vlan(self, ifacename, vlanid):
self.logger.info('%s: netlink: bridge vlan add vid %s dev %s'
% (ifacename, vlanid, ifacename))
if ifupdownflags.flags.DRYRUN: return
ifindex = self.get_iface_index(ifacename)
try:
return self._nlmanager_api.link_add_bridge_vlan(ifindex, vlanid)
except Exception as e:
raise Exception('netlink: %s: cannot create bridge vlan %s: %s'
% (ifacename, vlanid, str(e)))
def link_del_bridge_vlan(self, ifacename, vlanid):
self.logger.info('%s: netlink: bridge vlan del vid %s dev %s'
% (ifacename, vlanid, ifacename))
if ifupdownflags.flags.DRYRUN: return
ifindex = self.get_iface_index(ifacename)
try:
return self._nlmanager_api.link_del_bridge_vlan(ifindex, vlanid)
except Exception as e:
raise Exception('netlink: %s: cannot remove bridge vlan %s: %s'
% (ifacename, vlanid, str(e)))
def link_add_vxlan(self, ifacename, vxlanid, local=None, dstport=VXLAN_UDP_PORT,
group=None, learning=True, ageing=None, physdev=None, ttl=None):
cmd = 'ip link add %s type vxlan id %s dstport %s' % (ifacename,
vxlanid,
dstport)
cmd += ' local %s' % local if local else ''
cmd += ' ageing %s' % ageing if ageing else ''
cmd += ' remote %s' % group if group else ' noremote'
cmd += ' nolearning' if not learning else ''
cmd += ' dev %s' % physdev if physdev else ''
if ttl is not None:
cmd += ' ttl %s' % ttl
self.logger.info('%s: netlink: %s' % (ifacename, cmd))
if ifupdownflags.flags.DRYRUN: return
try:
if physdev:
physdev = self.get_iface_index(physdev)
return self._nlmanager_api.link_add_vxlan(ifacename,
vxlanid,
dstport=dstport,
local=local,
group=group,
learning=learning,
ageing=ageing,
physdev=physdev,
ttl=ttl)
except Exception as e:
raise Exception('netlink: %s: cannot create vxlan %s: %s'
% (ifacename, vxlanid, str(e)))
@staticmethod
def _link_dump_attr(link, ifla_attributes, dump):
for obj in ifla_attributes:
attr = link.attributes.get(obj['attr'])
if attr:
dump[obj['name']] = attr.get_pretty_value(obj=obj.get('func'))
@staticmethod
def _link_dump_linkdata_attr(linkdata, ifla_linkdata_attr, dump):
for obj in ifla_linkdata_attr:
attr = obj['attr']
if attr in linkdata:
func = obj.get('func')
value = linkdata.get(attr)
if func:
value = func(value)
if value or obj['accept_none']:
dump[obj['name']] = value
ifla_attributes = [
{
'attr': Link.IFLA_LINK,
'name': 'link',
'func': lambda x: netlink.get_iface_name(x) if x > 0 else None
},
{
'attr': Link.IFLA_MASTER,
'name': 'master',
'func': lambda x: netlink.get_iface_name(x) if x > 0 else None
},
{
'attr': Link.IFLA_IFNAME,
'name': 'ifname',
'func': str,
},
{
'attr': Link.IFLA_MTU,
'name': 'mtu',
'func': str
},
{
'attr': Link.IFLA_OPERSTATE,
'name': 'state',
'func': lambda x: '0%x' % int(x) if x > len(Link.oper_to_string) else Link.oper_to_string[x][8:]
},
{
'attr': Link.IFLA_AF_SPEC,
'name': 'af_spec',
'func': dict
}
]
ifla_address = {'attr': Link.IFLA_ADDRESS, 'name': 'hwaddress', 'func': str}
ifla_vxlan_attributes = [
{
'attr': Link.IFLA_VXLAN_LOCAL,
'name': 'local',
'func': str,
'accept_none': True
},
{
'attr': Link.IFLA_VXLAN_LOCAL6,
'name': 'local',
'func': str,
'accept_none': True
},
{
'attr': Link.IFLA_VXLAN_GROUP,
'name': 'svcnode',
'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None,
'accept_none': False
},
{
'attr': Link.IFLA_VXLAN_GROUP6,
'name': 'svcnode',
'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None,
'accept_none': False
},
{
'attr': Link.IFLA_VXLAN_LEARNING,
'name': 'learning',
'func': lambda x: 'on' if x else 'off',
'accept_none': True
}
]
def _link_dump_info_data_vlan(self, ifname, linkdata):
return {
'vlanid': str(linkdata.get(Link.IFLA_VLAN_ID, '')),
'vlan_protocol': linkdata.get(Link.IFLA_VLAN_PROTOCOL)
}
def _link_dump_info_data_vrf(self, ifname, linkdata):
vrf_info = {'table': str(linkdata.get(Link.IFLA_VRF_TABLE, ''))}
# to remove later when moved to a true netlink cache
linkCache.vrfs[ifname] = vrf_info
return vrf_info
def _link_dump_info_data_vxlan(self, ifname, linkdata):
for attr, value in (
('learning', 'on'),
('svcnode', None),
('vxlanid', str(linkdata.get(Link.IFLA_VXLAN_ID, ''))),
('ageing', str(linkdata.get(Link.IFLA_VXLAN_AGEING, ''))),
(Link.IFLA_VXLAN_PORT, linkdata.get(Link.IFLA_VXLAN_PORT))
):
linkdata[attr] = value
self._link_dump_linkdata_attr(linkdata, self.ifla_vxlan_attributes, linkdata)
return linkdata
ifla_bond_attributes = (
Link.IFLA_BOND_MODE,
Link.IFLA_BOND_MIIMON,
Link.IFLA_BOND_USE_CARRIER,
Link.IFLA_BOND_AD_LACP_RATE,
Link.IFLA_BOND_XMIT_HASH_POLICY,
Link.IFLA_BOND_MIN_LINKS,
Link.IFLA_BOND_NUM_PEER_NOTIF,
Link.IFLA_BOND_AD_ACTOR_SYSTEM,
Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
Link.IFLA_BOND_AD_LACP_BYPASS,
Link.IFLA_BOND_UPDELAY,
Link.IFLA_BOND_DOWNDELAY,
)
def _link_dump_info_data_bond(self, ifname, linkdata):
linkinfo = {}
for nl_attr in self.ifla_bond_attributes:
try:
linkinfo[nl_attr] = linkdata.get(nl_attr)
except Exception as e:
self.logger.debug('%s: parsing bond IFLA_INFO_DATA (%s): %s'
% (ifname, nl_attr, str(e)))
return linkinfo
# this dict contains the netlink attribute, cache key,
# and a callable to translate the netlink value into
# whatever value we need to store in the old cache to
# make sure we don't break anything
ifla_bridge_attributes = (
(Link.IFLA_BR_UNSPEC, Link.IFLA_BR_UNSPEC, None),
(Link.IFLA_BR_FORWARD_DELAY, "fd", lambda x: str(x / 100)),
(Link.IFLA_BR_HELLO_TIME, "hello", lambda x: str(x / 100)),
(Link.IFLA_BR_MAX_AGE, "maxage", lambda x: str(x / 100)),
(Link.IFLA_BR_AGEING_TIME, "ageing", lambda x: str(x / 100)),
(Link.IFLA_BR_STP_STATE, "stp", lambda x: 'yes' if x else 'no'),
(Link.IFLA_BR_PRIORITY, "bridgeprio", str),
(Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering', str),
(Link.IFLA_BR_VLAN_PROTOCOL, "vlan-protocol", str),
(Link.IFLA_BR_GROUP_FWD_MASK, Link.IFLA_BR_GROUP_FWD_MASK, None),
(Link.IFLA_BR_ROOT_ID, Link.IFLA_BR_ROOT_ID, None),
(Link.IFLA_BR_BRIDGE_ID, Link.IFLA_BR_BRIDGE_ID, None),
(Link.IFLA_BR_ROOT_PORT, Link.IFLA_BR_ROOT_PORT, None),
(Link.IFLA_BR_ROOT_PATH_COST, Link.IFLA_BR_ROOT_PATH_COST, None),
(Link.IFLA_BR_TOPOLOGY_CHANGE, Link.IFLA_BR_TOPOLOGY_CHANGE, None),
(Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, None),
(Link.IFLA_BR_HELLO_TIMER, Link.IFLA_BR_HELLO_TIMER, None),
(Link.IFLA_BR_TCN_TIMER, Link.IFLA_BR_TCN_TIMER, None),
(Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, None),
(Link.IFLA_BR_GC_TIMER, Link.IFLA_BR_GC_TIMER, None),
(Link.IFLA_BR_GROUP_ADDR, Link.IFLA_BR_GROUP_ADDR, None),
(Link.IFLA_BR_FDB_FLUSH, Link.IFLA_BR_FDB_FLUSH, None),
(Link.IFLA_BR_MCAST_ROUTER, "mcrouter", str),
(Link.IFLA_BR_MCAST_SNOOPING, "mcsnoop", str),
(Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, "mcqifaddr", str),
(Link.IFLA_BR_MCAST_QUERIER, "mcquerier", str),
(Link.IFLA_BR_MCAST_HASH_ELASTICITY, "hashel", str),
(Link.IFLA_BR_MCAST_HASH_MAX, "hashmax", str),
(Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, "mclmc", str),
(Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, "mcsqc", str),
(Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, "mclmi", lambda x: str(x / 100)),
(Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, "mcmi", lambda x: str(x / 100)),
(Link.IFLA_BR_MCAST_QUERIER_INTVL, "mcqpi", lambda x: str(x / 100)),
(Link.IFLA_BR_MCAST_QUERY_INTVL, "mcqi", lambda x: str(x / 100)),
(Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, "mcqri", lambda x: str(x / 100)),
(Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, "mcsqi", lambda x: str(x / 100)),
(Link.IFLA_BR_NF_CALL_IPTABLES, Link.IFLA_BR_NF_CALL_IPTABLES, None),
(Link.IFLA_BR_NF_CALL_IP6TABLES, Link.IFLA_BR_NF_CALL_IP6TABLES, None),
(Link.IFLA_BR_NF_CALL_ARPTABLES, Link.IFLA_BR_NF_CALL_ARPTABLES, None),
(Link.IFLA_BR_VLAN_DEFAULT_PVID, Link.IFLA_BR_VLAN_DEFAULT_PVID, None),
(Link.IFLA_BR_PAD, Link.IFLA_BR_PAD, None),
(Link.IFLA_BR_VLAN_STATS_ENABLED, "vlan-stats", str),
(Link.IFLA_BR_MCAST_STATS_ENABLED, "mcstats", str),
(Link.IFLA_BR_MCAST_IGMP_VERSION, "igmp-version", str),
(Link.IFLA_BR_MCAST_MLD_VERSION, "mld-version", str)
)
def _link_dump_info_data_bridge(self, ifname, linkdata):
linkinfo = {}
for nl_attr, cache_key, func in self.ifla_bridge_attributes:
try:
if func:
linkinfo[cache_key] = func(linkdata.get(nl_attr))
else:
linkinfo[cache_key] = linkdata.get(nl_attr)
# we also store the value in pure netlink,
# to make the transition easier in the future
linkinfo[nl_attr] = linkdata.get(nl_attr)
except Exception as e:
self.logger.error('%s: parsing birdge IFLA_INFO_DATA %s: %s'
% (ifname, nl_attr, str(e)))
return linkinfo
def _link_dump_info_slave_data_bridge(self, ifname, info_slave_data):
return info_slave_data
def _link_dump_info_data_gre_tunnel(self, ifname, info_slave_data):
tunnel_link_ifindex = info_slave_data.get(Link.IFLA_GRE_LINK)
return {
"endpoint": str(info_slave_data.get(Link.IFLA_GRE_REMOTE)),
"local": str(info_slave_data.get(Link.IFLA_GRE_LOCAL)),
"ttl": str(info_slave_data.get(Link.IFLA_GRE_TTL)),
"tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
}
def _link_dump_info_data_iptun_tunnel(self, ifname, info_slave_data):
tunnel_link_ifindex = info_slave_data.get(Link.IFLA_IPTUN_LINK)
return {
"endpoint": str(info_slave_data.get(Link.IFLA_IPTUN_REMOTE)),
"local": str(info_slave_data.get(Link.IFLA_IPTUN_LOCAL)),
"ttl": str(info_slave_data.get(Link.IFLA_IPTUN_TTL)),
"tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
}
def _link_dump_info_data_vti_tunnel(self, ifname, info_slave_data):
tunnel_link_ifindex = info_slave_data.get(Link.IFLA_VTI_LINK)
return {
"endpoint": str(info_slave_data.get(Link.IFLA_VTI_REMOTE)),
"local": str(info_slave_data.get(Link.IFLA_VTI_LOCAL)),
"tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
}
def _link_dump_info_data_xfrm(self, ifname, linkdata):
xfrm_physdev_link_ifindex = linkdata.get(Link.IFLA_XFRM_LINK)
return {
'xfrm-id': str(linkdata.get(Link.IFLA_XFRM_IF_ID, '')),
'xfrm-physdev': self.get_iface_name(xfrm_physdev_link_ifindex) if xfrm_physdev_link_ifindex else ""
}
def _link_dump_linkinfo(self, link, dump):
linkinfo = link.attributes[Link.IFLA_LINKINFO].get_pretty_value(dict)
if linkinfo:
info_kind = linkinfo.get(Link.IFLA_INFO_KIND)
info_data = linkinfo.get(Link.IFLA_INFO_DATA)
info_slave_kind = linkinfo.get(Link.IFLA_INFO_SLAVE_KIND)
info_slave_data = linkinfo.get(Link.IFLA_INFO_SLAVE_DATA)
dump['kind'] = info_kind
dump['slave_kind'] = info_slave_kind
if info_data:
link_kind_handler = self.link_kind_handlers.get(info_kind)
if callable(link_kind_handler):
dump['linkinfo'] = link_kind_handler(dump['ifname'], info_data)
if info_slave_data:
dump['info_slave_data'] = info_slave_data
def link_dump(self, ifname=None):
if ifname:
self.logger.info('netlink: ip link show dev %s' % ifname)
else:
self.logger.info('netlink: ip link show')
if ifupdownflags.flags.DRYRUN: return {}
links = dict()
try:
links_dump = self._nlmanager_api.link_dump(ifname)
except Exception as e:
raise Exception('netlink: link dump failed: %s' % str(e))
for link in links_dump:
try:
dump = dict()
flags = []
for flag, string in Link.flag_to_string.items():
if link.flags & flag:
flags.append(string[4:])
dump['flags'] = flags
dump['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
dump['ifindex'] = str(link.ifindex)
if link.device_type == Link.ARPHRD_ETHER:
self._link_dump_attr(link, [self.ifla_address], dump)
self._link_dump_attr(link, self.ifla_attributes, dump)
if Link.IFLA_LINKINFO in link.attributes:
self._link_dump_linkinfo(link, dump)
links[dump['ifname']] = dump
except Exception as e:
self.logger.warning('netlink: ip link show: %s' % str(e))
return links
def _addr_dump_extract_ifname(self, addr_packet):
addr_ifname_attr = addr_packet.attributes.get(Address.IFA_LABEL)
if addr_ifname_attr:
return addr_ifname_attr.get_pretty_value(str)
else:
return self.get_iface_name(addr_packet.ifindex)
@staticmethod
def _addr_filter(addr_ifname, addr):
return addr_ifname == 'lo' and addr in ['127.0.0.1/8', '::1/128', '0.0.0.0']
def _addr_dump_entry(self, ifaces, addr_packet, addr_ifname, ifa_attr):
attribute = addr_packet.attributes.get(ifa_attr)
if attribute:
address = attribute.get_pretty_value(str)
if hasattr(addr_packet, 'prefixlen'):
address = '%s/%d' % (address, addr_packet.prefixlen)
if self._addr_filter(addr_ifname, address):
return
addr_family = NetlinkPacket.af_family_to_string.get(addr_packet.family)
if not addr_family:
return
ifaces[addr_ifname]['addrs'][address] = {
'type': addr_family,
'scope': addr_packet.scope
}
ifa_address_attributes = [
Address.IFA_ADDRESS,
Address.IFA_LOCAL,
Address.IFA_BROADCAST,
Address.IFA_ANYCAST,
Address.IFA_MULTICAST
]
def addr_dump(self, ifname=None):
if ifname:
self.logger.info('netlink: ip addr show dev %s' % ifname)
else:
self.logger.info('netlink: ip addr show')
ifaces = dict()
addr_dump = self._nlmanager_api.addr_dump()
for addr_packet in addr_dump:
addr_ifname = self._addr_dump_extract_ifname(addr_packet)
if addr_packet.family not in [socket.AF_INET, socket.AF_INET6]:
continue
if ifname and ifname != addr_ifname:
continue
if addr_ifname not in ifaces:
ifaces[addr_ifname] = {'addrs': OrderedDict({})}
for ifa_attr in self.ifa_address_attributes:
self._addr_dump_entry(ifaces, addr_packet, addr_ifname, ifa_attr)
if ifname:
return {ifname: ifaces.get(ifname, {})}
return ifaces
netlink = Netlink()

View File

@@ -30,11 +30,12 @@ class networkInterfaces():
_addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel'],
'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel']}
# tunnel is part of the address family for backward compatibility but is not required.
def __init__(self, interfacesfile='/etc/network/interfaces',
interfacesfileiobuf=None, interfacesfileformat='native',
template_enable='0', template_engine=None,
template_lookuppath=None):
template_lookuppath=None, raw=False):
"""This member function initializes the networkinterfaces parser object.
Kwargs:
@@ -54,7 +55,7 @@ class networkInterfaces():
self.auto_ifaces = []
self.callbacks = {}
self.auto_all = False
self.raw = raw
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
self.callbacks = {'iface_found' : None,
@@ -257,12 +258,14 @@ class networkInterfaces():
self._parse_warn(self._currentfile, lineno,
'%s: unexpected characters in interface name' %ifacename)
ifaceobj.raw_config.append(iface_line)
if self.raw:
ifaceobj.raw_config.append(iface_line)
iface_config = collections.OrderedDict()
for line_idx in range(cur_idx + 1, len(lines)):
l = lines[line_idx].strip(whitespaces)
if self.ignore_line(l) == 1:
ifaceobj.raw_config.append(l)
if self.raw:
ifaceobj.raw_config.append(l)
continue
attrs = re.split(self._ws_split_regex, l, 1)
if self._is_keyword(attrs[0]):
@@ -273,7 +276,8 @@ class networkInterfaces():
self._parse_error(self._currentfile, line_idx,
'iface %s: invalid syntax \'%s\'' %(ifacename, l))
continue
ifaceobj.raw_config.append(l)
if self.raw:
ifaceobj.raw_config.append(l)
attrname = attrs[0]
# preprocess vars (XXX: only preprocesses $IFACE for now)
attrval = re.sub(r'\$IFACE', ifacename, attrs[1])

View File

@@ -40,6 +40,8 @@ class policymanager():
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
self.logger.info("policymanager init")
# we grab the json files from a known location and make sure that
# the defaults_policy is checked first
user_files = glob.glob('/etc/network/ifupdown2/policy.d/*.json')
@@ -64,7 +66,16 @@ class policymanager():
self.logger.debug("policymanager: merging system module %s policy with file %s" % (module, filename))
self.system_policy_array[module].update(system_array[module])
else:
self.system_policy_array[module] = system_array[module]
json_dict = system_array[module]
if isinstance(json_dict, dict):
self.system_policy_array[module] = system_array[module]
elif module != "README":
self.logger.warning(
"file %s contains an invalid policy schema, key "
"\"%s\" contains %s when a dictionary is expected" %
(filename, module, type(json_dict))
)
# take care of user defined policy defaults
self.user_policy_array = {}

View File

@@ -14,22 +14,20 @@ from sets import Set
try:
from ifupdown2.ifupdown.graph import *
from ifupdown2.ifupdown.ifupdownbase import *
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.statemanager import *
import ifupdown2.ifupdown.policymanager as policymanager
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
except ImportError:
from ifupdown.graph import *
from ifupdown.ifupdownbase import *
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdown.statemanager import *
import ifupdown.ifupdownflags as ifupdownflags
import ifupdown.policymanager as policymanager
class ifaceSchedulerFlags():
@@ -50,6 +48,11 @@ class ifaceScheduler():
_SCHED_STATUS = True
VRF_MGMT_DEVNAME = policymanager.policymanager_api.get_module_globals(
module_name="vrf",
attr="vrf-mgmt-devname"
)
@classmethod
def reset(cls):
cls._STATE_CHECK = True
@@ -102,8 +105,10 @@ class ifaceScheduler():
ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
except Exception, e:
if not ifupdownobj.ignore_error(str(e)):
err = 1
ifupdownobj.logger.error(str(e))
err = 1
#import traceback
#traceback.print_exc()
ifupdownobj.logger.error(str(e))
# Continue with rest of the modules
pass
finally:
@@ -148,7 +153,6 @@ class ifaceScheduler():
# if interface exists in the system
ifacename = ifaceobjs[0].name
ifupdownobj.logger.info('%s: running ops ...' %ifacename)
if ('down' in ops[0] and
ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
ifaceobjs[0].addr_method != 'ppp' and
@@ -265,6 +269,14 @@ class ifaceScheduler():
# Run lowerifaces or dependents
dlist = ifaceobj.lowerifaces
if dlist:
if ifaceobj.link_kind == ifaceLinkKind.VRF:
# remove non-auto lowerifaces from 'dlist'
for lower_ifname in list(dlist):
for lower_ifaceobj in ifupdownobj.get_ifaceobjs(lower_ifname) or []:
if lower_ifaceobj and not lower_ifaceobj.auto and ifaceobj.name == cls.VRF_MGMT_DEVNAME:
dlist.remove(lower_ifname)
ifupdownobj.logger.debug('%s: found dependents %s'
%(ifacename, str(dlist)))
try:

View File

@@ -63,7 +63,7 @@ class stateManager():
"""
__DEFAULT_STATE_DIR = "/run/network/"
__DEFAULT_STATE_DIR = "/var/tmp/network/"
state_filename = 'ifstatenew'
"""name of the satefile """
@@ -84,6 +84,7 @@ class stateManager():
self.ifaceobjdict = OrderedDict()
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
self.logger.info("stateManager init")
def init(self):
self.state_dir = ifupdownConfig.config.get("state_dir")
@@ -172,6 +173,11 @@ class stateManager():
return
if ifaceobj.status != ifaceStatus.SUCCESS:
return
# ifupdown2 prevents user from bringing the loopback interface
# down - to avoid any issue (like wrong error messages) we
# shouldn't remove lo ifaceobj from the statemanager
if ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
return
# If it matches any of the object, return
oidx = 0
for o in old_ifaceobjs:

View File

@@ -15,6 +15,7 @@ import signal
import logging
import subprocess
from string import maketrans
from functools import partial
from ipaddr import IPNetwork, IPAddress
@@ -103,6 +104,7 @@ class utils():
systemctl_cmd = '/bin/systemctl'
dpkg_cmd = '/usr/bin/dpkg'
logger.info("utils init command paths")
for cmd in ['bridge',
'ip',
'brctl',
@@ -130,6 +132,17 @@ class utils():
else:
logger.debug('warning: path %s not found: %s won\'t be usable' % (path + cmd, cmd))
mac_translate_tab = maketrans(":.-,", " ")
@classmethod
def mac_str_to_int(cls, hw_address):
mac = 0
if hw_address:
for i in hw_address.translate(cls.mac_translate_tab).split():
mac = mac << 8
mac += int(i, 16)
return mac
@staticmethod
def get_onff_from_onezero(value):
if value in utils._onoff_onezero:

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,6 @@
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
import pprint
class MSTPAttrsCache():
bridges = {}
@@ -24,91 +22,3 @@ class MSTPAttrsCache():
@classmethod
def invalidate(cls):
MSTPAttrsCache.bridges = {}
class linkCache():
""" This class contains methods and instance variables to cache
link info """
""" { <ifacename> : { 'ifindex': <index>,
'mtu': <mtu>,
'state' : <state>',
'flags' : <flags>,
'kind' : <kind: bridge, bond, vlan>,
'linkinfo' : {<attr1> : <attrval1>,
<attr2> : <attrval2>,
<ports> : {
} """
links = {}
vrfs = {}
@classmethod
def get_attr(cls, mapList):
return reduce(lambda d, k: d[k], mapList, linkCache.links)
@classmethod
def set_attr(cls, mapList, value):
cls.get_attr(mapList[:-1])[mapList[-1]] = value
@classmethod
def del_attr(cls, mapList):
try:
del cls.get_attr(mapList[:-1])[mapList[-1]]
except:
pass
@classmethod
def update_attrdict(cls, mapList, valuedict):
try:
cls.get_attr(mapList[:-1])[mapList[-1]].update(valuedict)
except:
cls.get_attr(mapList[:-1])[mapList[-1]] = valuedict
pass
@classmethod
def append_to_attrlist(cls, mapList, value):
cls.get_attr(mapList[:-1])[mapList[-1]].append(value)
@classmethod
def remove_from_attrlist(cls, mapList, value):
try:
cls.get_attr(mapList[:-1])[mapList[-1]].remove(value)
except:
pass
@classmethod
def check_attr(cls, attrlist, value=None):
try:
cachedvalue = cls.get_attr(attrlist)
if value:
if cachedvalue == value:
return True
else:
return False
elif cachedvalue:
return True
else:
return False
except:
return False
@classmethod
def invalidate(cls):
cls.links = {}
@classmethod
def reset(cls):
cls.invalidate()
cls.vrfs = {}
@classmethod
def dump(cls):
print 'Dumping link cache'
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(cls.links)
@classmethod
def dump_link(cls, linkname):
print 'Dumping link %s' % linkname
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(cls.links.get(linkname))

View File

@@ -5,6 +5,7 @@
#
import os
import errno
try:
from ifupdown2.ifupdown.utils import utils
@@ -20,12 +21,19 @@ class dhclient(utilsBase):
def _pid_exists(self, pidfilename):
if os.path.exists(pidfilename):
pid = self.read_file_oneline(pidfilename)
if not os.path.exists('/proc/%s' %pid):
try:
return os.readlink(
"/proc/%s/exe" % self.read_file_oneline(pidfilename)
).endswith("dhclient")
except OSError as e:
try:
if e.errno == errno.EACCES:
return os.path.exists("/proc/%s" % self.read_file_oneline(pidfilename))
except:
return False
except:
return False
else:
return False
return True
return False
def is_running(self, ifacename):
return self._pid_exists('/run/dhclient.%s.pid' %ifacename)

View File

@@ -60,6 +60,53 @@ class mstpctlutil(utilsBase):
def __init__(self, *args, **kargs):
utilsBase.__init__(self, *args, **kargs)
self.__batch = []
self.__batch_mode = False
def __add_to_batch(self, cmd):
self.__batch.append(cmd)
def __execute_or_batch(self, cmd):
if self.__batch_mode:
self.__add_to_batch(cmd)
else:
utils.exec_command("%s %s" % (utils.mstpctl_cmd, cmd))
def __execute_or_batch_dry_run(self, cmd):
"""
The batch function has it's own dryrun handler so we only handle
dryrun for non-batch mode. Which will be removed once the "utils"
module has it's own dryrun handlers
"""
if self.__batch_mode:
self.__add_to_batch(cmd)
else:
self.logger.info("DRY-RUN: executing: %s %s" % (utils.mstpctl_cmd, cmd))
def batch_start(self):
if not self.__batch_mode:
self.__batch_mode = True
self.__batch = []
def batch_commit(self):
if not self.__batch_mode or not self.__batch:
return
try:
utils.exec_command(
"%s batch -" % utils.mstpctl_cmd,
stdin="\n".join(self.__batch)
)
except:
raise
finally:
self.__batch_mode = False
del self.__batch
self.__batch = None
###############################################################################
###############################################################################
###############################################################################
@classmethod
def reset(cls):
cls._cache_fill_done = False
@@ -93,6 +140,7 @@ class mstpctlutil(utilsBase):
except Exception as e:
self.logger.info(str(e))
return mstpctl_bridgeport_attrs_dict
portname = bridgename # assigning portname to avoid an exception, in the exception handler
try:
mstpctl_bridge_cache = json.loads(output.strip('\n'))
for portname in mstpctl_bridge_cache.keys():
@@ -104,7 +152,7 @@ class mstpctlutil(utilsBase):
mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal)
MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict)
except Exception as e:
self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % str(e))
self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % (portname, str(e)))
mstpctl_bridge_attrs_dict = {}
try:
@@ -127,7 +175,7 @@ class mstpctlutil(utilsBase):
del mstpctl_bridge_attrs_dict['bridgeId']
MSTPAttrsCache.bridges[bridgename].update(mstpctl_bridge_attrs_dict)
except Exception as e:
self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % str(e))
self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % (bridgename, str(e)))
return MSTPAttrsCache.get(bridgename)
def get_bridge_ports_attrs(self, bridgename):
@@ -161,11 +209,9 @@ class mstpctlutil(utilsBase):
if cache_value and cache_value == value:
return
if attrname == 'treeportcost' or attrname == 'treeportprio':
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
bridgename, portname, '0', value])
self.__execute_or_batch("set%s %s %s 0 %s" % (attrname, bridgename, portname, value))
else:
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
bridgename, portname, value])
self.__execute_or_batch("set%s %s %s %s" % (attrname, bridgename, portname, value))
if json_attr:
self.update_bridge_port_cache(bridgename, portname, json_attr, value)
@@ -195,13 +241,12 @@ class mstpctlutil(utilsBase):
self._bridge_jsonAttr_map[attrname])
if attrvalue_curr and attrvalue_curr == attrvalue:
return
if attrname == 'treeprio':
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
'%s' % bridgename, '0', '%s' % attrvalue], stderr=None)
self.__execute_or_batch("set%s %s 0 %s" % (attrname, bridgename, attrvalue))
self.update_bridge_cache(bridgename, attrname, str(attrvalue))
else:
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
'%s' % bridgename, '%s' % attrvalue], stderr=None)
self.__execute_or_batch("set%s %s %s" % (attrname, bridgename, attrvalue))
self.update_bridge_cache(bridgename,
self._bridge_jsonAttr_map[attrname],
str(attrvalue))
@@ -223,9 +268,8 @@ class mstpctlutil(utilsBase):
attrvalue_curr = self.get_bridge_treeprio(bridgename)
if attrvalue_curr and attrvalue_curr == attrvalue:
return
utils.exec_commandl([utils.mstpctl_cmd,
'settreeprio', bridgename, '0',
str(attrvalue)])
self.__execute_or_batch("settreeprio %s 0 %s" % (bridgename, str(attrvalue)))
self.update_bridge_cache(bridgename, 'treeprio', str(attrvalue))
def showbridge(self, bridgename=None):

View File

54
ifupdown2/lib/addon.py Normal file
View File

@@ -0,0 +1,54 @@
# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# addon -- Addon base class
#
import logging
try:
from ifupdown2.lib.io import IO
from ifupdown2.lib.sysfs import Sysfs
from ifupdown2.lib.iproute2 import IPRoute2
from ifupdown2.lib.base_objects import Netlink, Cache, Requirements
except ImportError:
from lib.io import IO
from lib.sysfs import Sysfs
from lib.iproute2 import IPRoute2
from lib.base_objects import Netlink, Cache, Requirements
class Addon(Netlink, Cache):
"""
Base class for ifupdown2 addon modules
Provides common infrastructure methods for all addon modules
"""
def __init__(self):
Netlink.__init__(self)
Cache.__init__(self)
self.logger = logging.getLogger("ifupdown2.addons.%s" % self.__class__.__name__)
self.io = IO()
self.sysfs = Sysfs
self.iproute2 = IPRoute2()
self.requirements = Requirements()

View File

@@ -0,0 +1,69 @@
# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# base_objects -- Base classes used by higher level classes
#
import os
import logging
try:
from ifupdown2.lib.dry_run import DryRun
from ifupdown2.ifupdown.utils import utils
except ImportError:
from lib.dry_run import DryRun
from ifupdown.utils import utils
class BaseObject(DryRun):
"""
BaseObject should be the parent of any ifupdown2 object that wishes to
implement any "dry run" specific code and have a default logger.
More classes can inherit BaseObject and add features like Addon, FileIO or Sysfs...
"""
def __init__(self):
DryRun.__init__(self)
self.logger = logging.getLogger("ifupdown2.%s" % self.__class__.__name__)
def _import_NetlinkListenerWithCache():
try:
from ifupdown2.lib.nlcache import NetlinkListenerWithCache
except ImportError:
from lib.nlcache import NetlinkListenerWithCache
return NetlinkListenerWithCache
class Cache(BaseObject):
def __init__(self):
BaseObject.__init__(self)
self.cache = _import_NetlinkListenerWithCache().get_instance().cache
class Netlink(BaseObject):
def __init__(self):
BaseObject.__init__(self)
self.netlink = _import_NetlinkListenerWithCache().get_instance()
class Requirements(BaseObject):
bridge_utils_is_installed = os.path.exists(utils.brctl_cmd)

250
ifupdown2/lib/dry_run.py Normal file
View File

@@ -0,0 +1,250 @@
#!/usr/bin/python
# Copyright (C) 2019 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# ifupdown2 -- dry_run module
#
#
# __WeakMethodBound and __WeakMethodFree classes as well as the WeakMethod
# function were inspired by the implementation of ActiveState recipes 81253
# weakmethod.
# This code solves an important issue here. You can't have weakrefs on bound
# methods. Here is quote from the recipie:
#
# "Normal weakref.refs to bound methods don't quite work the way one expects,
# because bound methods are first-class objects; weakrefs to bound methods are
# dead-on-arrival unless some other strong reference to the same bound method
# exists."
#
import logging
import inspect
import weakref
class __WeakMethodBound:
""" ActiveState recipes 81253-weakmethod """
def __init__(self, f):
self.f = f.im_func
self.c = weakref.ref(f.im_self)
def __call__(self, *arg, **kwargs):
if not self.c():
raise TypeError("Method called on dead object")
apply(self.f, (self.c(),) + arg, kwargs)
class __WeakMethodFree:
""" ActiveState recipes 81253-weakmethod """
def __init__(self, f):
self.f = weakref.ref(f)
def __call__(self, *arg, **kwargs):
if not self.f():
raise TypeError("Function no longer exist")
apply(self.f(), arg, kwargs)
def WeakMethod(f):
""" ActiveState recipes 81253-weakmethod """
try:
f.im_func
except AttributeError:
return __WeakMethodFree(f)
return __WeakMethodBound(f)
def _weakref_call_back_delete(reference):
try:
DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(reference)
except:
pass
class DryRun(object):
"""
Detect dry_run functions and save the associated handler
"""
__DRY_RUN_PREFIX = "DRY-RUN"
def __init__(self):
self.logger = logging.getLogger("ifupdown2.%s" % self.__class__.__name__)
for attr_name in dir(self):
try:
# We need to iterate through the object attribute
# to find dryrun methods
if attr_name.lower().endswith("_dry_run"):
attr_value = getattr(self, attr_name)
# When we find a dryrun attribute we need to make sure
# it is a callable function or method.
if not self.__is_method_or_function(attr_value):
continue
base_attr_name = attr_name[:-8]
base_attr_value = getattr(self, base_attr_name)
# We try infere the base method/function name
# then make sure its a function or method
if not self.__is_method_or_function(base_attr_value):
continue
# now we are pretty sure we have want we want:
# - the base function
# - the associated dry_run code
# we will now register this couple in the DryRunManager
DryRunManager.get_instance().register_dry_run_handler(
weakref.ref(self, _weakref_call_back_delete),
handler_name=base_attr_name,
handler_code_weakref=WeakMethod(base_attr_value),
dry_run_code_weakref=WeakMethod(attr_value)
)
except:
pass
def log_info_ifname_dry_run(self, ifname, string):
self.logger.info("DRY-RUN: %s: %s" % (ifname, string))
def log_info_dry_run(self, string):
self.logger.info("DRY-RUN: %s" % string)
@staticmethod
def __is_method_or_function(obj):
return callable(obj) and (inspect.ismethod(obj) or inspect.isfunction(obj))
class _DryRunEntry(object):
def __init__(self, target_module_weakref, handler_name, handler_code_weakref, dry_run_code_weakref):
self.target_module_weakref = target_module_weakref
self.handler_name = handler_name
self.handler_code_weakref = handler_code_weakref
self.dry_run_code_weakref = dry_run_code_weakref
self.__status = False
def set(self):
target_module_ref = self.target_module_weakref()
if target_module_ref:
if self.dry_run_code_weakref:
target_module_ref.__dict__[self.handler_name] = self.dry_run_code_weakref
self.__status = True
else:
# if the reference is dead we need to unregister it
DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(self.target_module_weakref)
def unset(self):
target_module_ref = self.target_module_weakref()
if target_module_ref:
if self.handler_code_weakref:
target_module_ref.__dict__[self.handler_name] = self.handler_code_weakref
self.__status = False
else:
# if the reference is dead we need to unregister it
DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(self.target_module_weakref)
def get_status(self):
return self.__status
class DryRunManager(object):
__instance = None
@staticmethod
def get_instance():
if not DryRunManager.__instance:
DryRunManager.__instance = DryRunManager()
return DryRunManager.__instance
def __init__(self):
if DryRunManager.__instance:
raise RuntimeError("DryRunManager: invalid access. Please use DryRunManager.getInstance()")
else:
DryRunManager.__instance = self
self.__entries = dict()
self.__is_on = False
def register_dry_run_handler(self, module_weakref, handler_name, handler_code_weakref, dry_run_code_weakref):
"""
Register the dry run handler only using weakrefs - we don't want to mess up with garbage collection
:param module_weakref:
:param handler_name:
:param handler_code_weakref:
:param dry_run_code_weakref:
:return:
"""
dry_run_entry = _DryRunEntry(
target_module_weakref=module_weakref,
handler_name=handler_name,
handler_code_weakref=handler_code_weakref,
dry_run_code_weakref=dry_run_code_weakref
)
if self.__is_on:
dry_run_entry.set()
if module_weakref in self.__entries:
self.__entries[module_weakref].append(dry_run_entry)
else:
self.__entries[module_weakref] = [dry_run_entry]
def unregister_dry_run_handler_weakref_callback(self, reference):
"""
If we detect a dead reference, we should remove this reference from our
internal data structure
"""
try:
del self.__entries[reference]
except:
pass
def dry_run_mode_on(self):
"""
Enable the dry run mode
WARNING: not thread-safe
"""
for entries in self.__entries.itervalues():
for entry in entries:
entry.set()
self.__is_on = True
def dry_run_mode_off(self):
"""
Disable the dry run mode
WARNING: not thread-safe
"""
for entries in self.__entries.itervalues():
for entry in entries:
entry.unset()
self.__is_on = False
def dump_entries_stdout(self):
print "== DryRunManager dump =="
print " MODULE: HANDLER STATUS"
for entries in self.__entries.itervalues():
for entry in entries:
print " %s: %s() %s" % (repr(entry.target_module_weakref), entry.handler_name, "ON" if entry.get_status() else "OFF")
print "========================"
def is_dry_mode_on(self):
return self.__is_on

View File

@@ -0,0 +1,43 @@
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# ifupdown2 custom exceptions
#
class Ifupdown2Exception(Exception):
pass
class ExitWithStatus(Ifupdown2Exception):
def __init__(self, status):
Ifupdown2Exception.__init__(self)
self.status = status
def get_status(self):
return self.status
class ExitWithStatusAndError(ExitWithStatus):
def __init__(self, status, message):
ExitWithStatus.__init__(self, status)
self.message = message

130
ifupdown2/lib/io.py Normal file
View File

@@ -0,0 +1,130 @@
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# io -- all io (file) handlers
#
import json
import struct
import socket
import select
try:
from ifupdown2.lib.base_objects import BaseObject
except ImportError:
from lib.base_objects import BaseObject
class IO(BaseObject):
def __init__(self):
BaseObject.__init__(self)
def write_to_file(self, path, string):
try:
self.logger.info("writing \"%s\" to file %s" % (string, path))
with open(path, "w") as f:
f.write(string)
return True
except IOError, e:
self.logger.warn("error while writing to file %s: %s" % (path, str(e)))
return False
def write_to_file_dry_run(self, path, string):
self.log_info_dry_run("writing \"%s\" to file %s" % (string, path))
return True
def read_file_oneline(self, path):
try:
self.logger.info("reading '%s'" % path)
with open(path, "r") as f:
return f.readline().strip("\n")
except:
return None
def read_file_oneline_dry_run(self, path):
self.log_info_dry_run("reading \"%s\"" % path)
return None
def read_file(self, path):
""" read file and return lines from the file """
try:
self.logger.info("reading '%s'" % path)
with open(path, "r") as f:
return f.readlines()
except:
return None
class SocketIO(object):
"""
Helper class to provide common TX/RX methods for socket
communication to both client and daemon.
"""
@staticmethod
def tx_data(_socket, data):
"""
We don't send raw data over the socket, we pack it with the length
(first 4 bytes) then with the data. That way the the transfer is more
reliable
"""
ready = select.select([], [_socket], [])
if ready and ready[1] and ready[1][0] == _socket:
frmt = "=%ds" % len(data)
packed_msg = struct.pack(frmt, data)
packed_hdr = struct.pack("=I", len(packed_msg))
_socket.sendall(packed_hdr + packed_msg)
@staticmethod
def rx_json_packet(_socket):
"""
Reading data from socket. Unpacking the packets sent by "tx_data"
first 4 bytes are the length of the following data. The data should
be in json format
"""
ready = select.select([_socket], [], [])
if ready and ready[0] and ready[0][0] == _socket:
header_data = _socket.recv(4)
if not header_data:
raise Exception("rx_json_packet: socket closed")
if len(header_data) < 4:
raise Exception("rx_json_packet: invalid data received")
data_len = struct.unpack("=I", header_data)[0]
data = _socket.recv(data_len)
while len(data) < data_len:
data = data + _socket.recv(data_len - len(data))
return json.loads(data)
return None
def get_socket_peer_cred(self, _socket):
"""
Returns tuple of (pid, uid, gid) of connected AF_UNIX stream socket
:param _socket:
:return:
"""
return struct.unpack("3i", _socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize("3i")))

743
ifupdown2/lib/iproute2.py Normal file
View File

@@ -0,0 +1,743 @@
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# iproute2 -- contains all iproute2 related operation
#
import re
import shlex
import signal
import subprocess
from ipaddr import IPNetwork
try:
from ifupdown2.lib.sysfs import Sysfs
from ifupdown2.lib.base_objects import Cache, Requirements
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.iface import ifaceLinkPrivFlags
from ifupdown2.nlmanager.nlpacket import Link
except ImportError:
from lib.sysfs import Sysfs
from lib.base_objects import Cache, Requirements
from ifupdown.utils import utils
from ifupdown.iface import ifaceLinkPrivFlags
from nlmanager.nlpacket import Link
# WORK AROUND - Tunnel creation should be done via netlink and not iproute2 ####
import struct #
import socket #
#
try: #
import ifupdown2.nlmanager.nlpacket as nlpacket #
except: #
import nlmanager.nlpacket as nlpacket #
################################################################################
class IPRoute2(Cache, Requirements):
VXLAN_UDP_PORT = 4789
VXLAN_PEER_REGEX_PATTERN = re.compile("\s+dst\s+(\d+.\d+.\d+.\d+)\s+")
def __init__(self):
Cache.__init__(self)
Requirements.__init__(self)
self.sysfs = Sysfs
self.__batch = {}
self.__batch_mode = False
# if bridge utils is not installed overrrides specific functions to
# avoid constantly checking bridge_utils_is_installed
if not Requirements.bridge_utils_is_installed:
self.bridge_set_stp = lambda _, __: None
self.bridge_del_mcqv4src = lambda _, __: None
self.bridge_set_mcqv4src = lambda _, __, ___: None
############################################################################
# WORK-AROUND
############################################################################
def __update_cache_after_link_creation(self, ifname, kind):
"""
WORK AROUND - when creating tunnel via iproute2 we still need to fill
our internal cache to keep track of this interface until we receive the
NEWLINK notification. This code is a copy-paste from:
nlcache.tx_nlpacket_get_response_with_error_and_cache_on_ack
:param ifname:
:param kind:
:return:
"""
packet = nlpacket.Link(nlpacket.RTM_NEWLINK, False, use_color=False)
packet.flags = nlpacket.NLM_F_CREATE | nlpacket.NLM_F_REQUEST | nlpacket.NLM_F_ACK
packet.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
packet.add_attribute(nlpacket.Link.IFLA_IFNAME, ifname)
packet.add_attribute(nlpacket.Link.IFLA_LINKINFO, {
nlpacket.Link.IFLA_INFO_KIND: kind,
nlpacket.Link.IFLA_INFO_DATA: {}
})
packet.build_message(0, 0)
# When creating a new link via netlink, we don't always wait for the kernel
# NEWLINK notification to be cached to continue. If our request is ACKed by
# the OS we assume that the link was successfully created. Since we aren't
# waiting for the kernel notification to continue we need to manually fill
# our cache with the packet we just TX'ed. Once the NEWLINK notification
# is received it will simply override the previous entry.
# We need to keep track of those manually cached packets. We set a private
# flag on the objects via the attribute priv_flags
packet.priv_flags |= nlpacket.NLM_F_REQUEST
try:
# we need to decode the service header so all the attribute are properly
# filled in the packet object that we are about to store in cache.
# i.e.: packet.flags shouldn't contain NLM_F_* values but IFF_* (in case of Link object)
# otherwise call to cache.link_is_up() will probably return True
packet.decode_service_header()
except:
# we can ignore all errors
pass
# Then we can use our normal "add_link" API call to cache the packet
# and fill up our additional internal data structures.
self.cache.add_link(packet)
############################################################################
# BATCH
############################################################################
def __add_to_batch(self, prefix, cmd):
if prefix in self.__batch:
self.__batch[prefix].append(cmd)
else:
self.__batch[prefix] = [cmd]
def __execute_or_batch(self, prefix, cmd):
if self.__batch_mode:
self.__add_to_batch(prefix, cmd)
else:
utils.exec_command("%s %s" % (prefix, cmd))
def __execute_or_batch_dry_run(self, prefix, cmd):
"""
The batch function has it's own dryrun handler so we only handle
dryrun for non-batch mode. Which will be removed once the "utils"
module has it's own dryrun handlers
"""
if self.__batch_mode:
self.__add_to_batch(prefix, cmd)
else:
self.log_info_dry_run("executing: %s %s" % (prefix, cmd))
def batch_start(self):
if not self.__batch_mode:
self.__batch_mode = True
self.__batch = {}
def batch_commit(self):
try:
if not self.__batch_mode or not self.__batch:
return
for prefix, commands in self.__batch.iteritems():
utils.exec_command(
"%s -force -batch -" % prefix,
stdin="\n".join(commands)
)
except:
raise
finally:
self.__batch_mode = False
del self.__batch
self.__batch = None
############################################################################
# LINK
############################################################################
def link_up(self, ifname):
if not self.cache.link_is_up(ifname):
self.link_up_force(ifname)
def link_down(self, ifname):
if self.cache.link_is_up(ifname):
self.link_down_force(ifname)
def link_up_dry_run(self, ifname):
self.link_up_force(ifname)
def link_down_dry_run(self, ifname):
self.link_down_force(ifname)
def link_up_force(self, ifname):
self.__execute_or_batch(utils.ip_cmd, "link set dev %s up" % ifname)
def link_down_force(self, ifname):
self.__execute_or_batch(utils.ip_cmd, "link set dev %s down" % ifname)
###
def link_set_master(self, ifname, master):
if master != self.cache.get_master(ifname):
self.__execute_or_batch(
utils.ip_cmd,
"link set dev %s master %s" % (ifname, master)
)
def link_set_master_dry_run(self, ifname, master):
self.__execute_or_batch(
utils.ip_cmd,
"link set dev %s master %s" % (ifname, master)
)
###
def link_set_address(self, ifname, address):
if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
self.link_down(ifname)
self.__execute_or_batch(
utils.ip_cmd,
"link set dev %s address %s" % (ifname, address)
)
self.link_up(ifname)
def link_set_address_dry_run(self, ifname, address):
self.link_down(ifname)
self.__execute_or_batch(
utils.ip_cmd,
"link set dev %s address %s" % (ifname, address)
)
self.link_up(ifname)
def link_set_address_and_keep_down(self, ifname, address, keep_down=False):
if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
self.link_down(ifname)
self.__execute_or_batch(
utils.ip_cmd,
"link set dev %s address %s" % (ifname, address)
)
if not keep_down:
self.link_up(ifname)
def link_set_address_and_keep_down_dry_run(self, ifname, address, keep_down=False):
self.link_down(ifname)
self.__execute_or_batch(
utils.ip_cmd,
"link set dev %s address %s" % (ifname, address)
)
if not keep_down:
self.link_up(ifname)
###
def link_add_macvlan(self, ifname, macvlan_ifname, macvlan_mode):
utils.exec_command(
"%s link add link %s name %s type macvlan mode %s"
% (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
)
def link_add_macvlan_dry_run(self, ifname, macvlan_ifname, macvlan_mode):
# this dryrun method can be removed once dryrun handlers
# are added to the utils module
self.log_info_ifname_dry_run(ifname, "executing %s link add link %s name %s type macvlan mode %s"
% (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
)
###
def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None,
remoteips=None, learning='on', ageing=None, ttl=None, physdev=None):
if svcnodeip and remoteips:
raise Exception("svcnodeip and remoteip are mutually exclusive")
if self.cache.link_exists(name):
cmd = [
"link set dev %s type vxlan dstport %d"
% (name, self.VXLAN_UDP_PORT)
]
else:
cmd = [
"link add dev %s type vxlan id %s dstport %d"
% (name, vxlanid, self.VXLAN_UDP_PORT)
]
if svcnodeip:
if svcnodeip.is_multicast:
cmd.append("group %s" % svcnodeip)
else:
cmd.append("remote %s" % svcnodeip)
if ageing:
cmd.append("ageing %s" % ageing)
if learning == 'off':
cmd.append("nolearning")
if ttl is not None:
cmd.append("ttl %s" % ttl)
if physdev:
cmd.append("dev %s" % physdev)
if localtunnelip:
cmd.append("local %s" % localtunnelip)
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
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:
for l in output.split('\n'):
m = self.VXLAN_PEER_REGEX_PATTERN.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
###
@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])
############################################################################
# TUNNEL
############################################################################
def tunnel_create(self, tunnelname, mode, attrs=None):
if self.cache.link_exists(tunnelname):
return
cmd = []
if "6" in mode:
cmd.append("-6")
if mode in ["gretap"]:
cmd.append("link add %s type %s" % (tunnelname, mode))
else:
cmd.append("tunnel add %s mode %s" % (tunnelname, mode))
if attrs:
for k, v in attrs.iteritems():
cmd.append(k)
if v:
cmd.append(v)
utils.exec_command("%s %s" % (utils.ip_cmd, " ".join(cmd)))
self.__update_cache_after_link_creation(tunnelname, mode)
def tunnel_change(self, tunnelname, attrs=None):
""" tunnel change function """
if not self.cache.link_exists(tunnelname):
return
cmd = ["tunnel change %s" % tunnelname]
if attrs:
for k, v in attrs.iteritems():
cmd.append(k)
if v:
cmd.append(v)
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
############################################################################
# ADDRESS
############################################################################
def addr_flush(self, ifname):
if self.cache.link_has_ip(ifname):
self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
def link_set_ipv6_addrgen_dry_run(self, ifname, addrgen, link_created):
addrgen_str = "none" if addrgen else "eui64"
self.link_down(ifname)
self.__execute_or_batch(utils.ip_cmd, "link set dev %s addrgenmode %s" % (ifname, addrgen_str))
self.link_up(ifname)
def link_set_ipv6_addrgen(self, ifname, addrgen, link_created):
"""
IFLA_INET6_ADDR_GEN_MODE values:
0 = eui64
1 = none
:param ifname:
:param addrgen:
:param link_created:
:return:
"""
cached_ipv6_addr_gen_mode = self.cache.get_link_ipv6_addrgen_mode(ifname)
if cached_ipv6_addr_gen_mode == addrgen:
return True
disabled_ipv6 = self.sysfs.get_ipv6_conf_disable_ipv6(ifname)
if disabled_ipv6:
self.logger.info("%s: cannot set addrgen: ipv6 is disabled on this device" % ifname)
return False
if link_created:
link_mtu = self.sysfs.link_get_mtu(ifname)
else:
link_mtu = self.cache.get_link_mtu(ifname)
if link_mtu < 1280:
self.logger.info("%s: ipv6 addrgen is disabled on device with MTU "
"lower than 1280 (current mtu %s): cannot set addrgen %s"
% (ifname, link_mtu, "off" if addrgen else "on"))
return False
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.cache.address_flush_link(ifname)
is_link_up = self.cache.link_is_up(ifname)
if is_link_up:
self.link_down_force(ifname)
self.__execute_or_batch(
utils.ip_cmd,
"link set dev %s addrgenmode %s" % (ifname, Link.ifla_inet6_addr_gen_mode_dict.get(addrgen))
)
if is_link_up:
self.link_up_force(ifname)
return True
@staticmethod
def __compare_user_config_vs_running_state(running_addrs, user_addrs):
ip4 = []
ip6 = []
for ip in user_addrs or []:
obj = IPNetwork(ip)
if obj.version == 6:
ip6.append(str(obj))
else:
ip4.append(str(obj))
running_ipobj = []
for ip in running_addrs or []:
running_ipobj.append(str(ip))
return running_ipobj == (ip4 + ip6)
def add_addresses(self, ifacobj, ifname, address_list, purge_existing=False, metric=None, with_address_virtual=False):
if purge_existing:
running_address_list = self.cache.get_ifupdown2_addresses_list(
[ifacobj],
ifname,
with_address_virtual=with_address_virtual
)
address_list = utils.get_normalized_ip_addr(ifname, address_list)
if self.__compare_user_config_vs_running_state(running_address_list, address_list):
return
try:
self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
except Exception, e:
self.logger.warning("%s: flushing all ip address failed: %s" % (ifname, str(e)))
for addr in address_list:
try:
if metric:
self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s metric %s" % (addr, ifname, metric))
else:
self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s" % (addr, ifname))
except Exception as e:
self.logger.error("%s: add_address: %s" % (ifname, str(e)))
############################################################################
# BRIDGE
############################################################################
@staticmethod
def bridge_set_stp(bridge, stp_state):
utils.exec_command("%s stp %s %s" % (utils.brctl_cmd, bridge, stp_state))
@staticmethod
def bridge_fdb_show_dev(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:
pass
return fdbs
except Exception:
return None
@staticmethod
def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
target = "self" if bridge else ""
vlan_str = "vlan %s " % vlan if vlan else ""
dst_str = "dst %s " % remote if remote else ""
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 = "vlan %s " % vlan if vlan else ""
dst_str = "dst %s " % remote if remote else ""
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 = "vlan %s " % vlan if vlan else ""
dst_str = "dst %s " % remote if remote else ""
utils.exec_command(
"%s fdb del %s dev %s %s %s %s"
% (
utils.bridge_cmd,
address,
dev,
vlan_str,
target,
dst_str
)
)
@staticmethod
def bridge_vlan_del_vid_list(ifname, vids):
if not vids:
return
for v in vids:
utils.exec_command(
"%s vlan del vid %s dev %s" % (utils.bridge_cmd, v, ifname)
)
def bridge_vlan_del_vid_list_self(self, ifname, vids, is_bridge=True):
target = "self" if is_bridge else ""
for v in vids:
self.__execute_or_batch(
utils.bridge_cmd,
"vlan del vid %s dev %s %s" % (v, ifname, target)
)
@staticmethod
def bridge_vlan_add_vid_list(ifname, vids):
for v in vids:
utils.exec_command(
"%s vlan add vid %s dev %s" % (utils.bridge_cmd, v, ifname)
)
def bridge_vlan_add_vid_list_self(self, ifname, vids, is_bridge=True):
target = "self" if is_bridge else ""
for v in vids:
self.__execute_or_batch(
utils.bridge_cmd,
"vlan add vid %s dev %s %s" % (v, ifname, target)
)
def bridge_vlan_del_pvid(self, ifname, pvid):
self.__execute_or_batch(
utils.bridge_cmd,
"vlan del vid %s untagged pvid dev %s" % (pvid, ifname)
)
def bridge_vlan_add_pvid(self, ifname, pvid):
self.__execute_or_batch(
utils.bridge_cmd,
"vlan add vid %s untagged pvid dev %s" % (pvid, ifname)
)
def bridge_del_mcqv4src(self, bridge, vlan):
try:
vlan = int(vlan)
except Exception as e:
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_set_mcqv4src(self, bridge, vlan, mcquerier):
try:
vlan = int(vlan)
except Exception as e:
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
utils.exec_command("%s setmcqv4src %s %d %s" % (utils.brctl_cmd, bridge, vlan, mcquerier))
############################################################################
# ROUTE
############################################################################
@staticmethod
def route_add_gateway(ifname, 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)
if metric:
cmd += " metric %s" % metric
cmd += " dev %s" % ifname
if onlink:
cmd += " onlink"
utils.exec_command(cmd)
@staticmethod
def route_del_gateway(ifname, gateway, vrf=None, metric=None):
"""
delete default gw
we don't need a DRYRUN handler here as utils.exec_command should have one
"""
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" % ifname
utils.exec_command(cmd)
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.cache.get_vrf_table(upper_iface)
if vrf_table:
break
except:
pass
ip_route_del = []
for ip in ips:
ip_network_obj = IPNetwork(ip)
if ip_network_obj.version == 6:
route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
if vrf_table:
self.__execute_or_batch(
utils.ip_cmd,
"route del %s table %s dev %s" % (route_prefix, vrf_table, macvlan_ifacename)
)
else:
self.__execute_or_batch(
utils.ip_cmd,
"route del %s dev %s" % (route_prefix, macvlan_ifacename)
)
ip_route_del.append((route_prefix, vrf_table))
for ip, vrf_table in ip_route_del:
if vrf_table:
self.__execute_or_batch(
utils.ip_cmd,
"route add %s table %s dev %s proto kernel metric 9999" % (ip, vrf_table, macvlan_ifacename)
)
else:
self.__execute_or_batch(
utils.ip_cmd,
"route add %s dev %s proto kernel metric 9999" % (ip, macvlan_ifacename)
)
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

224
ifupdown2/lib/log.py Normal file
View File

@@ -0,0 +1,224 @@
# Copyright (C) 2016, 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
import os
import sys
import traceback
import logging
import logging.handlers
root_logger = logging.getLogger()
class LogManager:
LOGGER_NAME = "ifupdown2"
LOGGER_NAME_DAEMON = "ifupdown2d"
DEFAULT_TCP_LOGGING_PORT = 42422
DEFAULT_LOGGING_LEVEL_DAEMON = logging.INFO
DEFAULT_LOGGING_LEVEL_NORMAL = logging.WARNING
__instance = None
@staticmethod
def get_instance():
if not LogManager.__instance:
try:
LogManager.__instance = LogManager()
except Exception as e:
sys.stderr.write("warning: ifupdown2.Log: %s\n" % str(e))
traceback.print_exc()
return LogManager.__instance
def __init__(self):
"""
Setup root logger and console handler (stderr). To enable daemon, client
or standalone logging please call the proper function, see:
"start_(daemon|client|standlone)_logging"
"""
if LogManager.__instance:
raise RuntimeError("Log: invalid access. Please use Log.getInstance()")
else:
LogManager.__instance = self
self.__fmt = "%(levelname)s: %(message)s"
self.__debug_fmt = "%(asctime)s: %(threadName)s: %(name)s: " \
"%(filename)s:%(lineno)d:%(funcName)s(): " \
"%(levelname)s: %(message)s"
self.__root_logger = logging.getLogger()
self.__root_logger.name = self.LOGGER_NAME
self.__socket_handler = None
self.__syslog_handler = None
self.__console_handler = None
self.daemon = None
# by default we attach a console handler that logs on stderr
# the daemon can manually remove this handler on startup
self.__console_handler = logging.StreamHandler(sys.stderr)
self.__console_handler.setFormatter(logging.Formatter(self.__fmt))
self.__root_logger.addHandler(self.__console_handler)
if os.path.exists("/dev/log"):
try:
self.__syslog_handler = logging.handlers.SysLogHandler(
address="/dev/log",
facility=logging.handlers.SysLogHandler.LOG_DAEMON
)
self.__syslog_handler.setFormatter(logging.Formatter(self.__fmt))
except Exception as e:
sys.stderr.write("warning: syslog: %s\n" % str(e))
self.__syslog_handler = None
logging.addLevelName(logging.CRITICAL, "critical")
logging.addLevelName(logging.WARNING, "warning")
logging.addLevelName(logging.ERROR, "error")
logging.addLevelName(logging.DEBUG, "debug")
logging.addLevelName(logging.INFO, "info")
def set_level(self, default, error=False, warning=False, info=False, debug=False):
"""
Set root handler logging level
:param default:
:param error:
:param warning:
:param info:
:param debug:
"""
if debug:
log_level = logging.DEBUG
elif info:
log_level = logging.INFO
elif warning:
log_level = logging.WARNING
elif error:
log_level = logging.ERROR
else:
log_level = default
for handler in self.__root_logger.handlers:
handler.setLevel(log_level)
self.__root_logger.setLevel(log_level)
def enable_console(self):
""" Add console handler to root logger """
self.__root_logger.addHandler(self.__console_handler)
def disable_console(self):
""" Remove console handler from root logger """
self.__root_logger.removeHandler(self.__console_handler)
def enable_syslog(self):
""" Add syslog handler to root logger """
if self.__syslog_handler:
self.__root_logger.addHandler(self.__syslog_handler)
def disable_syslog(self):
""" Remove syslog handler from root logger """
if self.__syslog_handler:
self.__root_logger.removeHandler(self.__syslog_handler)
def close_log_stream(self):
""" Close socket to disconnect client.
We first have to perform this little hack: it seems like the socket is
not opened until data (LogRecord) are transmitted. In our most basic use
case (client sends "ifup -a") the daemon doesn't send back any LogRecord
but we can't predict that in the client. The client is already in a
blocking-select waiting for data on it's socket handler
(StreamRequestHandler). For this special case we need to manually call
"createSocket" to open the channel to the client so that we can properly
close it. That way the client can exit cleanly.
"""
self.__root_logger.removeHandler(self.__socket_handler)
self.__socket_handler.acquire()
self.__socket_handler.retryTime = None
try:
if not self.__socket_handler.sock:
self.__socket_handler.createSocket()
finally:
self.__socket_handler.close()
self.__socket_handler.release()
def start_stream(self):
self.__root_logger.addHandler(self.__socket_handler)
def set_daemon_logging_level(self, args):
self.set_level(self.DEFAULT_LOGGING_LEVEL_DAEMON, info=args.verbose, debug=args.debug)
def set_request_logging_level(self, args):
if not hasattr(args, "syslog") or not args.syslog:
self.disable_syslog()
else:
self.__root_logger.removeHandler(self.__socket_handler)
self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
def start_client_logging(self, args):
""" Setup root logger name and client log level
syslog is handled by the daemon directly
"""
self.__root_logger.name = self.LOGGER_NAME
if hasattr(args, "syslog") and args.syslog:
self.enable_syslog()
self.disable_console()
self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
def start_standalone_logging(self, args):
self.__root_logger.name = self.LOGGER_NAME
if hasattr(args, "syslog") and args.syslog:
self.enable_syslog()
self.disable_console()
self.__root_logger.removeHandler(self.__console_handler)
self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
def start_daemon_logging(self, args):
"""
Daemon mode initialize a socket handler to transmit logging to the
client, we can also do syslog logging and/or console logging (probably
just for debugging purpose)
:param args:
:return:
"""
self.__root_logger.name = self.LOGGER_NAME_DAEMON
self.daemon = True
self.enable_syslog()
# Create SocketHandler for daemon-client communication
self.__socket_handler = logging.handlers.SocketHandler(
"localhost",
port=self.DEFAULT_TCP_LOGGING_PORT
)
self.__root_logger.addHandler(self.__socket_handler)
if not args.console:
self.disable_console()
self.set_daemon_logging_level(args)

3216
ifupdown2/lib/nlcache.py Normal file

File diff suppressed because it is too large Load Diff

52
ifupdown2/lib/status.py Normal file
View File

@@ -0,0 +1,52 @@
# Copyright (C) 2019 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
class Status(object):
"""
Defining client and daemon exit status to better identify
client and daemon issue and exceptions.
80 > unknown > 90 > client status > 100 > daemon status
"""
class Client(object):
STATUS_SUCCESS = 0
STATUS_INIT = 91
STATUS_COULD_NOT_CONNECT = 92
STATUS_NO_PID = 93
STATUS_EMPTY = 94
STATUS_KEYBOARD_INTERRUPT = 95
STATUS_NLERROR = 96
STATUS_EXCEPTION_MAIN = 99
class Daemon(object):
STATUS_SUCCESS = 0
STATUS_INIT = 101
STATUS_UNKNOWN = 102
STATUS_SOCKET_ERROR = 103
STATUS_PROCESS_REQUEST = 104
STATUS_KEYBOARD_INTERRUPT = 105
STATUS_NLERROR = 106
STATUS_REQUEST_PARSE_ERROR = 106
STATUS_REQUEST_EXCEPTION = 107
STATUS_REQUEST_BASE_EXCEPTION = 108

248
ifupdown2/lib/sysfs.py Normal file
View File

@@ -0,0 +1,248 @@
# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# sysfs -- contains all sysfs related operation
#
import os
import glob
try:
from ifupdown2.lib.io import IO
from ifupdown2.lib.base_objects import Requirements
from ifupdown2.ifupdown.utils import utils
from ifupdown2.nlmanager.nlpacket import Link
except ImportError:
from lib.io import IO
from lib.base_objects import Requirements
from ifupdown.utils import utils
from nlmanager.nlpacket import Link
class __Sysfs(IO, Requirements):
__bond_netlink_to_sysfs_attr_map = {
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",
Link.IFLA_BOND_PRIMARY: "primary",
}
def __init__(self):
IO.__init__(self)
Requirements.__init__(self)
# Temporary work around to solve the circular dependency with nlcache.
# Once nlcache is created it will populate sysfs.cache
self.cache = None
# if bridge utils is not installed overrrides specific functions to
# avoid constantly checking bridge_utils_is_installed
if not Requirements.bridge_utils_is_installed:
self.bridge_get_mcqv4src = self.bridge_get_mcqv4src_dry_run
@staticmethod
def link_get_uppers(ifname):
try:
uppers = glob.glob("/sys/class/net/%s/upper_*" % ifname)
if not uppers:
return []
return [os.path.basename(u)[6:] for u in uppers]
except:
return []
@staticmethod
def link_get_lowers(ifname):
try:
lowers = glob.glob("/sys/class/net/%s/lower_*" % ifname)
if not lowers:
return []
return [os.path.basename(l)[6:] for l in lowers]
except:
return []
def link_is_up(self, ifname):
"""
Read sysfs operstate file
"""
return "up" == self.read_file_oneline("/sys/class/net/%s/operstate" % ifname)
def get_link_address(self, ifname):
"""
Read MAC hardware address from sysfs
"""
return self.read_file_oneline("/sys/class/net/%s/address" % ifname)
#
# MTU
#
def link_get_mtu(self, ifname):
return int(self.read_file_oneline("/sys/class/net/%s/mtu" % ifname) or 0)
def link_set_mtu(self, ifname, mtu_str, mtu_int):
if self.cache.get_link_mtu(ifname) != mtu_int:
if self.write_to_file('/sys/class/net/%s/mtu' % ifname, mtu_str):
self.cache.override_link_mtu(ifname, mtu_int)
def link_set_mtu_dry_run(self, ifname, mtu_str, mtu_int):
# we can remove the cache check in DRYRUN mode
self.write_to_file('/sys/class/net/%s/mtu' % ifname, mtu_str)
#
# ALIAS
#
def link_set_alias(self, ifname, alias):
cached_alias = self.cache.get_link_alias(ifname)
if cached_alias == alias:
return
if not alias:
alias = "\n"
if self.write_to_file("/sys/class/net/%s/ifalias" % ifname, alias):
pass # self.cache.override_link_mtu(ifname, mtu_int)
def link_set_alias_dry_run(self, ifname, alias):
# we can remove the cache check in DRYRUN mode
if not alias:
alias = ""
self.write_to_file("/sys/class/net/%s/ifalias" % ifname, alias)
############################################################################
# BRIDGE
############################################################################
def bridge_port_pvids_get(self, bridge_port_name):
return self.read_file_oneline("/sys/class/net/%s/brport/pvid" % bridge_port_name)
def bridge_get_stp(self, bridge):
stp_state_path = "/sys/class/net/%s/bridge/stp_state" % bridge
if not os.path.exists(stp_state_path):
return "error"
stp_state = self.read_file_oneline(stp_state_path)
if not stp_state:
return "error"
try:
stp_state_int = int(stp_state)
return "yes" if stp_state_int > 0 else "no"
except:
return "unknown"
def bridge_get_mcqv4src(self, bridge):
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()
return mcqv4src
except Exception as e:
self.logger.info("%s showmcqv4src: skipping unsupported command" % utils.brctl_cmd)
self.bridge_get_mcqv4src = self.bridge_get_mcqv4src_dry_run
return {}
@staticmethod
def bridge_get_mcqv4src_dry_run(bridge):
return {}
############################################################################
# BOND
############################################################################
def bond_remove_slave(self, bond_name, slave_name):
if self.cache.is_link_enslaved_to(slave_name, bond_name):
if self.write_to_file("/sys/class/net/%s/bonding/slaves" % bond_name, "-%s" % slave_name):
# success we can manually update our cache to make sure we stay up-to-date
self.cache.override_cache_unslave_link(slave=slave_name, master=bond_name)
def bond_remove_slave_dry_run(self, bond_name, slave_name):
self.write_to_file("/sys/class/net/%s/bonding/slaves" % bond_name, "-%s" % slave_name)
###
def bond_create(self, bond_name):
if self.cache.bond_exists(bond_name):
return
self.write_to_file("/sys/class/net/bonding_masters", "+%s" % bond_name)
def bond_create_dry_run(self, bond_name):
self.write_to_file("/sys/class/net/bonding_masters", "+%s" % bond_name)
###
def bond_set_attrs_nl(self, bond_name, ifla_info_data):
"""
bond_set_attrs_nl doesn't need a _dry_run handler because each
entry in ifla_info_data was checked against the cache already.
Here write_to_file already has a dry_run handler.
:param bond_name:
:param ifla_info_data:
:return:
"""
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.__bond_netlink_to_sysfs_attr_map.get(nl_attr)
if bond_attr_name is None:
self.logger.warning(
"%s: sysfs configuration: unknown bond attribute %s (value %s)"
% (bond_name, nl_attr, value)
)
continue
file_path = "/sys/class/net/%s/bonding/%s" % (bond_name, bond_attr_name)
if os.path.exists(file_path):
self.write_to_file(file_path, str(value))
except Exception as e:
self.logger.warning("%s: %s %s: %s" % (bond_name, bond_attr_name, value, str(e)))
############################################################################
# /proc/sys/ipv6/conf
############################################################################
def get_ipv6_conf_disable_ipv6(self, ifname):
return int(self.read_file_oneline("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname) or 0)
Sysfs = __Sysfs()

View File

@@ -104,7 +104,7 @@ OPTIONS
-p, --print-dependency {list,dot}
print iface dependency in list or dot format
--admin-state, --no-scripts
-m, --admin-state, --no-scripts
don't run any addon modules/scripts. Only bring
the interface administratively up/down

View File

@@ -104,7 +104,7 @@ OPTIONS
-p, --print-dependency {list,dot}
print iface dependency in list or dot format
--admin-state, --no-scripts
-m, --admin-state, --no-scripts
don't run any addon modules/scripts. Only bring
the interface administratively up/down

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
# Copyright (C) 2015-2020 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@@ -32,15 +32,21 @@ from struct import pack, unpack, calcsize
from threading import Thread, Event, Lock
from Queue import Queue
import logging
import signal
import socket
import errno
import os
log = logging.getLogger(__name__)
class NetlinkListener(Thread):
# As defined in asm/socket.h
_SO_ATTACH_FILTER = 26
def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvbuf_sz=10000000):
RECV_BUFFER = 4096 # 1024 * 1024
def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvbuf_sz=10000000, bpf_filter=None):
"""
groups controls what types of messages we are interested in hearing
To get everything pass:
@@ -50,12 +56,15 @@ class NetlinkListener(Thread):
RTMGRP_IPV6_IFADDR | \
RTMGRP_IPV6_ROUTE
"""
Thread.__init__(self)
Thread.__init__(self, name='NetlinkListener')
self.manager = manager
self.shutdown_event = Event()
self.groups = groups
self.pid_offset = pid_offset
self.rcvbuf_sz = rcvbuf_sz
self.bpf_filter = bpf_filter
self.rx_socket = None
self.rx_socket_prev_seq = {}
# if the app has requested for error notification socket errors will
# be sent via the SERVICE_ERROR event
@@ -63,7 +72,8 @@ class NetlinkListener(Thread):
self.supported_messages = [RTM_NEWLINK, RTM_DELLINK, RTM_NEWADDR,
RTM_DELADDR, RTM_NEWNEIGH, RTM_DELNEIGH,
RTM_NEWROUTE, RTM_DELROUTE]
RTM_NEWROUTE, RTM_DELROUTE,
RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB]
self.ignore_messages = [RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH,
RTM_GETROUTE, RTM_GETQDISC, NLMSG_ERROR, NLMSG_DONE]
@@ -86,34 +96,92 @@ class NetlinkListener(Thread):
if msgtype not in self.ignore_messages:
self.ignore_messages.append(msgtype)
def __bind_rx_socket(self, pid):
"""
bind rx_socket and retry mechanism in case of failure and collision
i.e.: [Errno 98] Address already in use
We will retry NLMANAGER_BIND_RETRY times (defaults to 4242)
:param pid:
:return:
"""
pid_offset = self.pid_offset
for i in xrange(0, int(os.getenv("NLMANAGER_BIND_RETRY", 4242))):
try:
pid_offset += i
self.rx_socket.bind((pid | (pid_offset << 22), self.groups))
self.pid_offset = pid_offset
return
except:
pass
# if we reach this line it means we've reach NLMANAGER_BIND_RETRY limit
# and couldn't successfully bind the rx_socket... We will try one more
# time but without catching the related exception.
self.rx_socket.bind((pid | (self.pid_offset << 22), self.groups))
def run(self):
manager = self.manager
header_PACK = 'IHHII'
header_LEN = calcsize(header_PACK)
try:
header_PACK = 'IHHII'
header_LEN = calcsize(header_PACK)
# The RX socket is used to listen to all netlink messages that fly by
# as things change in the kernel. We need a very large SO_RCVBUF here
# else we tend to miss messages.
# PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to
# use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
# with the netlink manager which always attempts to bind with the pid.
self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
_SO_RCVBUFFORCE = socket.SO_RCVBUFFORCE if hasattr(socket, 'SO_RCVBUFFORCE') else 33
self.rx_socket.setsockopt(socket.SOL_SOCKET, _SO_RCVBUFFORCE, self.rcvbuf_sz)
self.rx_socket.bind((manager.pid | (self.pid_offset << 22), self.groups))
self.rx_socket_prev_seq = {}
# The RX socket is used to listen to all netlink messages that fly by
# as things change in the kernel. We need a very large SO_RCVBUF here
# else we tend to miss messages.
# PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to
# use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
# with the netlink manager which always attempts to bind with the pid.
self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
try:
self.rx_socket.setsockopt(
socket.SOL_SOCKET,
socket.SO_RCVBUFFORCE if hasattr(socket, 'SO_RCVBUFFORCE') else 33,
self.rcvbuf_sz
)
if self.bpf_filter is not None:
self.rx_socket.setsockopt(
socket.SOL_SOCKET,
NetlinkListener._SO_ATTACH_FILTER,
self.bpf_filter
)
except Exception as e:
log.debug("nllistener: rx socket: setsockopt: %s" % str(e))
manager.target_lock.acquire()
if not manager.tx_socket:
manager.tx_socket_allocate()
manager.target_lock.release()
self.__bind_rx_socket(manager.pid)
my_sockets = (manager.tx_socket, self.rx_socket)
with manager.target_lock:
if not manager.tx_socket:
manager.tx_socket_allocate()
socket_string = {
manager.tx_socket: "TX",
self.rx_socket: "RX"
}
my_sockets = (manager.tx_socket, self.rx_socket)
socket_string = {
manager.tx_socket: "TX",
self.rx_socket: "RX"
}
except Exception as e:
if self.rx_socket:
self.rx_socket.close()
self.rx_socket = None
# before notifying the main thread we need to set
# manager.listener_ready properly to signal the failure
manager.listener_ready = False
manager.listener_event_ready.set()
if logging.root.level == logging.DEBUG:
# in debug mode we raise the exception so it can be displayed
# in the terminal: "Exception in thread NetlinkListener..."
raise
else:
log.error("netlink: listener thread: rx socket: %s" % str(e))
return
# Notify main thread that the NetlinkListener thread
# has started and is ready to start processing data
manager.listener_ready = True
manager.listener_event_ready.set()
while True:
@@ -123,7 +191,8 @@ class NetlinkListener(Thread):
# Only block for 1 second so we can wake up to see if shutdown_event is set
try:
(readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 1)
(readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 0.1)
# when ifupdown2 is not running we could change the timeout to 1 sec or more
except Exception as e:
log.error('select() error: ' + str(e))
continue
@@ -139,7 +208,7 @@ class NetlinkListener(Thread):
data = []
try:
data = s.recv(4096)
data = s.recv(self.RECV_BUFFER)
except socket.error, e:
log.error('recv() error: ' + str(e))
data = []
@@ -155,8 +224,15 @@ class NetlinkListener(Thread):
# Extract the length, etc from the header
(length, msgtype, flags, seq, pid) = unpack(header_PACK, data[:header_LEN])
msgtype_str = NetlinkPacket.type_to_string.get(msgtype)
if not msgtype_str:
data = data[length:]
log.debug('%s %s: RXed unknown/unsupported msg type %s skipping netlink message...' % (self, socket_string[s], msgtype))
continue
log.debug('%s %s: RXed %s seq %d, pid %d, %d bytes (%d total)' %
(self, socket_string[s], NetlinkPacket.type_to_string[msgtype],
(self, socket_string[s], msgtype_str,
seq, pid, length, total_length))
possible_ack = False
@@ -172,7 +248,13 @@ class NetlinkListener(Thread):
msg.decode_packet(length, flags, seq, pid, data)
if error_code:
log.debug("%s %s: RXed NLMSG_ERROR code %s (%d)" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code))
log.debug("%s %s: RXed NLMSG_ERROR code %s (%d): %s" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code, msg.error_to_human_readable_string.get(error_code)))
else:
log.debug("%s %s: RXed NLMSG_ERROR code %s (%d): %s... this is an ACK" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code, msg.error_to_human_readable_string.get(error_code)))
if manager.errorq_enabled:
with manager.errorq_lock:
manager.errorq.append(msg)
if possible_ack and seq == manager.target_seq and pid == manager.target_pid:
log.debug("%s %s: Setting RXed ACK alarm for seq %d, pid %d" %
@@ -213,17 +295,16 @@ class NetlinkListener(Thread):
data = data[length:]
if set_tx_socket_rxed_ack_alarm:
manager.target_lock.acquire()
manager.target_seq = None
manager.target_pid = None
manager.target_lock.release()
with manager.target_lock:
manager.target_seq = None
manager.target_pid = None
manager.tx_socket_rxed_ack.set()
if set_alarm:
manager.workq.put(('SERVICE_NETLINK_QUEUE', None))
manager.workq.put((manager.WORKQ_SERVICE_NETLINK_QUEUE, None))
if set_overrun:
manager.workq.put(('SERVICE_ERROR', "OVERFLOW"))
manager.workq.put((manager.WORKQ_SERVICE_ERROR, "OVERFLOW"))
if set_alarm or set_overrun:
manager.alarm.set()
@@ -233,7 +314,10 @@ class NetlinkListener(Thread):
class NetlinkManagerWithListener(NetlinkManager):
def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, error_notification=False, rcvbuf_sz=10000000):
WORKQ_SERVICE_NETLINK_QUEUE = 1
WORKQ_SERVICE_ERROR = 2
def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, error_notification=False, rcvbuf_sz=10000000, bpf_filter=None):
NetlinkManager.__init__(self, use_color=use_color, pid_offset=pid_offset)
self.groups = groups
self.workq = Queue()
@@ -255,6 +339,14 @@ class NetlinkManagerWithListener(NetlinkManager):
self.rcvbuf_sz = rcvbuf_sz
self.error_notification = error_notification
self.pid_offset = pid_offset
self.bpf_filter = bpf_filter
self.errorq = None
self.errorq_lock = None
self.errorq_enabled = False
self.listener_event_ready = None
self.listener_ready = None
# Listen to netlink messages
if start_listener:
@@ -266,11 +358,26 @@ class NetlinkManagerWithListener(NetlinkManager):
return 'NetlinkManagerWithListener'
def restart_listener(self):
self.listener = NetlinkListener(self, self.groups, self.pid_offset+1, self.error_notification, self.rcvbuf_sz)
"""
(re)Start Netlink listener thread and make sure to wait until
the newly created thread is ready.
:return:
"""
self.listener_event_ready = Event()
self.listener_ready = False
self.listener = NetlinkListener(self, self.groups, self.pid_offset + 1, self.error_notification, self.rcvbuf_sz, self.bpf_filter)
self.listener.start()
def signal_term_handler(self, signal, frame):
log.info("NetlinkManagerWithListener: Caught SIGTERM")
self.listener_event_ready.wait()
if not self.listener_ready:
self.listener.join()
# TODO: add custom exception (easier to ignore and recognize)
raise Exception()
def signal_term_handler(self, sig, frame):
if sig == signal.SIGTERM:
log.info("NetlinkManagerWithListener: Caught SIGTERM")
if self.listener:
self.listener.shutdown_event.set()
@@ -290,19 +397,23 @@ class NetlinkManagerWithListener(NetlinkManager):
self.alarm.set()
def tx_nlpacket_get_response(self, nlpacket):
# WARNING: having multiple threads waiting for ACKs might result in
# undefined behavior. To make this work we should probably have a
# (thread-safe) list of all the target SEQs and PIDs along side a
# reference to their alarms (thead.Event) to notify the right thread
# of the RXed ACK.
"""
TX the message and wait for an ack
"""
# NetlinkListener looks at the manager's target_seq and target_pid
# to know when we've RXed the ack that we want
self.target_lock.acquire()
self.target_seq = nlpacket.seq
self.target_pid = nlpacket.pid
with self.target_lock:
self.target_seq = nlpacket.seq
self.target_pid = nlpacket.pid
if not self.tx_socket:
self.tx_socket_allocate()
self.target_lock.release()
if not self.tx_socket:
self.tx_socket_allocate()
log.debug('%s TX: TXed %s seq %d, pid %d, %d bytes' %
(self, NetlinkPacket.type_to_string[nlpacket.msgtype],
@@ -325,13 +436,29 @@ class NetlinkManagerWithListener(NetlinkManager):
log.debug("RXed RTM_DELLINK seq %d, pid %d, %d bytes, for %s, state %s" %
(msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFLA_IFNAME), "up" if msg.is_up() else "down"))
def rx_rtm_newnetconf(self, msg):
ifindex = msg.get_attribute_value(msg.NETCONFA_IFINDEX)
ifname = self.ifname_by_index.get(ifindex)
if ifname:
log.debug("RXed RTM_NEWNETCONF seq %d, pid %d, %d bytes on ifname %s" % (msg.seq, msg.pid, msg.length, ifname))
else:
log.debug("RXed RTM_NEWNETCONF seq %d, pid %d, %d bytes on ifindex %s" % (msg.seq, msg.pid, msg.length, ifindex))
def rx_rtm_delnetconf(self, msg):
ifindex = msg.get_attribute_value(msg.NETCONFA_IFINDEX)
ifname = self.ifname_by_index.get(ifindex)
if ifname:
log.debug("RXed RTM_DELNETCONF seq %d, pid %d, %d bytes on ifname %s" % (msg.seq, msg.pid, msg.length, ifname))
else:
log.debug("RXed RTM_DELNETCONF seq %d, pid %d, %d bytes on ifindex %s" % (msg.seq, msg.pid, msg.length, ifindex))
def rx_rtm_newaddr(self, msg):
log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
(msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s on %s" % (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), self.ifname_by_index.get(msg.ifindex)))
def rx_rtm_deladdr(self, msg):
log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
(msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s on %s" % (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), self.ifname_by_index.get(msg.ifindex)))
def rx_rtm_newneigh(self, msg):
log.debug("RXed RTM_NEWNEIGH seq %d, pid %d, %d bytes, for %s on %s" %
@@ -349,6 +476,12 @@ class NetlinkManagerWithListener(NetlinkManager):
log.debug("RXed RTM_DELROUTE seq %d, pid %d, %d bytes, for %s%s" %
(msg.seq, msg.pid, msg.length, msg.get_prefix_string(), msg.get_nexthops_string(self.ifname_by_index)))
def rx_rtm_newmdb(self, msg):
log.debug("RXed RTM_NEWMDB")
def rx_rtm_delmdb(self, msg):
log.debug("RXed RTM_DELMDB")
def rx_nlmsg_done(self, msg):
log.debug("RXed NLMSG_DONE seq %d, pid %d, %d bytes" % (msg.seq, msg.pid, msg.length))
@@ -552,7 +685,7 @@ class NetlinkManagerWithListener(NetlinkManager):
def filter_by_nested_attribute(self, add, filter_type, msgtype, attr_filter):
self._filter_update(add, filter_type, msgtype, ('NESTED_ATTRIBUTE', attr_filter))
def service_netlinkq(self):
def service_netlinkq(self, notify_event=None):
msg_count = {}
processed = 0
@@ -577,6 +710,12 @@ class NetlinkManagerWithListener(NetlinkManager):
elif msgtype == RTM_NEWROUTE or msgtype == RTM_DELROUTE:
msg = Route(msgtype, debug, use_color=self.use_color)
elif msgtype in (RTM_GETNETCONF, RTM_NEWNETCONF, RTM_DELNETCONF):
msg = Netconf(msgtype, debug, use_color=self.use_color)
elif msgtype == RTM_NEWMDB or msgtype == RTM_DELMDB:
msg = MDB(msgtype, debug, use_color=self.use_color)
elif msgtype == NLMSG_DONE:
msg = Done(msgtype, debug, use_color=self.use_color)
@@ -630,6 +769,18 @@ class NetlinkManagerWithListener(NetlinkManager):
elif msg.msgtype == RTM_DELROUTE:
self.rx_rtm_delroute(msg)
elif msg.msgtype == RTM_NEWNETCONF:
self.rx_rtm_newnetconf(msg)
elif msg.msgtype == RTM_DELNETCONF:
self.rx_rtm_delnetconf(msg)
elif msg.msgtype == RTM_NEWMDB:
self.rx_rtm_newmdb(msg)
elif msg.msgtype == RTM_DELMDB:
self.rx_rtm_delmdb(msg)
elif msg.msgtype == NLMSG_DONE:
self.rx_nlmsg_done(msg)
@@ -639,6 +790,9 @@ class NetlinkManagerWithListener(NetlinkManager):
if processed:
self.netlinkq = self.netlinkq[processed:]
if notify_event:
notify_event.set()
# too chatty
# for msgtype in msg_count:
# log.debug('RXed %d %s messages' % (msg_count[msgtype], NetlinkPacket.type_to_string[msgtype]))

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
# Copyright (C) 2015-2020 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@@ -130,7 +130,10 @@ class NetlinkManager(object):
self._debug_set_clear((RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE), enabled)
def debug_netconf(self, enabled):
self._debug_set_clear((RTM_GETNETCONF, RTM_NEWNETCONF), enabled)
self._debug_set_clear((RTM_GETNETCONF, RTM_NEWNETCONF, RTM_DELNETCONF), enabled)
def debug_mdb(self, enabled):
self._debug_set_clear((RTM_GETMDB, RTM_NEWMDB, RTM_DELMDB), enabled)
def debug_this_packet(self, mtype):
if mtype in self.debug:
@@ -353,6 +356,9 @@ class NetlinkManager(object):
elif msgtype in (RTM_GETNETCONF, RTM_NEWNETCONF):
msg = Netconf(msgtype, nlpacket.debug, use_color=self.use_color)
elif msgtype in (RTM_GETMDB, RTM_NEWMDB, RTM_DELMDB):
msg = MDB(msgtype, nlpacket.debug, use_color=self.use_color)
else:
raise Exception("RXed unknown netlink message type %s" % msgtype)
@@ -396,6 +402,10 @@ class NetlinkManager(object):
msg = Route(rtm_type, debug, use_color=self.use_color)
msg.body = pack('Bxxxii', family, 0, 0)
elif rtm_type == RTM_GETMDB:
msg = MDB(rtm_type, debug, use_color=self.use_color)
msg.body = pack('Bxxxii', family, 0, 0)
else:
log.error("request_dump RTM_GET %s is not supported" % rtm_type)
return None
@@ -723,24 +733,24 @@ class NetlinkManager(object):
return self._link_add(ifindex, ifname, 'vlan', ifla_info_data)
def link_add_macvlan(self, ifindex, ifname):
def link_add_macvlan(self, ifindex, ifname, macvlan_mode):
"""
ifindex is the index of the parent interface that this sub-interface
is being added to
"""
return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE})
def link_add_xfrm(self, physdev, xfrm_ifname, xfrm_id):
"""
ifindex is the index of the parent interface that this sub-interface
is being added to
"""
ifla_info_data = {
Link.IFLA_XFRM_IF_ID: int(xfrm_id),
Link.IFLA_XFRM_LINK: int(physdev)
}
return self._link_add(ifindex=None, ifname=xfrm_ifname, kind='xfrm', ifla_info_data=ifla_info_data)
return self._link_add(
ifindex,
ifname,
'macvlan',
{
Link.IFLA_MACVLAN_MODE: {
"private": Link.MACVLAN_MODE_PRIVATE,
"vepa": Link.MACVLAN_MODE_VEPA,
"bridge": Link.MACVLAN_MODE_BRIDGE,
"passthru": Link.MACVLAN_MODE_PASSTHRU
}.get(macvlan_mode, Link.MACVLAN_MODE_PRIVATE)
}
)
def vlan_get(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
"""
@@ -1077,3 +1087,14 @@ class NetlinkManager(object):
msg.flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK
msg.build_message(self.sequence.next(), self.pid)
return self.tx_nlpacket_get_response(msg)
# ===
# MDB
# ===
def mdb_dump(self):
debug = RTM_GETMDB in self.debug
msg = MDB(RTM_GETMDB, debug, use_color=self.use_color)
msg.body = pack('Bxxxiii', socket.AF_BRIDGE, 0, 0, 0)
msg.flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK
msg.build_message(self.sequence.next(), self.pid)
return self.tx_nlpacket_get_response(msg)

File diff suppressed because it is too large Load Diff

View File

@@ -1,288 +0,0 @@
#!/usr/bin/python
#
# Copyright 2017 Cumulus Networks, Inc. All rights reserved.
# Authors:
# Roopa Prabhu, roopa@cumulusnetworks.com
# Julien Fortin, julien@cumulusnetworks.com
#
# ifupdown2 --
# tool to configure network interfaces
#
import os
import re
import json
import fcntl
import struct
import signal
import socket
import daemon
import select
import datetime
import threading
try:
import ifupdown2.ifupdown.argv
from ifupdown2.ifupdown.log import log
from ifupdown2.ifupdown.main import Ifupdown2
except ImportError:
import ifupdown.argv
from ifupdown.log import log
from ifupdown.main import Ifupdown2
class Daemon:
shutdown_event = threading.Event()
def __init__(self):
self.uds = None
self.context = None
self.working_directory = '/var/run/ifupdown2d/'
self.server_address = '/var/run/ifupdown2d/uds'
if not os.path.exists(self.working_directory):
log.info('creating %s' % self.working_directory)
os.makedirs(self.working_directory, mode=0755)
if os.path.exists(self.server_address):
log.info('removing uds %s' % self.server_address)
os.remove(self.server_address)
self.context = daemon.DaemonContext(
working_directory=self.working_directory,
signal_map={
signal.SIGINT: self.signal_handler,
signal.SIGTERM: self.signal_handler,
signal.SIGQUIT: self.signal_handler,
},
umask=0o22
)
try:
self.SO_PEERCRED = socket.SO_PEERCRED
except AttributeError:
# powerpc is the only non-generic we care about. alpha, mips,
# sparc, and parisc also have non-generic values.
machine = os.uname()[4]
if re.search(r'^(ppc|powerpc)', machine):
self.SO_PASSCRED = 20
self.SO_PEERCRED = 21
else:
self.SO_PASSCRED = 16
self.SO_PEERCRED = 17
log.info('daemonizing ifupdown2d...')
self.context.open()
log.info('preloading all necessary modules')
self.preload_imports()
try:
log.info('opening UNIX socket')
self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
fcntl.fcntl(self.uds.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
except Exception as e:
raise Exception('socket: %s' % str(e))
try:
self.uds.bind(self.server_address)
except Exception as e:
raise Exception('bind: %s' % str(e))
try:
self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
except Exception as e:
raise Exception('setsockopt: %s' % str(e))
try:
self.uds.listen(1)
except Exception as e:
raise Exception('listen: %s' % str(e))
os.chmod(self.server_address, 0777)
def __del__(self):
if self.context:
self.context.close()
if self.uds:
self.uds.close()
@staticmethod
def preload_imports():
"""
preloading all the necessary modules
at first will increase performances
"""
try:
import io
import pdb
import imp
import sets
import json
import glob
import time
import copy
import errno
import pprint
import atexit
import ipaddr
import cPickle
import logging
import argparse
import StringIO
import datetime
import traceback
import itertools
import subprocess
import argcomplete
import collections
import ConfigParser
import pkg_resources
import ifupdown2.ifupdown.exceptions
import ifupdown2.ifupdown.graph
import ifupdown2.ifupdown.iface
import ifupdown2.ifupdown.iff
import ifupdown2.ifupdown.ifupdownbase
import ifupdown2.ifupdown.ifupdownbase
import ifupdown2.ifupdown.ifupdownconfig
import ifupdown2.ifupdown.ifupdownflags
import ifupdown2.ifupdown.ifupdownmain
import ifupdown2.ifupdown.netlink
import ifupdown2.ifupdown.networkinterfaces
import ifupdown2.ifupdown.policymanager
import ifupdown2.ifupdown.scheduler
import ifupdown2.ifupdown.statemanager
import ifupdown2.ifupdown.template
import ifupdown2.ifupdown.utils
import ifupdown2.ifupdownaddons.cache
import ifupdown2.ifupdownaddons.dhclient
import ifupdown2.ifupdownaddons.mstpctlutil
import ifupdown2.ifupdownaddons.LinkUtils
import ifupdown2.ifupdownaddons.modulebase
import ifupdown2.ifupdownaddons.systemutils
import ifupdown2.ifupdownaddons.utilsbase
except ImportError, e:
raise ImportError('%s - required module not found' % str(e))
@staticmethod
def signal_handler(sig, frame):
log.info('received %s' % 'SIGINT' if sig == signal.SIGINT else 'SIGTERM')
Daemon.shutdown_event.set()
@staticmethod
def user_waiting_for_reply():
return not log.is_syslog()
def run(self):
try:
while True:
if Daemon.shutdown_event.is_set():
log.info("shutdown signal RXed, breaking out loop")
break
try:
(client_socket, client_address) = self.uds.accept()
except socket.error as e:
log.error(str(e))
break
pid = os.fork()
if pid == 0:
exit(self.ifupdown2(client_socket))
else:
log.tx_data(json.dumps({'pid': pid}), socket=client_socket)
start = datetime.datetime.now()
status = os.WEXITSTATUS(os.waitpid(pid, 0)[1])
end = datetime.datetime.now()
log.tx_data(json.dumps({'status': status}), socket=client_socket)
client_socket.close()
log.info('exit status %d - in %ssecs'
% (status, (end - start).total_seconds()))
except Exception as e:
log.error(e)
self.uds.close()
def get_client_uid(self, client_socket):
creds = client_socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize('3i'))
(pid, uid, gid) = struct.unpack('3i', creds)
log.debug('client uid %d' % uid)
return uid
@staticmethod
def get_client_request(client_socket):
"""
This function handles requests of any length.
if the received json is longer than 65k it will be truncated
several calls to recv will be needed, we store the data until
we can decode them with the json library.
"""
data = []
while True:
log.debug('waiting for request on client socket')
ready = select.select([client_socket], [], [])
if ready and ready[0] and ready[0][0] == client_socket:
# data available start reading
raw_data = client_socket.recv(65536)
try:
return json.loads(raw_data)
except ValueError:
# the json is incomplete
data.append(raw_data)
if len(data) > 1:
try:
return json.loads(''.join(data))
except ValueError:
pass
def ifupdown2(self, client_socket):
try:
fcntl.fcntl(client_socket.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
ifupdown2 = Ifupdown2(daemon=True, uid=self.get_client_uid(client_socket))
ifupdown2.set_signal_handlers()
request = self.get_client_request(client_socket)
log.info('request: %s' % request['argv'])
ifupdown2.parse_argv(request['argv'])
# adjust the logger with argv
ifupdown2.update_logger(socket=client_socket)
try:
status = ifupdown2.main(request['stdin'])
except Exception as e:
log.error(str(e))
status = 1
except ifupdown2.ifupdown.argv.ArgvParseError as e:
log.update_current_logger(syslog=False, verbose=True, debug=False)
log.set_socket(client_socket)
e.log_error()
status = 1
except Exception as e:
log.error(e)
status = 1
log.flush()
log.set_socket(None)
client_socket.close()
return status
if __name__ == '__main__':
try:
Daemon().run()
except Exception as e:
print e
log.error(str(e))
import traceback
log.error(traceback.format_exc())
exit(1)

View File

@@ -1,168 +0,0 @@
#!/bin/bash
# This replaces the old init.d script, and is run from the networking.service
# Only has start, stop, reload, because that's all systemd has.
# restart is implemented in systemd by stop then start.
RUN_DIR="/run/network"
IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
STATE_DIR="/var/tmp/network"
IFSTATE_FILE="${STATE_DIR}/ifstatenew"
NAME=networking
[ -x /sbin/ifup ] || exit 0
[ -x /sbin/ifdown ] || exit 0
CONFIGURE_INTERFACES=yes
EXTRA_ARGS=
[ -f /etc/default/networking ] && . /etc/default/networking
[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
perf_options() {
# At bootup lets set perfmode
[ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
echo -n "--perfmode"
}
process_exclusions() {
set -- $EXCLUDE_INTERFACES
exclusions=""
for d
do
exclusions="-X $d $exclusions"
done
echo $exclusions
}
check_network_file_systems() {
[ -e /proc/mounts ] || return 0
if [ -e /etc/iscsi/iscsi.initramfs ]; then
echo ${NAME}':' "not deconfiguring network interfaces: iSCSI root is mounted."
exit 0
fi
while read DEV MTPT FSTYPE REST; do
case $DEV in
/dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
echo ${NAME}':' "not deconfiguring network interfaces: network devices still mounted."
exit 0
;;
esac
case $FSTYPE in
nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
echo ${NAME}':' "not deconfiguring network interfaces: network file systems still mounted."
exit 0
;;
esac
done < /proc/mounts
}
check_network_swap() {
[ -e /proc/swaps ] || return 0
while read DEV MTPT FSTYPE REST; do
case $DEV in
/dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
echo ${NAME}':' "not deconfiguring network interfaces: network swap still mounted."
exit 0
;;
esac
done < /proc/swaps
}
ifup_hotplug () {
if [ -d /sys/class/net ]
then
ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
do
link=${iface##:*}
link=${link##.*}
if [ -e "/sys/class/net/$link" ]
then
echo "$iface"
fi
done)
if [ -n "$ifaces" ]
then
ifup $ifaces "$@" || true
fi
fi
}
ifup_mgmt () {
ifaces=$(ifquery --list --allow=mgmt 2>/dev/null)
if [ -n "$ifaces" ]; then
echo "bringing up mgmt class interfaces"
ifup --allow=mgmt
fi
}
ifupdown_init() {
# remove state file at boot
[ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
[ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
[ ! -e /etc/network/run ] && \
ln -sf /run/network /etc/network/run &>/dev/null
}
case "$1" in
start)
ifupdown_init
if [ "$CONFIGURE_INTERFACES" = no ]
then
echo ${NAME}':' "Not configuring network interfaces, see /etc/default/networking"
exit 0
fi
set -f
exclusions=$(process_exclusions)
perfoptions=$(perf_options)
echo ${NAME}':' "Configuring network interfaces"
ifup_mgmt
ifup -a $EXTRA_ARGS $exclusions $perfoptions
ifup_hotplug $HOTPLUG_ARGS $EXTRA_ARGS $exclusions
;;
stop)
if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
SYSRESET=0
systemctl list-jobs | egrep -q '(shutdown|reboot|halt|poweroff)\.target'
[ $? -eq 0 ] && SYSRESET=1
if [ $SYSRESET -eq 1 ]; then
echo ${NAME}':' "Skipping deconfiguring network interfaces"
exit 0
fi
fi
ifupdown_init
check_network_file_systems
check_network_swap
exclusions=$(process_exclusions)
echo ${NAME}':' "Deconfiguring network interfaces"
ifdown -a $EXTRA_ARGS $exclusions
;;
reload)
ifupdown_init
exclusions=$(process_exclusions)
echo ${NAME}':' "Reloading network interfaces configuration"
ifreload -a $EXTRA_ARGS $exclusions
;;
*)
echo ${NAME}':' "Usage: $0 {start|stop|reload}"
exit 1
;;
esac
exit 0

View File

@@ -12,7 +12,6 @@ INSTALL_REQUIRES = [
]
DATA_FILES = [
('/etc/default/', ['etc/default/networking']),
('/etc/network/ifupdown2/', ['etc/network/ifupdown2/addons.conf']),
('/etc/network/ifupdown2/', ['etc/network/ifupdown2/ifupdown2.conf']),
]
@@ -25,13 +24,12 @@ ENTRY_POINTS = {}
def build_deb_package():
try:
return sys.argv[sys.argv.index('--root') + 1].endswith('/debian/ifupdown2')
except:
return False
except Exception:
pass
return False
if build_deb_package():
DATA_FILES.append(('/usr/share/ifupdown2/sbin/', ['ifupdown2/sbin/start-networking']))
else:
if not build_deb_package():
ENTRY_POINTS = {
'console_scripts': [
'ifup = ifupdown2.__main__:main',
@@ -42,8 +40,8 @@ else:
}
setup(
author='Roopa Prabhu',
author_email='roopa@cumulusnetworks.com',
author='Julien Fortin',
author_email='julien@cumulusnetworks.com',
maintainer='Julien Fortin',
maintainer_email='julien@cumulusnetworks.com',
classifiers=[
@@ -65,7 +63,7 @@ setup(
name='ifupdown2',
packages=find_packages(),
url='https://github.com/CumulusNetworks/ifupdown2',
version='1.2.9',
version='2.0.0',
data_files=DATA_FILES,
setup_requires=['setuptools'],
scripts=SCRIPTS,