diff --git a/.gitignore b/.gitignore index bd4b7662..60a10f6c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ dist/ downloads/ eggs/ .eggs/ -lib/ +/lib/ lib64/ parts/ sdist/ diff --git a/debian/changelog b/debian/changelog index 3ff53e75..b64932ed 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,22 @@ -ifupdown2 (1.2.9-1) unstable; urgency=medium - - * - - -- Julien Fortin Thu, 11 Jul 2019 23:42:42 +0200 +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 Tue, 01 Oct 2019 23:42:42 +0200 ifupdown2 (1.2.8-1) unstable; urgency=medium diff --git a/debian/control b/debian/control index dd1a0b2c..7809864c 100644 --- a/debian/control +++ b/debian/control @@ -2,13 +2,15 @@ Source: ifupdown2 Section: admin Priority: optional Maintainer: Julien Fortin -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. diff --git a/debian/ifup@.service b/debian/ifup@.service new file mode 100644 index 00000000..da1891b3 --- /dev/null +++ b/debian/ifup@.service @@ -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 diff --git a/debian/ifupdown2.install b/debian/ifupdown2.install index 7444031d..0ecf4b98 100644 --- a/debian/ifupdown2.install +++ b/debian/ifupdown2.install @@ -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/ diff --git a/debian/ifupdown2.networking.service b/debian/ifupdown2.networking.service index 06e1545b..b2acd97a 100644 --- a/debian/ifupdown2.networking.service +++ b/debian/ifupdown2.networking.service @@ -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 diff --git a/debian/rules b/debian/rules index 36356002..6274b3a5 100755 --- a/debian/rules +++ b/debian/rules @@ -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 diff --git a/debian/watch b/debian/watch new file mode 100644 index 00000000..9e7c0dae --- /dev/null +++ b/debian/watch @@ -0,0 +1 @@ +version=3 diff --git a/etc/default/networking b/etc/default/networking deleted file mode 100644 index cc3d3ef1..00000000 --- a/etc/default/networking +++ /dev/null @@ -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" diff --git a/debian/networking.init b/etc/init.d/networking similarity index 100% rename from debian/networking.init rename to etc/init.d/networking diff --git a/etc/network/ifupdown2/addons.conf b/etc/network/ifupdown2/addons.conf index dd3d2237..c43d3776 100644 --- a/etc/network/ifupdown2/addons.conf +++ b/etc/network/ifupdown2/addons.conf @@ -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 \ No newline at end of file +post-down,xfrm diff --git a/etc/network/ifupdown2/ifupdown2.conf b/etc/network/ifupdown2/ifupdown2.conf index e05c35f6..ecdd94f3 100644 --- a/etc/network/ifupdown2/ifupdown2.conf +++ b/etc/network/ifupdown2/ifupdown2.conf @@ -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/ diff --git a/ifupdown2/__init__.py b/ifupdown2/__init__.py index 151f650f..e60c8d92 100644 --- a/ifupdown2/__init__.py +++ b/ifupdown2/__init__.py @@ -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 diff --git a/ifupdown2/__main__.py b/ifupdown2/__main__.py index 4dd4270b..4477b822 100755 --- a/ifupdown2/__main__.py +++ b/ifupdown2/__main__.py @@ -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__ -except: - import ifupdown.config as core_config - from ifupdown.log import log + config.__version__ = __version__ - core_config.__version__ = __import__('__init__').__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 config -class Ifupdown2Complete(Exception): - def __init__(self, status): - self.status = status + config.__version__ = __import__("__init__").__version__ + from lib.exceptions import ExitWithStatus, ExitWithStatusAndError -class Ifupdown2Client: - def __init__(self, argv): + from ifupdown.client import Client + from ifupdown.exceptions import ArgvParseHelp - 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: + 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: - import ifupdown2.ifupdown.main as main_ifupdown2 + 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) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index a8ef1e25..83974d79 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -6,15 +6,16 @@ import socket -from ipaddr import IPNetwork, IPv4Network, IPv6Network, _BaseV6 +from ipaddr import IPNetwork, IPv4Network, IPv6Network 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.ifupdownaddons.dhclient import dhclient - from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils from ifupdown2.ifupdownaddons.modulebase import moduleBase import ifupdown2.ifupdown.statemanager as statemanager @@ -22,12 +23,13 @@ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig 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 ifupdownaddons.dhclient import dhclient - from ifupdownaddons.LinkUtils import LinkUtils from ifupdownaddons.modulebase import moduleBase import ifupdown.statemanager as statemanager @@ -36,103 +38,144 @@ import ifupdown.ifupdownconfig as ifupdownconfig -class address(moduleBase): +class address(Addon, moduleBase): """ ifupdown2 addon module to configure address, mtu, hwaddress, alias (description) on an interface """ - _modinfo = {'mhelp' : 'address configuration module for interfaces', - 'attrs': { - 'address' : - {'help' : 'ipv4 or ipv6 addresses', - 'validvals' : ['', ''], - 'multiline' : True, - 'example' : ['address 10.0.12.3/24', - 'address 2000:1000:1000:1000:3::5/128']}, - 'netmask' : - {'help': 'netmask', - 'example' : ['netmask 255.255.255.0'], - 'compat' : True}, - 'broadcast' : - {'help': 'broadcast address', - 'validvals' : ['', ], - 'example' : ['broadcast 10.0.1.255']}, - 'scope' : - {'help': 'scope', - 'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'], - 'example' : ['scope host']}, - 'preferred-lifetime' : - {'help': 'preferred lifetime', - 'validrange' : ['0', '65535'], - 'example' : ['preferred-lifetime forever', - 'preferred-lifetime 10']}, - 'gateway' : - {'help': 'default gateway', - 'validvals' : ['', ''], - 'multiline' : True, - 'example' : ['gateway 255.255.255.0']}, - 'mtu' : - { 'help': 'interface mtu', - 'validrange' : ['552', '9216'], - 'example' : ['mtu 1600'], - 'default' : '1500'}, - 'hwaddress' : - {'help' : 'hw address', - 'validvals' : ['',], - 'example': ['hwaddress 44:38:39:00:27:b8']}, - 'alias' : - { 'help': 'description/alias', - 'example' : ['alias testnetwork']}, - 'address-purge' : - { 'help': 'purge existing addresses. By default ' + - 'any existing ip addresses on an interface are ' + - 'purged to match persistant addresses in the ' + - 'interfaces file. Set this attribute to \'no\'' + - 'if you want to preserve existing addresses', - 'validvals' : ['yes', 'no'], - 'default' : 'yes', - 'example' : ['address-purge yes/no']}, - 'clagd-vxlan-anycast-ip' : - { 'help' : 'Anycast local IP address for ' + - 'dual connected VxLANs', - 'validvals' : ['', ], - 'example' : ['clagd-vxlan-anycast-ip 36.0.0.11']}, - 'arp-accept' : - { 'help': 'Allow gratuitous arp to update arp table', - 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], - 'default' : 'off', - 'example' : ['arp-accept on']}, - 'ip-forward' : - { 'help': 'ip forwarding flag', - 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], - 'default' : 'off', - 'example' : ['ip-forward off']}, - 'ip6-forward' : - { 'help': 'ipv6 forwarding flag', - 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], - 'default' : 'off', - 'example' : ['ip6-forward off']}, - 'mpls-enable' : - { 'help': 'mpls enable flag', - 'validvals': ['yes', 'no'], - 'default' : 'no', - 'example' : ['mpls-enable yes']}, - 'ipv6-addrgen': { - 'help': 'enable disable ipv6 link addrgenmode', - 'validvals': ['on', 'off'], - 'default': 'on', - 'example': [ - 'ipv6-addrgen on', - 'ipv6-addrgen off' - ] - } - }} + _modinfo = { + 'mhelp': 'address configuration module for interfaces', + 'attrs': { + 'address': { + 'help': 'The address of the interface. The format of the ' + 'address depends on the protocol. It is a dotted ' + 'quad for IP and a sequence of hexadecimal halfwords ' + 'separated by colons for IPv6. The ADDRESS may be ' + 'followed by a slash and a decimal number which ' + 'encodes the network prefix length.', + 'validvals': ['', ''], + 'multiline': True, + 'example': [ + 'address 10.0.12.3/24', + 'address 2000:1000:1000:1000:3::5/128' + ] + }, + 'netmask': { + 'help': 'Address netmask', + 'example': ['netmask 255.255.255.0'], + 'compat': True + }, + 'broadcast': { + 'help': 'The broadcast address on the interface.', + 'validvals': [''], + 'example': ['broadcast 10.0.1.255'] + }, + 'scope': { + 'help': 'The scope of the area where this address is valid. ' + 'The available scopes are listed in file /etc/iproute2/rt_scopes. ' + 'Predefined scope values are: ' + 'global - the address is globally valid. ' + 'site - (IPv6 only, deprecated) the address is site local, i.e. it is valid inside this site. ' + 'link - the address is link local, i.e. it is valid only on this device. ' + 'host - the address is valid only inside this host.', + 'validvals': ['universe', 'site', 'link', 'host', 'nowhere'], + 'example': ['scope host'] + }, + 'preferred-lifetime': { + 'help': 'The preferred lifetime of this address; see section ' + '5.5.4 of RFC 4862. When it expires, the address is ' + 'no longer used for new outgoing connections. ' + 'Defaults to forever.', + 'validrange': ['0', '65535'], + 'example': [ + 'preferred-lifetime forever', + 'preferred-lifetime 10' + ] + }, + 'pointopoint': { + 'help': 'Set the remote IP address for a point-to-point link', + 'validvals': ['', ''], + 'example': [ + 'pointopoint 10.10.10.42/32' + ] + }, + 'gateway': { + 'help': 'Default gateway', + 'validvals': ['', ''], + 'multiline': True, + 'example': ['gateway 255.255.255.0'] + }, + 'mtu': { + 'help': 'Interface MTU (maximum transmission unit)', + 'validrange': ['552', '9216'], + 'example': ['mtu 1600'], + 'default': '1500' + }, + 'hwaddress': { + 'help': 'Hardware address (mac)', + 'validvals': [''], + 'example': ['hwaddress 44:38:39:00:27:b8'] + }, + 'alias': { + 'help': 'description/alias: give the device a symbolic name for easy reference.', + 'example': ['alias testnetwork'] + }, + 'address-purge': { + 'help': 'Purge existing addresses. By default any existing ' + 'ip addresses on an interface are purged to match ' + 'persistant addresses in the interfaces file. Set ' + 'this attribute to \'no\' if you want to preserve ' + 'existing addresses', + 'validvals': ['yes', 'no'], + 'default': 'yes', + 'example': ['address-purge yes/no'] + }, + 'clagd-vxlan-anycast-ip': { + 'help': 'Anycast local IP address for dual connected VxLANs', + 'validvals': [''], + 'example': ['clagd-vxlan-anycast-ip 36.0.0.11'] + }, + 'ip-forward': { + 'help': 'ip forwarding flag', + 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], + 'default': 'off', + 'example': ['ip-forward off'] + }, + 'ip6-forward': { + 'help': 'ipv6 forwarding flag', + 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], + 'default': 'off', + 'example': ['ip6-forward off'] + }, + 'mpls-enable': { + 'help': 'mpls enable flag', + 'validvals': ['yes', 'no'], + 'default': 'no', + 'example': ['mpls-enable yes'] + }, + 'ipv6-addrgen': { + 'help': 'enable disable ipv6 link addrgenmode', + 'validvals': ['on', 'off'], + 'default': 'on', + 'example': [ + 'ipv6-addrgen on', + 'ipv6-addrgen off' + ] + }, + 'arp-accept': { + 'help': 'Allow gratuitous arp to update arp table', + 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], + 'default': 'off', + 'example': ['arp-accept on'] + }, + } + } + + DEFAULT_MTU_STRING = "1500" def __init__(self, *args, **kargs): + Addon.__init__(self) moduleBase.__init__(self, *args, **kargs) - self.ipcmd = None self._bridge_fdb_query_cache = {} - self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu') - self.max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='max_mtu') self.ipforward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip-forward') self.ip6forward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip6-forward') self.ifaces_defaults = policymanager.policymanager_api.get_iface_defaults(module_name=self.__class__.__name__) @@ -143,15 +186,10 @@ def __init__(self, *args, **kargs): ) ) - if not self.default_mtu: - self.default_mtu = '1500' - - self.logger.info('address: using default mtu %s' %self.default_mtu) + self.default_mtu = self.__policy_get_default_mtu() + self.max_mtu = self.__policy_get_max_mtu() - if self.max_mtu: - self.logger.info('address: using max mtu %s' %self.max_mtu) - - self.lower_iface_mtu_checked_list = list() + self.default_loopback_addresses = (IPNetwork('127.0.0.1/8'), IPNetwork('::1/128')) self.l3_intf_arp_accept = utils.get_boolean_from_string( policymanager.policymanager_api.get_module_globals( @@ -169,6 +207,39 @@ def __init__(self, *args, **kargs): default=True ) + def __policy_get_default_mtu(self): + default_mtu = policymanager.policymanager_api.get_attr_default( + module_name=self.__class__.__name__, + attr="mtu" + ) + + if not default_mtu: + default_mtu = self.DEFAULT_MTU_STRING + + try: + self.default_mtu_int = int(default_mtu) + except ValueError as e: + self.logger.error("address: invalid default mtu \"%s\" set via policy: %s" % (default_mtu, str(e))) + default_mtu = self.DEFAULT_MTU_STRING + self.default_mtu_int = int(self.DEFAULT_MTU_STRING) + + self.logger.info("address: using default mtu %s" % default_mtu) + + return default_mtu + + def __policy_get_max_mtu(self): + max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr="max_mtu") + if max_mtu: + try: + max_mtu_int = int(max_mtu) + self.logger.info("address: using max mtu %s" % self.max_mtu) + return max_mtu_int + except ValueError as e: + self.logger.warning("address: policy max_mtu: %s" % str(e)) + else: + self.logger.info("address: max_mtu undefined") + return 0 + def syntax_check(self, ifaceobj, ifaceobj_getfunc=None): return (self.syntax_check_multiple_gateway(ifaceobj) and self.syntax_check_addr_allowed_on(ifaceobj, True) @@ -226,10 +297,14 @@ def syntax_check_sysctls(self, ifaceobj): return result def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc): - mtu = ifaceobj.get_attr_value_first('mtu') - if mtu: - return self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc, - syntaxcheck=True) + mtu_str = ifaceobj.get_attr_value_first('mtu') + if mtu_str: + try: + mtu_int = int(mtu_str) + except ValueError as e: + self.logger.warning("%s: invalid mtu %s: %s" % (ifaceobj.name, mtu_str, str(e))) + return False + return self._check_mtu_config(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc, syntaxcheck=True) return True def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False): @@ -280,13 +355,13 @@ def _process_bridge(self, ifaceobj, up): arp_accept = ifaceobj.get_attr_value_first('arp-accept') arp_accept = utils.boolean_support_binary(arp_accept) is_vlan_dev_on_vlan_aware_bridge = False - is_bridge = self.ipcmd.is_bridge(ifaceobj.name) + is_bridge = self.cache.get_link_kind(ifaceobj.name) == 'bridge' if not is_bridge: if ifaceobj.link_kind & ifaceLinkKind.VLAN: bridgename = ifaceobj.lowerifaces[0] vlan = self._get_vlan_id(ifaceobj) - is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename) - if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)) + is_vlan_dev_on_vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridgename) + if ((is_bridge and not self.cache.bridge_is_vlan_aware(ifaceobj.name)) or is_vlan_dev_on_vlan_aware_bridge): if self._address_valid(addrs): if self.l3_intf_arp_accept: @@ -304,152 +379,169 @@ def _process_bridge(self, ifaceobj, up): try: for old_obj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: old_hwaddress = old_obj.get_attr_value_first("hwaddress") - if old_hwaddress and self.ipcmd.mac_str_to_int(old_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress): - self.ipcmd.bridge_fdb_del(bridgename, old_hwaddress, vlan) + if old_hwaddress and utils.mac_str_to_int(old_hwaddress) != utils.mac_str_to_int(hwaddress): + self.iproute2.bridge_fdb_del(bridgename, old_hwaddress, vlan) break except: pass - self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan) + self.iproute2.bridge_fdb_add(bridgename, hwaddress, vlan) else: - self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan) + self.iproute2.bridge_fdb_del(bridgename, hwaddress, vlan) - def _get_anycast_addr(self, ifaceobjlist): - for ifaceobj in ifaceobjlist: - anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip') - if anycast_addr: - anycast_addr = anycast_addr+'/32' - return anycast_addr - return None + def __get_ip_addr_with_attributes(self, ifaceobj_list, ifname): + user_config_ip_addrs_list = list() - def _inet_address_convert_to_cidr(self, ifaceobjlist): - newaddrs = [] - newaddr_attrs = {} + try: + for ifaceobj in ifaceobj_list: - for ifaceobj in ifaceobjlist: - addrs = ifaceobj.get_attr_value('address') - if not addrs: - continue + user_addrs = ifaceobj.get_attr_value("address") - if not self.syntax_check_addr_allowed_on(ifaceobj, - syntax_check=False): - return (False, newaddrs, newaddr_attrs) - # If user address is not in CIDR notation, convert them to CIDR - for addr_index in range(0, len(addrs)): - addr = addrs[addr_index] - newaddr = addr - if '/' in addr: - newaddrs.append(addr) - else: - netmask = ifaceobj.get_attr_value_n('netmask', addr_index) - if netmask: - prefixlen = IPNetwork('%s' %addr + - '/%s' %netmask).prefixlen - newaddr = addr + '/%s' %prefixlen + if not user_addrs: + continue + + if not self.syntax_check_addr_allowed_on(ifaceobj, syntax_check=False): + return False, None + + for index, addr in enumerate(user_addrs): + addr_attributes = {} + addr_obj = None + + # convert the ip from string to IPNetwork object + if "/" in addr: + addr_obj = IPNetwork(addr) else: - # we are here because there is no slash (/xx) and no netmask - # just let IPNetwork handle the ipv4 or ipv6 address mask - prefixlen = IPNetwork(addr).prefixlen - newaddr = addr + '/%s' %prefixlen - newaddrs.append(newaddr) - - attrs = {} - for a in ['broadcast', 'pointopoint', 'scope', - 'preferred-lifetime']: - aval = ifaceobj.get_attr_value_n(a, addr_index) - if aval: - attrs[a] = aval - - if attrs: - newaddr_attrs[newaddr]= attrs - return (True, newaddrs, newaddr_attrs) - - def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs): - for addr_index in range(0, len(newaddrs)): - try: - if newaddr_attrs: - self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index], - newaddr_attrs.get(newaddrs[addr_index], - {}).get('broadcast'), - newaddr_attrs.get(newaddrs[addr_index], - {}).get('pointopoint'), - newaddr_attrs.get(newaddrs[addr_index], - {}).get('scope'), - newaddr_attrs.get(newaddrs[addr_index], - {}).get('preferred-lifetime')) + netmask = ifaceobj.get_attr_value_n("netmask", index) + + if netmask: + addr_obj = IPNetwork("%s/%s" % (addr, netmask)) + else: + addr_obj = IPNetwork(addr) + + for attr_name in ("broadcast", "scope", "preferred-lifetime"): + attr_value = ifaceobj.get_attr_value_n(attr_name, index) + if attr_value: + addr_attributes[attr_name] = attr_value + + pointopoint = ifaceobj.get_attr_value_n("pointopoint", index) + try: + if pointopoint: + addr_attributes["pointopoint"] = IPNetwork(pointopoint) + except Exception as e: + self.logger.warning("%s: pointopoint %s: %s" % (ifaceobj.name, pointopoint, str(e))) + + user_config_ip_addrs_list.append((addr_obj, addr_attributes)) + except Exception as e: + self.logger.warning("%s: convert string ip address into IPNetwork object: %s" % (ifname, str(e))) + return False, None + + return True, user_config_ip_addrs_list + + def __add_ip_addresses_with_attributes(self, ifaceobj, ifname, user_config_ip_addrs): + try: + for ip, attributes in user_config_ip_addrs: + if attributes: + self.netlink.addr_add( + ifname, ip, + scope=attributes.get("scope"), + peer=attributes.get("pointopoint"), + broadcast=attributes.get("broadcast"), + preferred_lifetime=attributes.get("preferred-lifetime") + ) else: - self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index]) - except Exception, e: - self.log_error(str(e), ifaceobj) + self.netlink.addr_add(ifname, ip) + except Exception as e: + self.log_error(str(e), ifaceobj) - def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None, - force_reapply=False): - squash_addr_config = (True if \ - ifupdownconfig.config.get('addr_config_squash', \ - '0') == '1' else False) + @staticmethod + def __add_loopback_anycast_ip_to_running_ip_addr_list(ifaceobjlist): + """ + if anycast address is configured on 'lo' and is in running + config add it to newaddrs so that ifreload doesn't wipe it out + :param ifaceobjlist: + :param running_ip_addrs: + """ + anycast_ip_addr = None - if (squash_addr_config and - not (ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING)): + for ifaceobj in ifaceobjlist: + anycast_addr = ifaceobj.get_attr_value_first("clagd-vxlan-anycast-ip") + if anycast_addr: + anycast_ip_addr = IPNetwork(anycast_addr) + + return str(anycast_ip_addr) if anycast_ip_addr else None + + def process_addresses(self, ifaceobj, ifaceobj_getfunc=None, force_reapply=False): + squash_addr_config = ifupdownconfig.config.get("addr_config_squash", "0") == "1" + + if squash_addr_config and not ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING: return - purge_addresses = ifaceobj.get_attr_value_first('address-purge') - if not purge_addresses: - purge_addresses = 'yes' + ifname = ifaceobj.name + purge_addresses = utils.get_boolean_from_string(ifaceobj.get_attr_value_first("address-purge"), default=True) + + if not squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS: + # if youngest sibling and squash addr is not set + # print a warning that addresses will not be purged + if ifaceobj.flags & iface.YOUNGEST_SIBLING: + self.logger.warning("%s: interface has multiple iface stanzas, skip purging existing addresses" % ifname) + purge_addresses = False if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS: - ifaceobjlist = ifaceobj_getfunc(ifaceobj.name) + ifaceobj_list = ifaceobj_getfunc(ifname) else: - ifaceobjlist = [ifaceobj] - - module_name = self.__class__.__name__ - ifname = ifaceobj.name + ifaceobj_list = [ifaceobj] - (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist) - newaddrs = utils.get_ip_objs(module_name, ifname, newaddrs) + addr_supported, user_config_ip_addrs_list = self.__get_ip_addr_with_attributes(ifaceobj_list, ifname) if not addr_supported: return - if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)): - # if youngest sibling and squash addr is not set - # print a warning that addresses will not be purged - if (ifaceobj.flags & iface.YOUNGEST_SIBLING): - self.logger.warn('%s: interface has multiple ' %ifaceobj.name + - 'iface stanzas, skip purging existing addresses') - purge_addresses = 'no' - if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes': - # if perfmode is not set and purge addresses is not set to 'no' + if not ifupdownflags.flags.PERFMODE and purge_addresses: + # if perfmode is not set and purge addresses is set to True # lets purge addresses not in the config - runningaddrs = self.ipcmd.get_running_addrs(ifaceobj, details=False) + anycast_ip = None + + running_ip_addrs = self.cache.get_ifupdown2_addresses_list(ifaceobj_list, ifname) - # if anycast address is configured on 'lo' and is in running config - # add it to newaddrs so that ifreload doesn't wipe it out - anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist)) + if ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK: + anycast_ip = self.__add_loopback_anycast_ip_to_running_ip_addr_list(ifaceobj_list) - if runningaddrs and anycast_addr and anycast_addr in runningaddrs: - newaddrs.append(anycast_addr) + # user_ip4, user_ip6 and ordered_user_configured_ips IP addresses are now represented + # in string format. Comparaisons between IPNetwork object are not reliable, i.e.: + # IPNetwork("2001:aa::2/64") == IPNetwork("2001:aa::150/64") + user_ip4, user_ip6, ordered_user_configured_ips = self.order_user_configured_addrs(user_config_ip_addrs_list) - user_ip4, user_ip6, newaddrs = self.order_user_configured_addrs(newaddrs) + running_ip_addrs_str = self.get_ipnetwork_object_list_in_string_format(running_ip_addrs) - if newaddrs == runningaddrs or self.compare_running_ips_and_user_config(user_ip4, user_ip6, runningaddrs): + if ordered_user_configured_ips == running_ip_addrs or self.compare_running_ips_and_user_config(user_ip4, user_ip6, running_ip_addrs): if force_reapply: - self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs) + self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list) return try: - # if primary address is not same, there is no need to keep any. - # reset all addresses - if newaddrs and runningaddrs and newaddrs[0] != runningaddrs[0]: + # if primary address is not same, there is no need to keep any, reset all addresses. + if ordered_user_configured_ips and running_ip_addrs_str and ordered_user_configured_ips[0] != running_ip_addrs_str[0]: + self.logger.info("%s: primary ip changed (from %s to %s) we need to purge all ip addresses and re-add them" + % (ifname, ordered_user_configured_ips[0], running_ip_addrs_str[0])) skip_addrs = [] else: - skip_addrs = newaddrs or [] - for addr in runningaddrs or []: + skip_addrs = ordered_user_configured_ips + + if anycast_ip: + skip_addrs.append(anycast_ip) + + for index, addr in enumerate(running_ip_addrs_str): if addr in skip_addrs: continue - self.ipcmd.addr_del(ifaceobj.name, addr) + # we still have to send the IPNetwork object + # to the netlink "addr_del" API + self.netlink.addr_del(ifname, running_ip_addrs[index]) except Exception, e: self.log_warn(str(e)) - if not newaddrs: + if not user_config_ip_addrs_list: return - self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs) + self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list) + + def get_ipnetwork_object_list_in_string_format(self, obj_list): + return [str(obj) for obj in obj_list] def compare_running_ips_and_user_config(self, user_ip4, user_ip6, running_addrs): """ @@ -496,12 +588,13 @@ def compare_running_ips_and_user_config(self, user_ip4, user_ip6, running_addrs) return i == len_ip6 - def order_user_configured_addrs(self, user_config_addrs): + @staticmethod + def order_user_configured_addrs(user_config_addrs): ip4 = [] ip6 = [] - for a in user_config_addrs: - if isinstance(a, _BaseV6): + for a, _ in user_config_addrs: + if a.version == 6: ip6.append(str(a)) else: ip4.append(str(a)) @@ -511,7 +604,7 @@ def order_user_configured_addrs(self, user_config_addrs): def _delete_gateway(self, ifaceobj, gateways, vrf, metric): for del_gw in gateways: try: - self.ipcmd.route_del_gateway(ifaceobj.name, del_gw, vrf, metric) + self.iproute2.route_del_gateway(ifaceobj.name, del_gw, vrf, metric) except Exception as e: self.logger.debug('%s: %s' % (ifaceobj.name, str(e))) @@ -522,7 +615,7 @@ def _add_delete_gateway(self, ifaceobj, gateways=[], prev_gw=[]): vrf, metric) for add_gw in gateways: try: - self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf, metric, onlink=self.l3_intf_default_gateway_set_onlink) + self.iproute2.route_add_gateway(ifaceobj.name, add_gw, vrf, metric, onlink=self.l3_intf_default_gateway_set_onlink) except Exception as e: self.log_error('%s: %s' % (ifaceobj.name, str(e))) @@ -536,7 +629,7 @@ def _get_prev_gateway(self, ifaceobj, gateways): return ipv return prev_gateways - def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False): + def _check_mtu_config(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc, syntaxcheck=False): retval = True if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE): if syntaxcheck: @@ -550,39 +643,41 @@ def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False): masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0]) if masterobj: master_mtu = masterobj[0].get_attr_value_first('mtu') - if master_mtu and master_mtu != mtu: + if master_mtu and master_mtu != mtu_str: + log_msg = ("%s: bond slave mtu %s is different from bond master %s mtu %s. " + "There is no need to configure mtu on a bond slave." % + (ifaceobj.name, mtu_str, masterobj[0].name, master_mtu)) if syntaxcheck: - self.logger.warn('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu)) + self.logger.warn(log_msg) retval = False else: - self.logger.info('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu)) + self.logger.info(log_msg) elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and ifaceobj.lowerifaces): lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0]) if lowerobj: if syntaxcheck: - lowerdev_mtu = lowerobj[0].get_attr_value_first('mtu') + lowerdev_mtu = int(lowerobj[0].get_attr_value_first('mtu') or 0) else: - lowerdev_mtu = self.ipcmd.link_get_mtu_sysfs(lowerobj[0].name) - if lowerdev_mtu and int(mtu) > int(lowerdev_mtu): + lowerdev_mtu = self.cache.get_link_mtu(lowerobj[0].name) # return type: int + if lowerdev_mtu and mtu_int > lowerdev_mtu: self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' - %(ifaceobj.name, mtu, lowerobj[0].name, lowerdev_mtu)) + %(ifaceobj.name, mtu_str, lowerobj[0].name, lowerdev_mtu)) retval = False elif (not lowerobj[0].link_kind and not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and - not lowerdev_mtu and self.default_mtu and - (int(mtu) > int(self.default_mtu))): + not lowerdev_mtu and self.default_mtu and (mtu_int > self.default_mtu_int)): # only check default mtu on lower device which is a physical interface self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' - %(ifaceobj.name, mtu, lowerobj[0].name, self.default_mtu)) + %(ifaceobj.name, mtu_str, lowerobj[0].name, self.default_mtu)) retval = False - if self.max_mtu and mtu > self.max_mtu: + if self.max_mtu and mtu_int > self.max_mtu: self.logger.warn('%s: specified mtu %s is greater than max mtu %s' - %(ifaceobj.name, mtu, self.max_mtu)) + %(ifaceobj.name, mtu_str, self.max_mtu)) retval = False return retval - def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc): + def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc): if (not ifaceobj.upperifaces or (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or @@ -596,53 +691,48 @@ def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc): # only adjust mtu for vlan devices on ifaceobj umtu = upperobjs[0].get_attr_value_first('mtu') if not umtu: - running_mtu = self.ipcmd.link_get_mtu(upperobjs[0].name) - if not running_mtu or (running_mtu != mtu): - self.ipcmd.link_set(u, 'mtu', mtu) + running_mtu = self.cache.get_link_mtu(upperobjs[0].name) + if not running_mtu or running_mtu != mtu_int: + self.sysfs.link_set_mtu(u, mtu_str=mtu_str, mtu_int=mtu_int) - def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc, mtu): - if mtu: - if not self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc): - return - cached_running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) - running_mtu = self.ipcmd.link_get_mtu_sysfs(ifaceobj.name) - if not running_mtu or (running_mtu and running_mtu != mtu): - force = cached_running_mtu != running_mtu - self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu, force=force) - if (not ifupdownflags.flags.ALL and + def _process_mtu_config_mtu_valid(self, ifaceobj, ifaceobj_getfunc, mtu_str, mtu_int): + if not self._check_mtu_config(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc): + return + + if mtu_int != self.cache.get_link_mtu(ifaceobj.name): + self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=mtu_str, mtu_int=mtu_int) + + if (not ifupdownflags.flags.ALL and not ifaceobj.link_kind and ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'): - # This is additional cost to us, so do it only when - # ifupdown2 is called on a particular interface and - # it is a physical interface - self._propagate_mtu_to_upper_devs(ifaceobj, mtu, ifaceobj_getfunc) - return + # This is additional cost to us, so do it only when + # ifupdown2 is called on a particular interface and + # it is a physical interface + self._propagate_mtu_to_upper_devs(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc) + return + + def _process_mtu_config_mtu_none(self, ifaceobj): + cached_link_mtu = self.cache.get_link_mtu(ifaceobj.name) if ifaceobj.link_kind: # bonds, vxlan and custom devices (like dummy) need an explicit set of mtu. # bridges don't need mtu set - if (ifaceobj.link_kind & ifaceLinkKind.BOND or - ifaceobj.link_kind & ifaceLinkKind.VXLAN or - ifaceobj.link_kind & ifaceLinkKind.OTHER - ): - running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) - if (self.default_mtu and running_mtu != self.default_mtu): - self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) + if ifaceobj.link_kind & ifaceLinkKind.BOND \ + or ifaceobj.link_kind & ifaceLinkKind.VXLAN \ + or ifaceobj.link_kind & ifaceLinkKind.OTHER: + if cached_link_mtu != self.default_mtu_int: + self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int) return + if (ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0' and ifaceobj.lowerifaces): # set vlan interface mtu to lower device mtu if (ifaceobj.link_kind & ifaceLinkKind.VLAN): lower_iface = ifaceobj.lowerifaces[0] - if lower_iface not in self.lower_iface_mtu_checked_list: - lower_iface_mtu = self.ipcmd.link_get_mtu_sysfs(lower_iface) - self.ipcmd.cache_update([lower_iface, 'mtu'], lower_iface_mtu) - self.lower_iface_mtu_checked_list.append(lower_iface) - else: - lower_iface_mtu = self.ipcmd.link_get_mtu(lower_iface) + lower_iface_mtu_int = self.cache.get_link_mtu(lower_iface) - if lower_iface_mtu != self.ipcmd.link_get_mtu_sysfs(ifaceobj.name): - self.ipcmd.link_set_mtu(ifaceobj.name, lower_iface_mtu) + if lower_iface_mtu_int != cached_link_mtu: + self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=str(lower_iface_mtu_int), mtu_int=lower_iface_mtu_int) elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and @@ -655,28 +745,28 @@ def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc, mtu): # config by the kernel in play, we try to be cautious here # on which devices we want to reset mtu to default. # essentially only physical interfaces which are not bond slaves - running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) - if running_mtu != self.default_mtu: - self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) + if cached_link_mtu != self.default_mtu_int: + self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int) def _set_bridge_forwarding(self, ifaceobj): """ set ip forwarding to 0 if bridge interface does not have a ip nor svi """ ifname = ifaceobj.name + + netconf_ipv4_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET, ifname) + netconf_ipv6_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET6, ifname) + if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address'): - if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '1': + if netconf_ipv4_forwarding: self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0) - if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '1': + if netconf_ipv6_forwarding: self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 0) else: - if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '0': + if not netconf_ipv4_forwarding: self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 1) - if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '0': + if not netconf_ipv6_forwarding: self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 1) - def sysctl_get_forwarding_value_from_proc(self, ifname, family): - return self.read_file_oneline("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname)) - def sysctl_write_forwarding_value_to_proc(self, ifname, family, value): self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value) @@ -692,10 +782,7 @@ def _sysctl_config(self, ifaceobj): if ifupdownflags.flags.PERFMODE: running_mpls_enable = '0' else: - running_mpls_enable = self.read_file_oneline( - '/proc/sys/net/mpls/conf/%s/input' - % ifaceobj.name - ) + running_mpls_enable = str(self.cache.get_netconf_mpls_input(ifaceobj.name)) if mpls_enable != running_mpls_enable: try: @@ -733,73 +820,65 @@ def _sysctl_config(self, ifaceobj): self.log_error('%s: \'ip6-forward\' is not supported for ' 'bridge port' %ifaceobj.name) return - setting_default_value = False if not ipforward: setting_default_value = True - ipforward = self.ipforward - - if ipforward: - ipforward = utils.boolean_support_binary(ipforward) - # File read has been used for better performance - # instead of using sysctl command - running_ipforward = self.read_file_oneline( - '/proc/sys/net/ipv4/conf/%s/forwarding' - %ifaceobj.name) - - if ipforward != running_ipforward: - try: - - self.sysctl_set('net.ipv4.conf.%s.forwarding' - %('/'.join(ifaceobj.name.split("."))), - ipforward) - except Exception as e: - if not setting_default_value: - ifaceobj.status = ifaceStatus.ERROR - self.logger.error('%s: %s' %(ifaceobj.name, str(e))) - + ipforward = (self.ipforward or + self.get_mod_subattr('ip-forward', 'default')) + ipforward = int(utils.get_boolean_from_string(ipforward)) + running_ipforward = self.cache.get_netconf_forwarding(socket.AF_INET, ifaceobj.name) + if ipforward != running_ipforward: + try: + self.sysctl_set('net.ipv4.conf.%s.forwarding' + %('/'.join(ifaceobj.name.split("."))), + ipforward) + except Exception as e: + if not setting_default_value: + ifaceobj.status = ifaceStatus.ERROR + self.logger.error('%s: %s' %(ifaceobj.name, str(e))) setting_default_value = False if not ip6forward: setting_default_value = True - ip6forward = self.ip6forward - - if ip6forward: - ip6forward = utils.boolean_support_binary(ip6forward) - # File read has been used for better performance - # instead of using sysctl command - running_ip6forward = self.read_file_oneline( - '/proc/sys/net/ipv6/conf/%s/forwarding' - %ifaceobj.name) - if ip6forward != running_ip6forward: - try: - self.sysctl_set('net.ipv6.conf.%s.forwarding' - %('/'.join(ifaceobj.name.split("."))), - ip6forward) - except Exception as e: - # There is chance of ipv6 being removed because of, - # for example, setting mtu < 1280 - # In such cases, log error only if user has configured - # ip6-forward - if not setting_default_value: - ifaceobj.status = ifaceStatus.ERROR - self.logger.error('%s: %s' %(ifaceobj.name, str(e))) + ip6forward = (self.ip6forward or + self.get_mod_subattr('ip6-forward', 'default')) + ip6forward = int(utils.get_boolean_from_string(ip6forward)) + running_ip6forward = self.cache.get_netconf_forwarding(socket.AF_INET6, ifaceobj.name) + if ip6forward != running_ip6forward: + try: + self.sysctl_set('net.ipv6.conf.%s.forwarding' + %('/'.join(ifaceobj.name.split("."))), + ip6forward) + except Exception as e: + # There is chance of ipv6 being removed because of, + # for example, setting mtu < 1280 + # In such cases, log error only if user has configured + # ip6-forward + if not setting_default_value: + ifaceobj.status = ifaceStatus.ERROR + self.logger.error('%s: %s' %(ifaceobj.name, str(e))) def process_mtu(self, ifaceobj, ifaceobj_getfunc): - mtu = ifaceobj.get_attr_value_first('mtu') + mtu_str = ifaceobj.get_attr_value_first('mtu') + mtu_from_policy = False - if not mtu: - default_iface_mtu = self.ifaces_defaults.get(ifaceobj.name, {}).get('mtu') + if not mtu_str: + mtu_str = self.ifaces_defaults.get(ifaceobj.name, {}).get('mtu') + mtu_from_policy = True - if default_iface_mtu: - try: - mtu = default_iface_mtu - int(default_iface_mtu) - except Exception as e: - self.logger.warning('%s: MTU value from policy file: %s' % (ifaceobj.name, str(e))) - return + if mtu_str: + try: + mtu_int = int(mtu_str) + except Exception as e: + if mtu_from_policy: + self.logger.warning("%s: invalid MTU value from policy file (iface_defaults): %s" % (ifaceobj.name, str(e))) + else: + self.logger.warning("%s: invalid MTU value: %s" % (ifaceobj.name, str(e))) + return - self._process_mtu_config(ifaceobj, ifaceobj_getfunc, mtu) + self._process_mtu_config_mtu_valid(ifaceobj, ifaceobj_getfunc, mtu_str, mtu_int) + else: + self._process_mtu_config_mtu_none(ifaceobj) def up_ipv6_addrgen(self, ifaceobj): user_configured_ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') @@ -808,7 +887,7 @@ def up_ipv6_addrgen(self, ifaceobj): # no need to go further during perfmode (boot) return - if not user_configured_ipv6_addrgen and ifaceobj.addr_method == 'dhcp': + if not user_configured_ipv6_addrgen and ifaceobj.addr_method in ["dhcp", "ppp"]: return if not user_configured_ipv6_addrgen: @@ -825,24 +904,24 @@ def up_ipv6_addrgen(self, ifaceobj): }.get(user_configured_ipv6_addrgen.lower(), None) if ipv6_addrgen_nl is not None: - self.ipcmd.ipv6_addrgen(ifaceobj.name, ipv6_addrgen_nl, link_created=True) + self.iproute2.batch_start() + self.iproute2.link_set_ipv6_addrgen(ifaceobj.name, ipv6_addrgen_nl, link_created=True) + self.iproute2.batch_commit() # link_create=False will flush the addr cache of that intf else: self.logger.warning('%s: invalid value "%s" for attribute ipv6-addrgen' % (ifaceobj.name, user_configured_ipv6_addrgen)) - def _up(self, ifaceobj, ifaceobj_getfunc=None): - if not self.ipcmd.link_exists(ifaceobj.name): + def _pre_up(self, ifaceobj, ifaceobj_getfunc=None): + if not self.cache.link_exists(ifaceobj.name): return if not self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc): return - alias = ifaceobj.get_attr_value_first('alias') - current_alias = self.ipcmd.link_get_alias(ifaceobj.name) - if alias and alias != current_alias: - self.ipcmd.link_set_alias(ifaceobj.name, alias) - elif not alias and current_alias: - self.ipcmd.link_set_alias(ifaceobj.name, '') + # + # alias + # + self.sysfs.link_set_alias(ifaceobj.name, ifaceobj.get_attr_value_first("alias")) self._sysctl_config(ifaceobj) @@ -850,7 +929,7 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): force_reapply = False try: # release any stale dhcp addresses if present - if (addr_method not in ["dhcp", "ppp"] and not ifupdownflags.flags.PERFMODE and + if (addr_method not in ["dhcp", "ppp"] and not ifupdownflags.flags.PERFMODE and not (ifaceobj.flags & iface.HAS_SIBLINGS)): # if not running in perf mode and ifaceobj does not have # any sibling iface objects, kill any stale dhclient @@ -859,100 +938,122 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): if dhclientcmd.is_running(ifaceobj.name): # release any dhcp leases dhclientcmd.release(ifaceobj.name) + self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET) force_reapply = True elif dhclientcmd.is_running6(ifaceobj.name): dhclientcmd.release6(ifaceobj.name) + self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6) force_reapply = True except: pass - self.ipcmd.batch_start() self.up_ipv6_addrgen(ifaceobj) if addr_method not in ["dhcp", "ppp"]: - self._inet_address_config(ifaceobj, ifaceobj_getfunc, - force_reapply) + self.process_addresses(ifaceobj, ifaceobj_getfunc, force_reapply) else: # remove old addresses added by ifupdown2 # (if intf was moved from static config to dhcp) for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: for addr in old_ifaceobj.get_attr_value("address") or []: - self.ipcmd.addr_del(ifaceobj.name, addr) + self.netlink.addr_del(ifaceobj.name, addr) self.process_mtu(ifaceobj, ifaceobj_getfunc) try: - self.ipcmd.batch_commit() - except Exception as e: - self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False) + self.process_hwaddress(ifaceobj) - self.up_hwaddress(ifaceobj) + # Handle special things on a bridge + self._process_bridge(ifaceobj, True) + except Exception, e: + self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) + def _up(self, ifaceobj, ifaceobj_getfunc=None): gateways = ifaceobj.get_attr_value('gateway') if not gateways: gateways = [] prev_gw = self._get_prev_gateway(ifaceobj, gateways) self._add_delete_gateway(ifaceobj, gateways, prev_gw) - def up_hwaddress(self, ifaceobj): - try: - hwaddress = self._get_hwaddress(ifaceobj) + def process_hwaddress(self, ifaceobj): + hwaddress = self._get_hwaddress(ifaceobj) - if hwaddress: - if not ifupdownflags.flags.PERFMODE: # system is clean - running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) - else: - running_hwaddress = None - - if self.ipcmd.mac_str_to_int(running_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress): - slave_down = False - netlink.link_set_updown(ifaceobj.name, "down") - if ifaceobj.link_kind & ifaceLinkKind.BOND: - # if bond, down all the slaves - if ifaceobj.lowerifaces: - for l in ifaceobj.lowerifaces: - netlink.link_set_updown(l, "down") - slave_down = True - try: - self.ipcmd.link_set_hwaddress(ifaceobj.name, hwaddress, force=True) - finally: - netlink.link_set_updown(ifaceobj.name, "up") - if slave_down: - for l in ifaceobj.lowerifaces: - netlink.link_set_updown(l, "up") + if not hwaddress: + if ifaceobj.link_kind & ifaceLinkKind.VLAN: + # When hwaddress is removed from vlan config + # we should go back to system or bridge mac + for lower in ifaceobj.lowerifaces: + if self.cache.get_link_kind(lower) == "bridge": + hwaddress = self.cache.get_link_address(lower) + break + if not hwaddress: + return + else: + return - # Handle special things on a bridge - self._process_bridge(ifaceobj, True) - except Exception, e: - self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) + if not ifupdownflags.flags.PERFMODE: # system is clean + running_hwaddress = self.cache.get_link_address(ifaceobj.name) + else: + running_hwaddress = None + + if utils.mac_str_to_int(hwaddress) != utils.mac_str_to_int(running_hwaddress): + slave_down = False + if ifaceobj.link_kind & ifaceLinkKind.BOND: + # if bond, down all the slaves + if ifaceobj.lowerifaces: + for l in ifaceobj.lowerifaces: + self.netlink.link_down(l) + slave_down = True + try: + self.netlink.link_set_address(ifaceobj.name, hwaddress) + finally: + if slave_down: + for l in ifaceobj.lowerifaces: + self.netlink.link_up(l) def _down(self, ifaceobj, ifaceobj_getfunc=None): try: - if not self.ipcmd.link_exists(ifaceobj.name): + if not self.cache.link_exists(ifaceobj.name): return addr_method = ifaceobj.addr_method if addr_method not in ["dhcp", "ppp"]: if ifaceobj.get_attr_value_first('address-purge')=='no': addrlist = ifaceobj.get_attr_value('address') - for addr in addrlist: - self.ipcmd.addr_del(ifaceobj.name, addr) - #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0]) + for addr in addrlist or []: + self.netlink.addr_del(ifaceobj.name, addr) elif not ifaceobj.link_kind: # for logical interfaces we don't need to remove the ip addresses # kernel will do it for us on 'ip link del' - self.ipcmd.del_addr_all(ifaceobj.name) + if ifaceobj_getfunc: + ifaceobj_list = ifaceobj_getfunc(ifaceobj.name) or [ifaceobj] + else: + ifaceobj_list = [ifaceobj] + + for addr in self.cache.get_ifupdown2_addresses_list(ifaceobj_list, ifaceobj.name): + self.netlink.addr_del(ifaceobj.name, addr) + gateways = ifaceobj.get_attr_value('gateway') if gateways: self._delete_gateway(ifaceobj, gateways, ifaceobj.get_attr_value_first('vrf'), ifaceobj.get_attr_value_first('metric')) - mtu = ifaceobj.get_attr_value_first('mtu') - if (not ifaceobj.link_kind and mtu and - self.default_mtu and (mtu != self.default_mtu)): - self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) - alias = ifaceobj.get_attr_value_first('alias') - if alias: - self.write_file('/sys/class/net/%s/ifalias' % ifaceobj.name, '\n') + + # + # mtu -- + # If device is not a logical intf and has its MTU configured by + # ifupdown2. If this MTU is different from our default mtu, + # if so we need to reset it back to default. + if not ifaceobj.link_kind and self.default_mtu and ifaceobj.get_attr_value_first('mtu') and self.cache.get_link_mtu(ifaceobj.name) != self.default_mtu_int: + self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int) + + # + # alias + # only reset alias on non-logical device + if not ifaceobj.link_kind: + alias = ifaceobj.get_attr_value_first("alias") + if alias: + self.sysfs.link_set_alias(ifaceobj.name, None) # None to reset alias. + # XXX hwaddress reset cannot happen because we dont know last # address. @@ -980,7 +1081,7 @@ def _get_iface_addresses(self, ifaceobj): 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 @@ -992,11 +1093,11 @@ def _check_addresses_in_bridge(self, ifaceobj, hwaddress): if ifaceobj.link_kind & ifaceLinkKind.VLAN: bridgename = ifaceobj.lowerifaces[0] vlan = self._get_vlan_id(ifaceobj) - if self.ipcmd.bridge_is_vlan_aware(bridgename): - fdb_addrs = [self.ipcmd.mac_str_to_int(fdb_addr) for fdb_addr in self._get_bridge_fdbs(bridgename, str(vlan))] + if self.cache.bridge_is_vlan_aware(bridgename): + fdb_addrs = [utils.mac_str_to_int(fdb_addr) for fdb_addr in self._get_bridge_fdbs(bridgename, str(vlan))] if not fdb_addrs: - return False - hwaddress_int = self.ipcmd.mac_str_to_int(hwaddress) + return False + hwaddress_int = utils.mac_str_to_int(hwaddress) if hwaddress_int not in fdb_addrs: return False return True @@ -1011,10 +1112,7 @@ def _query_sysctl(self, ifaceobj, ifaceobjcurr): 'for bridge port') ifaceobjcurr.update_config_with_status('ip-forward', 1, None) else: - running_ipforward = self.read_file_oneline( - '/proc/sys/net/ipv4/conf/%s/forwarding' - %ifaceobj.name) - running_ipforward = utils.get_boolean_from_string(running_ipforward) + running_ipforward = self.cache.get_netconf_forwarding(socket.AF_INET, ifaceobj.name) config_ipforward = utils.get_boolean_from_string(ipforward) ifaceobjcurr.update_config_with_status( 'ip-forward', @@ -1030,10 +1128,7 @@ def _query_sysctl(self, ifaceobj, ifaceobjcurr): 'for bridge port') ifaceobjcurr.update_config_with_status('ip6-forward', 1, None) else: - running_ip6forward = self.read_file_oneline( - '/proc/sys/net/ipv6/conf/%s/forwarding' - %ifaceobj.name) - running_ip6forward = utils.get_boolean_from_string(running_ip6forward) + running_ip6forward = self.cache.get_netconf_forwarding(socket.AF_INET6, ifaceobj.name) config_ip6forward = utils.get_boolean_from_string(ip6forward) ifaceobjcurr.update_config_with_status( 'ip6-forward', @@ -1042,11 +1137,7 @@ def _query_sysctl(self, ifaceobj, ifaceobjcurr): ) mpls_enable = ifaceobj.get_attr_value_first('mpls-enable'); if mpls_enable: - running_mpls_enable = self.read_file_oneline( - '/proc/sys/net/mpls/conf/%s/input' - %ifaceobj.name) - running_mpls_enable = utils.get_yesno_from_onezero( - running_mpls_enable) + running_mpls_enable = utils.get_yesno_from_onezero(str(self.cache.get_netconf_mpls_input(ifaceobj.name))) ifaceobjcurr.update_config_with_status('mpls-enable', running_mpls_enable, mpls_enable != running_mpls_enable) @@ -1062,14 +1153,17 @@ def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr): ifaceobjcurr.update_config_with_status( 'ipv6-addrgen', ipv6_addrgen, - utils.get_boolean_from_string(ipv6_addrgen) == self.ipcmd.get_ipv6_addrgen_mode(ifaceobj.name) + utils.get_boolean_from_string(ipv6_addrgen) == self.cache.get_link_ipv6_addrgen_mode(ifaceobj.name) ) else: ifaceobjcurr.update_config_with_status('ipv6-addrgen', ipv6_addrgen, 1) def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): + """ + TODO: Check broadcast address, scope, etc + """ runningaddrsdict = None - if not self.ipcmd.link_exists(ifaceobj.name): + if not self.cache.link_exists(ifaceobj.name): self.logger.debug('iface %s not found' %ifaceobj.name) return @@ -1077,11 +1171,11 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): addr_method = ifaceobj.addr_method self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, - 'mtu', self.ipcmd.link_get_mtu) + 'mtu', self.cache.get_link_mtu_str) hwaddress = self._get_hwaddress(ifaceobj) if hwaddress: - rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) - if not rhwaddress or self.ipcmd.mac_str_to_int(rhwaddress) != self.ipcmd.mac_str_to_int(hwaddress): + rhwaddress = self.cache.get_link_address(ifaceobj.name) + if not rhwaddress or utils.mac_str_to_int(rhwaddress) != utils.mac_str_to_int(hwaddress): ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, 1) elif not self._check_addresses_in_bridge(ifaceobj, hwaddress): @@ -1093,69 +1187,71 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, 0) self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, - 'alias', self.ipcmd.link_get_alias) + 'alias', self.cache.get_link_alias) self._query_sysctl(ifaceobj, ifaceobjcurr) # compare addresses if addr_method in ["dhcp", "ppp"]: return - addrs = utils.get_normalized_ip_addr(ifaceobj.name, - self._get_iface_addresses(ifaceobj)) - runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobj) - # if anycast address is configured on 'lo' and is in running config - # add it to addrs so that query_check doesn't fail - anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')) - if anycast_addr: - anycast_addr = anycast_addr+'/32' - if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr): - addrs.append(anycast_addr) + + if ifaceobj_getfunc: + ifaceobj_list = ifaceobj_getfunc(ifaceobj.name) + else: + ifaceobj_list = [ifaceobj] + + intf_running_addrs = self.cache.get_ifupdown2_addresses_list(ifaceobj_list, ifaceobj.name) + user_config_addrs = self.cache.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format([ifaceobj], details=False) + + try: + clagd_vxlan_anycast_ip = IPNetwork(ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')) + + if clagd_vxlan_anycast_ip in intf_running_addrs: + user_config_addrs.append(clagd_vxlan_anycast_ip) + except: + pass # Set ifaceobjcurr method and family ifaceobjcurr.addr_method = ifaceobj.addr_method ifaceobjcurr.addr_family = ifaceobj.addr_family - if not runningaddrsdict and not addrs: + + if not intf_running_addrs and not user_config_addrs: + # The device doesn't have any ips configured and the + # the user didn't specify any ip in the configuration file return - runningaddrs = runningaddrsdict.keys() if runningaddrsdict else [] - # Add /32 netmask to configured address without netmask. - # This may happen on interfaces where pointopoint is used. - runningaddrs = [ addr if '/' in addr else addr + '/32' for addr in runningaddrs] - if runningaddrs != addrs: - runningaddrsset = set(runningaddrs) if runningaddrs else set([]) - addrsset = set(addrs) if addrs else set([]) - if (ifaceobj.flags & iface.HAS_SIBLINGS): - if not addrsset: - return - # only check for addresses present in running config - addrsdiff = addrsset.difference(runningaddrsset) - for addr in addrs: - if addr in addrsdiff: - ifaceobjcurr.update_config_with_status('address', - addr, 1) - else: - ifaceobjcurr.update_config_with_status('address', - addr, 0) - else: - addrsdiff = addrsset.symmetric_difference(runningaddrsset) - for addr in addrsset.union(runningaddrsset): - if addr in addrsdiff: - ifaceobjcurr.update_config_with_status('address', - addr, 1) - else: - ifaceobjcurr.update_config_with_status('address', - addr, 0) - elif addrs: - [ifaceobjcurr.update_config_with_status('address', - addr, 0) for addr in addrs] - #XXXX Check broadcast address, scope, etc + + for address in user_config_addrs: + ifaceobjcurr.update_config_with_status('address', str(address), address not in intf_running_addrs) + try: + intf_running_addrs.remove(address) + except: + pass + + # if any ip address is left in 'intf_running_addrs' it means + # that they used to be configured by ifupdown2 but not anymore + # but are still on the intf, so we need to mark them as fail + # we will only mark them as failure on the first sibling + if ifaceobj.flags & iface.HAS_SIBLINGS: + if not ifaceobj.flags & iface.YOUNGEST_SIBLING: + return + + all_stanza_user_config_ip = self.cache.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format( + ifaceobj_list, + details=False + ) + + for address in intf_running_addrs: + if address not in all_stanza_user_config_ip: + ifaceobjcurr.update_config_with_status('address', str(address), 1) + return def query_running_ipv6_addrgen(self, ifaceobjrunning): - ipv6_addrgen = self.ipcmd.get_ipv6_addrgen_mode(ifaceobjrunning.name) + ipv6_addrgen = self.cache.get_link_ipv6_addrgen_mode(ifaceobjrunning.name) if ipv6_addrgen: ifaceobjrunning.update_config('ipv6-addrgen', 'off') def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): - if not self.ipcmd.link_exists(ifaceobjrunning.name): + if not self.cache.link_exists(ifaceobjrunning.name): self.logger.debug('iface %s not found' %ifaceobjrunning.name) return @@ -1166,46 +1262,44 @@ def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): dhclientcmd.is_running6(ifaceobjrunning.name)): # If dhcp is configured on the interface, we skip it return - isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name) - if isloopback: - default_addrs = ['127.0.0.1/8', '::1/128'] + + intf_running_addrs = self.cache.get_addresses_list(ifaceobjrunning.name) or [] + + if self.cache.link_is_loopback(ifaceobjrunning.name): + for default_addr in self.default_loopback_addresses: + try: + intf_running_addrs.remove(default_addr) + except: + pass ifaceobjrunning.addr_family.append('inet') ifaceobjrunning.addr_method = 'loopback' - else: - default_addrs = [] - runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobjrunning) - if runningaddrsdict: - [ifaceobjrunning.update_config('address', addr) - for addr, addrattrs in runningaddrsdict.items() - if addr not in default_addrs] - mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name) + + for addr in intf_running_addrs: + ifaceobjrunning.update_config('address', str(addr)) + + mtu = self.cache.get_link_mtu_str(ifaceobjrunning.name) if (mtu and (ifaceobjrunning.name == 'lo' and mtu != '16436') or (ifaceobjrunning.name != 'lo' and mtu != self.get_mod_subattr('mtu', 'default'))): ifaceobjrunning.update_config('mtu', mtu) - alias = self.ipcmd.link_get_alias(ifaceobjrunning.name) + + alias = self.cache.get_link_alias(ifaceobjrunning.name) if alias: ifaceobjrunning.update_config('alias', alias) - ipforward = self.read_file_oneline( - '/proc/sys/net/ipv4/conf/%s/forwarding' - %ifaceobjrunning.name) - - - _run_ops = {'up' : _up, - 'down' : _down, - 'query-checkcurr' : _query_check, - 'query-running' : _query_running } + _run_ops = { + 'pre-up': _pre_up, + '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): """ run address configuration on the interface object passed as argument @@ -1227,7 +1321,6 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): 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, ifaceobj_getfunc=ifaceobj_getfunc) diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index be16c811..f15d5c2a 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -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 @@ 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 @@ 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' : ['',], - '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": ["", ], + "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 @@ def __init__(self, *args, **kargs): 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 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): 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 @@ def _remove_addresses_from_bridge(self, ifaceobj, hwaddress): 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 @@ def _remove_addresses_from_bridge(self, ifaceobj, hwaddress): 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 @@ def _check_addresses_in_bridge(self, ifaceobj, hwaddress): 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 @@ def _fix_connected_route(self, ifaceobj, vifacename, addr): 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 @@ def get_addressvirtual_ipv6_addrgen_user_conf(self, ifaceobj): 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 @@ def _remove_running_address_config(self, ifaceobj): ]: 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 @@ def _remove_address_config(self, ifaceobj, address_virtual_list=None): 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 @@ def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None): (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 @@ def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None): # 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 @@ def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False }, ] """ + 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 @@ def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False 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 @@ def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False "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 @@ def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False 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 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): "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 @@ def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=F 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 @@ def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=F 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 @@ def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=F 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 @@ def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=F 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 @@ def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_li 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 @@ def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_li 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 @@ def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_li 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 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): #### 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 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr): 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 @@ def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_con 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 @@ def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_con 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 @@ def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_con 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 @@ def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_con 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 @@ def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): 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 @@ def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, 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: diff --git a/ifupdown2/addons/batman_adv.py b/ifupdown2/addons/batman_adv.py index 23ec0e12..13a9d7af 100644 --- a/ifupdown2/addons/batman_adv.py +++ b/ifupdown2/addons/batman_adv.py @@ -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 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' : [ '' ], - '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': [''], + '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' : [ '' ], - 'required' : False, - 'batman-attr' : True, + 'batman-hop-penalty': { + 'help': 'B.A.T.M.A.N. hop penalty', + 'validvals': [''], + '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 @@ def __init__ (self, *args, **kargs): '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 @@ def _get_batman_attr (self, ifaceobj, attr): 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 @@ def _read_current_batman_attr (self, ifaceobj, attr, dont_map = False): 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 @@ def _set_batman_attr (self, ifaceobj, attr, value): 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 @@ def _set_routing_algo (self, routing_algo): 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 @@ def _find_member_ifaces (self, ifaceobj, ignore = True): 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 @@ def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None): return [None] - def _up (self, ifaceobj): if self._get_batman_ifaces (ifaceobj) == None: raise Exception ('could not determine batman interfacaes') @@ -228,7 +211,7 @@ def _up (self, ifaceobj): # 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 @@ def _up (self, ifaceobj): 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 @@ def _up (self, ifaceobj): 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 @@ def _down (self, ifaceobj): 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 @@ def _query_check (self, ifaceobj, ifaceobjcurr): 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 @@ def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args): 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: diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index 72096b36..641b520a 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -11,136 +11,162 @@ 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': ['', ], - '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': ['', ], - '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': [''], - '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": ["", ], + "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": ["", ], + "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": [""], + "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 @@ def __init__(self, *args, **kargs): 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 @@ def get_bond_slaves(ifaceobj): 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 @@ def syntax_check(self, ifaceobj, ifaceobj_getfunc): 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 @@ def enable_ipv6_if_prev_brport(self, ifname): 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 @@ def _is_clag_bond(self, ifaceobj): 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 @@ def _add_slaves(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def check_updown_delay_nl(self, link_exists, ifaceobj, ifla_info_data): """ 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 @@ def check_updown_delay_nl(self, link_exists, ifaceobj, ifla_info_data): 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 @@ def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data): 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 @@ def get_ifla_bond_attr_from_user_config(self, ifaceobj, link_exists): 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 @@ def _should_down_bond(self, ifla_info_data): 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 @@ def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data): 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 @@ def create_or_set_bond_config(self, ifaceobj): # 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 @@ def create_or_set_bond_config(self, ifaceobj): 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 @@ def create_or_set_bond_config(self, ifaceobj): 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 @@ def _query_check_bond_slaves(self, ifaceobjcurr, attr, user_bond_slaves, running 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): # 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): 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 @@ def translate_nl_value_slowfast(value): 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 @@ 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() - 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, 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) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 3e5fd788..00b1eaaf 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -12,6 +12,8 @@ from collections import Counter try: + from ifupdown2.lib.addon import Addon + import ifupdown2.ifupdown.exceptions as exceptions import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags @@ -20,14 +22,12 @@ from ifupdown2.ifupdown.iface import * from ifupdown2.ifupdown.utils import utils - from ifupdown2.ifupdown.netlink import netlink from ifupdown2.ifupdownaddons.cache import * - from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils from ifupdown2.ifupdownaddons.modulebase import moduleBase - - import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig except ImportError: + from lib.addon import Addon + import ifupdown.exceptions as exceptions import ifupdown.policymanager as policymanager import ifupdown.ifupdownflags as ifupdownflags @@ -36,367 +36,430 @@ from ifupdown.iface import * from ifupdown.utils import utils - from ifupdown.netlink import netlink from ifupdownaddons.cache import * - from ifupdownaddons.LinkUtils import LinkUtils from ifupdownaddons.modulebase import moduleBase - import ifupdown.ifupdownconfig as ifupdownconfig - class bridgeFlags: PORT_PROCESSED = 0x1 PORT_PROCESSED_OVERRIDE = 0x2 -class bridge(moduleBase): +class bridge(Addon, moduleBase): """ ifupdown2 addon module to configure linux bridges """ - _modinfo = { 'mhelp' : 'Bridge configuration module. Supports both ' + - 'vlan aware and non vlan aware bridges. For the vlan ' + - 'aware bridge, the port specific attributes must be ' + - 'specified under the port. And for vlan unaware bridge ' + - 'port specific attributes must be specified under the ' + - 'bridge.', - 'attrs' : { - 'bridge-vlan-aware' : - {'help' : 'vlan aware bridge. Setting this ' + - 'attribute to yes enables vlan filtering' + - ' on the bridge', - 'validvals' : ['yes', 'no'], - 'example' : ['bridge-vlan-aware yes/no'], - 'default': 'no' - }, - 'bridge-ports' : - {'help' : 'bridge ports', - 'multivalue' : True, - 'required' : True, - 'validvals': ['', 'none'], - 'example' : ['bridge-ports swp1.100 swp2.100 swp3.100', - 'bridge-ports glob swp1-3.100', - 'bridge-ports regex (swp[1|2|3].100)']}, - 'bridge-ports-condone-regex' : - {'help' : 'bridge ports to ignore/condone when reloading config / removing interfaces', - 'required' : False, - 'example' : [ 'bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$']}, - 'bridge-stp' : - {'help': 'bridge-stp yes/no', - 'example' : ['bridge-stp no'], - 'validvals' : ['yes', 'on', 'off', 'no'], - 'default' : 'no'}, - 'bridge-bridgeprio' : - {'help': 'bridge priority', - 'validrange' : ['0', '65535'], - 'example' : ['bridge-bridgeprio 32768'], - 'default' : '32768'}, - 'bridge-ageing' : - {'help': 'bridge ageing', - 'validrange' : ['0', '65535'], - 'example' : ['bridge-ageing 300'], - 'default' : '300'}, - 'bridge-fd' : - { 'help' : 'bridge forward delay', - 'validrange' : ['0', '255'], - 'example' : ['bridge-fd 15'], - 'default' : '15'}, - 'bridge-gcint' : - # XXX: recheck values - { 'help' : 'bridge garbage collection interval in secs', - 'validrange' : ['0', '255'], - 'example' : ['bridge-gcint 4'], - 'default' : '4', - 'compat' : True, - 'deprecated': True}, - 'bridge-hello' : - { 'help' : 'bridge set hello time', - 'validrange' : ['0', '255'], - 'example' : ['bridge-hello 2'], - 'default' : '2'}, - 'bridge-maxage' : - { 'help' : 'bridge set maxage', - 'validrange' : ['0', '255'], - 'example' : ['bridge-maxage 20'], - 'default' : '20'}, - 'bridge-pathcosts' : - { 'help' : 'bridge set port path costs', - 'validvals': [''], - 'validrange' : ['0', '65535'], - 'example' : ['under the port (for vlan aware bridge): bridge-pathcosts 100', - 'under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100'], - 'default' : '100'}, - 'bridge-portprios' : - { 'help' : 'bridge port prios', - 'validvals': [''], - 'validrange' : ['0', '65535'], - 'example' : ['under the port (for vlan aware bridge): bridge-portprios 32', - 'under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32'], - 'default' : '32'}, - 'bridge-mclmc' : - { 'help' : 'set multicast last member count', - 'validrange' : ['0', '255'], - 'example' : ['bridge-mclmc 2'], - 'default' : '2'}, - 'bridge-mcrouter' : - { 'help': 'Set bridge multicast routers: 0 - disabled - no, 1 - automatic (queried), 2 - permanently enabled - yes', - 'validvals' : ['yes', 'no', '0', '1', '2'], - 'example' : ['bridge-mcrouter 1'], - 'default': 'yes' - }, - 'bridge-mcsnoop' : - { 'help' : 'set multicast snooping', - 'validvals' : ['yes', 'no', '0', '1'], - 'default' : 'yes', - 'example' : ['bridge-mcsnoop yes']}, - 'bridge-mcsqc' : - { 'help' : 'set multicast startup query count', - 'validrange' : ['0', '255'], - 'default' : '2', - 'example' : ['bridge-mcsqc 2']}, - 'bridge-mcqifaddr' : - { 'help' : 'set multicast query to use ifaddr', - 'validvals' : ['yes', 'no', '0', '1'], - 'default' : 'no', - 'example' : ['bridge-mcqifaddr no']}, - 'bridge-mcquerier' : - { 'help' : 'set multicast querier', - 'validvals' : ['yes', 'no', '0', '1'], - 'default' : 'no', - 'example' : ['bridge-mcquerier no']}, - 'bridge-hashel' : - { 'help' : 'set hash elasticity', - 'validrange' : ['0', '4096'], - 'default' : '4', - 'example' : ['bridge-hashel 4096']}, - 'bridge-hashmax' : - { 'help' : 'set hash max', - 'validrange' : ['0', '4096'], - 'default' : '512', - 'example' : ['bridge-hashmax 4096']}, - 'bridge-mclmi' : - { 'help' : 'set multicast last member interval (in secs)', - 'validrange' : ['0', '255'], - 'default' : '1', - 'example' : ['bridge-mclmi 1']}, - 'bridge-mcmi' : - { 'help' : 'set multicast membership interval (in secs)', - 'validrange' : ['0', '255'], - 'default' : '260', - 'example' : ['bridge-mcmi 260']}, - 'bridge-mcqpi' : - { 'help' : 'set multicast querier interval (in secs)', - 'validrange' : ['0', '255'], - 'default' : '255', - 'example' : ['bridge-mcqpi 255']}, - 'bridge-mcqi' : - { 'help' : 'set multicast query interval (in secs)', - 'validrange' : ['0', '255'], - 'default' : '125', - 'example' : ['bridge-mcqi 125']}, - 'bridge-mcqri' : - { 'help' : 'set multicast query response interval (in secs)', - 'validrange' : ['0', '255'], - 'default' : '10', - 'example' : ['bridge-mcqri 10']}, - 'bridge-mcsqi' : - { 'help' : 'set multicast startup query interval (in secs)', - 'validrange' : ['0', '255'], - 'default' : '31', - 'example' : ['bridge-mcsqi 31']}, - 'bridge-mcqv4src' : - { 'help' : 'set per VLAN v4 multicast querier source address', - 'validvals' : ['', ], - 'multivalue' : True, - 'compat' : True, - 'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']}, - 'bridge-portmcrouter': - { - 'help': 'Set port multicast routers: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled', - 'validvals': [''], - 'example': [ - 'under the port (for vlan aware bridge): bridge-portmcrouter 0', - 'under the port (for vlan aware bridge): bridge-portmcrouter 1', - 'under the port (for vlan aware bridge): bridge-portmcrouter 2', - 'under the port (for vlan aware bridge): bridge-portmcrouter disabled', - 'under the port (for vlan aware bridge): bridge-portmcrouter automatic', - 'under the port (for vlan aware bridge): bridge-portmcrouter enabled', - 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2', - 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled', - 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1', - ] - }, - 'bridge-portmcfl' : - { 'help' : 'port multicast fast leave.', - 'validvals': [''], - 'default' : 'no', - 'example' : ['under the port (for vlan aware bridge): bridge-portmcfl no', - 'under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no']}, - 'bridge-waitport' : - { 'help' : 'wait for a max of time secs for the' + - ' specified ports to become available,' + - 'if no ports are specified then those' + - ' specified on bridge-ports will be' + - ' used here. Specifying no ports here ' + - 'should not be used if we are using ' + - 'regex or \"all\" on bridge_ports,' + - 'as it wouldnt work.', - 'default' : '0', - 'validvals': [''], - 'example' : ['bridge-waitport 4 swp1 swp2']}, - 'bridge-maxwait' : - { 'help' : 'forces to time seconds the maximum time ' + - 'that the Debian bridge setup scripts will ' + - 'wait for the bridge ports to get to the ' + - 'forwarding status, doesn\'t allow factional ' + - 'part. If it is equal to 0 then no waiting' + - ' is done', - 'validrange' : ['0', '255'], - 'default' : '0', - 'example' : ['bridge-maxwait 3']}, - 'bridge-vids' : - { 'help' : 'bridge port vids. Can be specified ' + - 'under the bridge or under the port. ' + - 'If specified under the bridge the ports ' + - 'inherit it unless overridden by a ' + - 'bridge-vids attribute under the port', - 'multivalue' : True, - 'validvals': [''], - 'example' : ['bridge-vids 4000', - 'bridge-vids 2000 2200-3000'], - 'aliases': ['bridge-trunk']}, - 'bridge-pvid' : - { 'help' : 'bridge port pvid. Must be specified under' + - ' the bridge port', - 'validrange' : ['0', '4096'], - 'example' : ['bridge-pvid 1']}, - 'bridge-access' : - { 'help' : 'bridge port access vlan. Must be ' + - 'specified under the bridge port', - 'validrange' : ['1', '4094'], - 'example' : ['bridge-access 300']}, - 'bridge-allow-untagged' : - { 'help' : 'indicate if the bridge port accepts ' + - 'untagged packets or not. Must be ' + - 'specified under the bridge port. ' + - 'Default is \'yes\'', - 'validvals' : ['yes', 'no'], - 'example' : ['bridge-allow-untagged yes'], - 'default' : 'yes'}, - 'bridge-port-vids' : - { 'help' : 'bridge vlans', - 'compat': True, - 'example' : ['bridge-port-vids bond0=1-1000,1010-1020']}, - 'bridge-port-pvids' : - { 'help' : 'bridge port vlans', - 'compat': True, - 'example' : ['bridge-port-pvids bond0=100 bond1=200']}, - 'bridge-learning' : - { 'help' : 'bridge port learning flag', - 'validvals': ['on', 'off', ''], - 'default': 'on', - 'example' : ['bridge-learning off']}, - 'bridge-igmp-version' : - { 'help' : 'mcast igmp version', - 'validvals': ['2', '3'], - 'default' : '2', - 'example' : ['bridge-igmp-version 2']}, - 'bridge-mld-version': - { 'help' : 'mcast mld version', - 'validvals': ['1', '2'], - 'default' : '1', - 'example' : ['bridge-mld-version 1']}, - 'bridge-unicast-flood' : - { 'help' : 'bridge port unicast flood flag', - 'validvals': ['on', 'off', ''], - 'default': 'on', - 'example' : ['under the port (for vlan aware bridge): bridge-unicast-flood on', - 'under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on']}, - 'bridge-multicast-flood' : - { 'help' : 'bridge port multicast flood flag', - 'validvals': ['on', 'off', ''], - 'default': 'on', - 'example' : ['under the port (for vlan aware bridge): bridge-multicast-flood on', - 'under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on']}, - 'bridge-vlan-protocol' : - { 'help' : 'bridge vlan protocol', - 'default' : '802.1q', - 'validvals': ['802.1q', '802.1ad'], - 'example' : ['bridge-vlan-protocol 802.1q']}, - 'bridge-vlan-stats' : - { 'help' : 'bridge vlan stats', - 'default' : 'off', - 'validvals': ['on', 'off'], - 'example' : ['bridge-vlan-stats off']}, - 'bridge-arp-nd-suppress' : - { 'help' : 'bridge port arp nd suppress flag', - 'validvals': ['on', 'off', ''], - 'default': 'off', - 'example' : ['under the port (for vlan aware bridge): bridge-arp-nd-suppress on', - 'under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on']}, - 'bridge-mcstats' : - { 'help' : 'bridge multicast stats', - 'default' : 'off', - 'validvals': ['on', 'off'], - 'example' : ['bridge-mcstats off']}, - 'bridge-l2protocol-tunnel': { - 'help': 'layer 2 protocol tunneling', - 'validvals': [ # XXX: lists all combinations, should move to - # a better representation - 'all', - 'cdp', - 'cdp lacp', - 'cdp lacp lldp', - 'cdp lacp lldp pvst', - 'cdp lacp lldp stp', - 'cdp lacp pvst', - 'cdp lacp pvst stp', - 'cdp lacp stp', - 'cdp lldp', - 'cdp lldp pvst', - 'cdp lldp pvst stp', - 'cdp lldp stp', - 'cdp pvst', - 'cdp pvst stp', - 'cdp stp', - 'lacp', - 'lacp lldp', - 'lacp lldp pvst', - 'lacp lldp pvst stp', - 'lacp lldp stp', - 'lacp pvst', - 'lacp pvst stp', - 'lacp stp', - 'lldp', - 'lldp pvst', - 'lldp pvst stp', - 'lldp stp', - 'pvst', - 'pvst stp', - 'stp', - ''], - 'example': [ - 'under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all', - 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst', - 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst', - 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp', - 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel all' - ] - } - }} + _modinfo = { + "mhelp": "Bridge configuration module. Supports both vlan aware and non " + "vlan aware bridges. For the vlan aware bridge, the port " + "specific attributes must be specified under the port. And for " + "vlan unaware bridge port specific attributes must be specified " + "under the bridge.", + "attrs": { + "bridge-vlan-aware": { + "help": "vlan aware bridge. Setting this " + "attribute to yes enables vlan filtering" + " on the bridge", + "validvals": ["yes", "no"], + "example": ["bridge-vlan-aware yes/no"], + "default": "no" + }, + "bridge-ports": { + "help": "bridge ports", + "multivalue": True, + "required": True, + "validvals": [""], + "example": [ + "bridge-ports swp1.100 swp2.100 swp3.100", + "bridge-ports glob swp1-3.100", + "bridge-ports regex (swp[1|2|3].100)" + ] + }, + "bridge-stp": { + "help": "bridge-stp yes/no", + "example": ["bridge-stp no"], + "validvals": ["yes", "on", "off", "no"], + "default": "no" + }, + "bridge-bridgeprio": { + "help": "bridge priority", + "validrange": ["0", "65535"], + "example": ["bridge-bridgeprio 32768"], + "default": "32768" + }, + "bridge-ageing": { + "help": "bridge ageing", + "validrange": ["0", "65535"], + "example": ["bridge-ageing 300"], + "default": "300" + }, + "bridge-fd": { + "help": "bridge forward delay", + "validrange": ["0", "255"], + "example": ["bridge-fd 15"], + "default": "15" + }, + # XXX: recheck values + "bridge-gcint": { + "help": "bridge garbage collection interval in secs", + "validrange": ["0", "255"], + "example": ["bridge-gcint 4"], + "default": "4", + "compat": True, + "deprecated": True + }, + "bridge-hello": { + "help": "bridge set hello time", + "validrange": ["0", "255"], + "example": ["bridge-hello 2"], + "default": "2" + }, + "bridge-maxage": { + "help": "bridge set maxage", + "validrange": ["0", "255"], + "example": ["bridge-maxage 20"], + "default": "20" + }, + "bridge-pathcosts": { + "help": "bridge set port path costs", + "validvals": [""], + "validrange": ["0", "65535"], + "example": [ + "under the port (for vlan aware bridge): bridge-pathcosts 100", + "under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100" + ], + "default": "100" + }, + "bridge-portprios": { + "help": "bridge port prios", + "validvals": [""], + "validrange": ["0", "65535"], + "example": [ + "under the port (for vlan aware bridge): bridge-portprios 32", + "under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32" + ], + }, + "bridge-mclmc": { + "help": "set multicast last member count", + "validrange": ["0", "255"], + "example": ["bridge-mclmc 2"], + "default": "2" + }, + "bridge-mcrouter": { + "help": "Set bridge multicast routers: 0 - disabled - no, 1 - automatic (queried), 2 - permanently enabled - yes", + "validvals": ["yes", "no", "0", "1", "2"], + "example": ["bridge-mcrouter 1"], + "default": "yes" + }, + "bridge-mcsnoop": { + "help": "set multicast snooping", + "validvals": ["yes", "no", "0", "1"], + "default": "yes", + "example": ["bridge-mcsnoop yes"] + }, + "bridge-mcsqc": { + "help": "set multicast startup query count", + "validrange": ["0", "255"], + "default": "2", + "example": ["bridge-mcsqc 2"] + }, + "bridge-mcqifaddr": { + "help": "set multicast query to use ifaddr", + "validvals": ["yes", "no", "0", "1"], + "default": "no", + "example": ["bridge-mcqifaddr no"] + }, + "bridge-mcquerier": { + "help": "set multicast querier", + "validvals": ["yes", "no", "0", "1"], + "default": "no", + "example": ["bridge-mcquerier no"] + }, + "bridge-hashel": { + "help": "set hash elasticity", + "validrange": ["0", "4096"], + "default": "4", + "example": ["bridge-hashel 4096"] + }, + "bridge-hashmax": { + "help": "set hash max", + "validrange": ["0", "4096"], + "default": "512", + "example": ["bridge-hashmax 4096"] + }, + "bridge-mclmi": { + "help": "set multicast last member interval (in secs)", + "validrange": ["0", "255"], + "default": "1", + "example": ["bridge-mclmi 1"] + }, + "bridge-mcmi": { + "help": "set multicast membership interval (in secs)", + "validrange": ["0", "255"], + "default": "260", + "example": ["bridge-mcmi 260"] + }, + "bridge-mcqpi": { + "help": "set multicast querier interval (in secs)", + "validrange": ["0", "255"], + "default": "255", + "example": ["bridge-mcqpi 255"] + }, + "bridge-mcqi": { + "help": "set multicast query interval (in secs)", + "validrange": ["0", "255"], + "default": "125", + "example": ["bridge-mcqi 125"] + }, + "bridge-mcqri": { + "help": "set multicast query response interval (in secs)", + "validrange": ["0", "255"], + "default": "10", + "example": ["bridge-mcqri 10"] + }, + "bridge-mcsqi": { + "help": "set multicast startup query interval (in secs)", + "validrange": ["0", "255"], + "default": "31", + "example": ["bridge-mcsqi 31"] + }, + "bridge-mcqv4src": { + "help": "set per VLAN v4 multicast querier source address", + "validvals": ["", ], + "multivalue": True, + "compat": True, + "example": ["bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1"] + }, + "bridge-portmcrouter": { + "help": "Set port multicast routers: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled", + "validvals": [""], + "example": [ + "under the port (for vlan aware bridge): bridge-portmcrouter 0", + "under the port (for vlan aware bridge): bridge-portmcrouter 1", + "under the port (for vlan aware bridge): bridge-portmcrouter 2", + "under the port (for vlan aware bridge): bridge-portmcrouter disabled", + "under the port (for vlan aware bridge): bridge-portmcrouter automatic", + "under the port (for vlan aware bridge): bridge-portmcrouter enabled", + "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2", + "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled", + "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1", + ] + }, + "bridge-portmcfl": { + "help": "port multicast fast leave.", + "validvals": [""], + "default": "no", + "example": [ + "under the port (for vlan aware bridge): bridge-portmcfl no", + "under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no" + ] + }, + "bridge-waitport": { + "help": "wait for a max of time secs for the" + " specified ports to become available," + "if no ports are specified then those" + " specified on bridge-ports will be" + " used here. Specifying no ports here " + "should not be used if we are using " + "regex or \"all\" on bridge_ports," + "as it wouldnt work.", + "default": "0", + "validvals": [""], + "example": ["bridge-waitport 4 swp1 swp2"] + }, + "bridge-maxwait": { + "help": "forces to time seconds the maximum time " + "that the Debian bridge setup scripts will " + "wait for the bridge ports to get to the " + "forwarding status, doesn\"t allow factional " + "part. If it is equal to 0 then no waiting" + " is done", + "validrange": ["0", "255"], + "default": "0", + "example": ["bridge-maxwait 3"] + }, + "bridge-vids": { + "help": "bridge port vids. Can be specified " + "under the bridge or under the port. " + "If specified under the bridge the ports " + "inherit it unless overridden by a " + "bridge-vids attribute under the port", + "multivalue": True, + "validvals": [""], + "example": [ + "bridge-vids 4000", + "bridge-vids 2000 2200-3000" + ], + "aliases": ["bridge-trunk"] + }, + "bridge-pvid": { + "help": "bridge port pvid. Must be specified under" + " the bridge port", + "validrange": ["0", "4096"], + "example": ["bridge-pvid 1"] + }, + "bridge-access": { + "help": "bridge port access vlan. Must be " + "specified under the bridge port", + "validrange": ["1", "4094"], + "example": ["bridge-access 300"] + }, + "bridge-allow-untagged": { + "help": "indicate if the bridge port accepts " + "untagged packets or not. Must be " + "specified under the bridge port. " + "Default is \"yes\"", + "validvals": ["yes", "no"], + "example": ["bridge-allow-untagged yes"], + "default": "yes" + }, + "bridge-port-vids": { + "help": "bridge vlans", + "compat": True, + "example": ["bridge-port-vids bond0=1-1000,1010-1020"] + }, + "bridge-port-pvids": { + "help": "bridge port vlans", + "compat": True, + "example": ["bridge-port-pvids bond0=100 bond1=200"] + }, + "bridge-learning": { + "help": "bridge port learning flag", + "validvals": ["on", "off", ""], + "default": "on", + "example": ["bridge-learning off"] + }, + "bridge-igmp-version": { + "help": "mcast igmp version", + "validvals": ["2", "3"], + "default": "2", + "example": ["bridge-igmp-version 2"] + }, + "bridge-mld-version": { + "help": "mcast mld version", + "validvals": ["1", "2"], + "default": "1", + "example": ["bridge-mld-version 1"] + }, + "bridge-unicast-flood": { + "help": "bridge port unicast flood flag", + "validvals": ["on", "off", ""], + "default": "on", + "example": ["under the port (for vlan aware bridge): bridge-unicast-flood on", + "under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on"] + }, + "bridge-multicast-flood": { + "help": "bridge port multicast flood flag", + "validvals": ["on", "off", ""], + "default": "on", + "example": [ + "under the port (for vlan aware bridge): bridge-multicast-flood on", + "under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on" + ] + }, + "bridge-broadcast-flood": { + "help": "bridge port broadcast flood flag", + "validvals": ["on", "off", ""], + "default": "on", + "example": [ + "under the port (for vlan aware bridge): bridge-broadcast-flood on", + "under the bridge (for vlan unaware bridge): bridge-broadcast-flood swp1=on swp2=on" + ] + }, + "bridge-vlan-protocol": { + "help": "bridge vlan protocol", + "default": "802.1q", + "validvals": ["802.1q", "802.1ad"], + "example": ["bridge-vlan-protocol 802.1q"] + }, + "bridge-vlan-stats": { + "help": "bridge vlan stats", + "default": "off", + "validvals": ["on", "off"], + "example": ["bridge-vlan-stats off"] + }, + "bridge-arp-nd-suppress": { + "help": "bridge port arp nd suppress flag", + "validvals": ["on", "off", ""], + "default": "off", + "example": [ + "under the port (for vlan aware bridge): bridge-arp-nd-suppress on", + "under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on" + ] + }, + "bridge-mcstats": { + "help": "bridge multicast stats", + "default": "off", + "validvals": ["on", "off", "1", "0", "yes", "no"], + "example": ["bridge-mcstats off"] + }, + "bridge-l2protocol-tunnel": { + "help": "layer 2 protocol tunneling", + "validvals": [ # XXX: lists all combinations, should move to + # a better representation + "all", + "cdp", + "cdp lacp", + "cdp lacp lldp", + "cdp lacp lldp pvst", + "cdp lacp lldp stp", + "cdp lacp pvst", + "cdp lacp pvst stp", + "cdp lacp stp", + "cdp lldp", + "cdp lldp pvst", + "cdp lldp pvst stp", + "cdp lldp stp", + "cdp pvst", + "cdp pvst stp", + "cdp stp", + "lacp", + "lacp lldp", + "lacp lldp pvst", + "lacp lldp pvst stp", + "lacp lldp stp", + "lacp pvst", + "lacp pvst stp", + "lacp stp", + "lldp", + "lldp pvst", + "lldp pvst stp", + "lldp stp", + "pvst", + "pvst stp", + "stp", + ""], + "example": [ + "under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all", + "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst", + "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst", + "under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp", + "under the port (for vlan aware bridge): bridge-l2protocol-tunnel all" + ] + }, + "bridge-ports-condone-regex": { + "help": "bridge ports to ignore/condone when reloading config / removing interfaces", + "required": False, + "example": ["bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$"] + }, + } + } + + bridge_utils_missing_warning = True # Netlink attributes not associated with ifupdown2 # attributes are left commented-out for a future use # and kept in order :) - _ifla_br_attributes_map = ( + _ifla_br_attributes_map = { # Link.IFLA_BR_UNSPEC, - ('bridge-fd', Link.IFLA_BR_FORWARD_DELAY), - ('bridge-hello', Link.IFLA_BR_HELLO_TIME), - ('bridge-maxage', Link.IFLA_BR_MAX_AGE), - ('bridge-ageing', Link.IFLA_BR_AGEING_TIME), - ('bridge-stp', Link.IFLA_BR_STP_STATE), - ('bridge-bridgeprio', Link.IFLA_BR_PRIORITY), - ('bridge-vlan-aware', Link.IFLA_BR_VLAN_FILTERING), - ('bridge-vlan-protocol', Link.IFLA_BR_VLAN_PROTOCOL), + 'bridge-fd': Link.IFLA_BR_FORWARD_DELAY, + 'bridge-hello': Link.IFLA_BR_HELLO_TIME, + 'bridge-maxage': Link.IFLA_BR_MAX_AGE, + 'bridge-ageing': Link.IFLA_BR_AGEING_TIME, + 'bridge-stp': Link.IFLA_BR_STP_STATE, + 'bridge-bridgeprio': Link.IFLA_BR_PRIORITY, + 'bridge-vlan-aware': Link.IFLA_BR_VLAN_FILTERING, + 'bridge-vlan-protocol': Link.IFLA_BR_VLAN_PROTOCOL, # Link.IFLA_BR_GROUP_FWD_MASK, # Link.IFLA_BR_ROOT_ID, # Link.IFLA_BR_BRIDGE_ID, @@ -410,68 +473,35 @@ class bridge(moduleBase): # Link.IFLA_BR_GC_TIMER, # Link.IFLA_BR_GROUP_ADDR, # Link.IFLA_BR_FDB_FLUSH, - ('bridge-mcrouter', Link.IFLA_BR_MCAST_ROUTER), + 'bridge-mcrouter': Link.IFLA_BR_MCAST_ROUTER, #('bridge-mcsnoop', Link.IFLA_BR_MCAST_SNOOPING), # requires special handling so we won't loop on this attr - ('bridge-mcqifaddr', Link.IFLA_BR_MCAST_QUERY_USE_IFADDR), - ('bridge-mcquerier', Link.IFLA_BR_MCAST_QUERIER), - ('bridge-hashel', Link.IFLA_BR_MCAST_HASH_ELASTICITY), - ('bridge-hashmax', Link.IFLA_BR_MCAST_HASH_MAX), - ('bridge-mclmc', Link.IFLA_BR_MCAST_LAST_MEMBER_CNT), - ('bridge-mcsqc', Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT), - ('bridge-mclmi', Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL), - ('bridge-mcmi', Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL), - ('bridge-mcqpi', Link.IFLA_BR_MCAST_QUERIER_INTVL), - ('bridge-mcqi', Link.IFLA_BR_MCAST_QUERY_INTVL), - ('bridge-mcqri', Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL), - ('bridge-mcsqi', Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL), + 'bridge-mcqifaddr': Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, + 'bridge-mcquerier': Link.IFLA_BR_MCAST_QUERIER, + 'bridge-hashel': Link.IFLA_BR_MCAST_HASH_ELASTICITY, + 'bridge-hashmax': Link.IFLA_BR_MCAST_HASH_MAX, + 'bridge-mclmc': Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, + 'bridge-mcsqc': Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, + 'bridge-mclmi': Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, + 'bridge-mcmi': Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, + 'bridge-mcqpi': Link.IFLA_BR_MCAST_QUERIER_INTVL, + 'bridge-mcqi': Link.IFLA_BR_MCAST_QUERY_INTVL, + 'bridge-mcqri': Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + 'bridge-mcsqi': Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, # Link.IFLA_BR_NF_CALL_IPTABLES, # Link.IFLA_BR_NF_CALL_IP6TABLES, # Link.IFLA_BR_NF_CALL_ARPTABLES, # Link.IFLA_BR_VLAN_DEFAULT_PVID, # Link.IFLA_BR_PAD, # (Link.IFLA_BR_VLAN_STATS_ENABLED, 'bridge-vlan-stats'), # already dealt with, in a separate loop - ('bridge-igmp-version', Link.IFLA_BR_MCAST_IGMP_VERSION, ), - ('bridge-mcstats', Link.IFLA_BR_MCAST_STATS_ENABLED), - ('bridge-mld-version', Link.IFLA_BR_MCAST_MLD_VERSION) - ) + 'bridge-igmp-version': Link.IFLA_BR_MCAST_IGMP_VERSION, + 'bridge-mcstats': Link.IFLA_BR_MCAST_STATS_ENABLED, + 'bridge-mld-version': Link.IFLA_BR_MCAST_MLD_VERSION + } # 'bridge-vlan-stats & bridge-mcstat are commented out even though, today # they are supported. It is done this way because this dictionary is used # in a loop, but these attributes require additional work. Thus they are # excluded from this loop without overhead. - # we are still using the old linkCache we need an easy way - # to use this cache with the new full-netlink approach - _ifla_br_attributes_old_cache_key_map = dict( - ( - (Link.IFLA_BR_FORWARD_DELAY, 'fd'), - (Link.IFLA_BR_HELLO_TIME, 'hello'), - (Link.IFLA_BR_MAX_AGE, 'maxage'), - (Link.IFLA_BR_AGEING_TIME, 'ageing'), - (Link.IFLA_BR_STP_STATE, 'stp'), - (Link.IFLA_BR_PRIORITY, 'bridgeprio'), - (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering'), - (Link.IFLA_BR_VLAN_PROTOCOL, 'vlan-protocol'), - (Link.IFLA_BR_MCAST_ROUTER, 'mcrouter'), - (Link.IFLA_BR_MCAST_SNOOPING, 'mcsnoop'), - (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, 'mcqifaddr'), - (Link.IFLA_BR_MCAST_QUERIER, 'mcquerier'), - (Link.IFLA_BR_MCAST_HASH_ELASTICITY, 'hashel'), - (Link.IFLA_BR_MCAST_HASH_MAX, 'hashmax'), - (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, 'mclmc'), - (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, 'mcsqc'), - (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, 'mclmi'), - (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, 'mcmi'), - (Link.IFLA_BR_MCAST_QUERIER_INTVL, 'mcqpi'), - (Link.IFLA_BR_MCAST_QUERY_INTVL, 'mcqi'), - (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, 'mcqri'), - (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, 'mcsqi'), - (Link.IFLA_BR_VLAN_STATS_ENABLED, 'vlan-stats'), - (Link.IFLA_BR_MCAST_STATS_ENABLED, 'mcstats'), - (Link.IFLA_BR_MCAST_IGMP_VERSION, 'igmp-version'), - (Link.IFLA_BR_MCAST_MLD_VERSION, 'mld-version') - ) - ) - _ifla_br_attributes_translate_user_config_to_netlink_map = dict( ( # Link.IFLA_BR_UNSPEC, @@ -482,7 +512,7 @@ class bridge(moduleBase): # Link.IFLA_BR_STP_STATE, # STP is treated outside the loop (Link.IFLA_BR_PRIORITY, int), (Link.IFLA_BR_VLAN_FILTERING, utils.get_boolean_from_string), - (Link.IFLA_BR_VLAN_PROTOCOL, str), + (Link.IFLA_BR_VLAN_PROTOCOL, str.upper), # Link.IFLA_BR_GROUP_FWD_MASK, # Link.IFLA_BR_ROOT_ID, # Link.IFLA_BR_BRIDGE_ID, @@ -522,17 +552,17 @@ class bridge(moduleBase): ) ) - _ifla_brport_attributes_map = ( + _ifla_brport_attributes_map = { # Link.IFLA_BRPORT_UNSPEC, # Link.IFLA_BRPORT_STATE, - ('bridge-portprios', Link.IFLA_BRPORT_PRIORITY), - ('bridge-pathcosts', Link.IFLA_BRPORT_COST), + 'bridge-portprios': Link.IFLA_BRPORT_PRIORITY, + 'bridge-pathcosts': Link.IFLA_BRPORT_COST, # Link.IFLA_BRPORT_MODE, # Link.IFLA_BRPORT_GUARD, # Link.IFLA_BRPORT_PROTECT, - ('bridge-portmcfl', Link.IFLA_BRPORT_FAST_LEAVE), - ('bridge-learning', Link.IFLA_BRPORT_LEARNING), - ('bridge-unicast-flood', Link.IFLA_BRPORT_UNICAST_FLOOD), + 'bridge-portmcfl': Link.IFLA_BRPORT_FAST_LEAVE, + 'bridge-learning': Link.IFLA_BRPORT_LEARNING, + 'bridge-unicast-flood': Link.IFLA_BRPORT_UNICAST_FLOOD, # Link.IFLA_BRPORT_PROXYARP, # Link.IFLA_BRPORT_LEARNING_SYNC, # Link.IFLA_BRPORT_PROXYARP_WIFI, @@ -548,17 +578,17 @@ class bridge(moduleBase): # Link.IFLA_BRPORT_FORWARD_DELAY_TIMER, # Link.IFLA_BRPORT_HOLD_TIMER, # Link.IFLA_BRPORT_FLUSH, - ('bridge-portmcrouter', Link.IFLA_BRPORT_MULTICAST_ROUTER), + 'bridge-portmcrouter': Link.IFLA_BRPORT_MULTICAST_ROUTER, # Link.IFLA_BRPORT_PAD, - ('bridge-multicast-flood', Link.IFLA_BRPORT_MCAST_FLOOD), + 'bridge-multicast-flood': Link.IFLA_BRPORT_MCAST_FLOOD, # Link.IFLA_BRPORT_MCAST_TO_UCAST, # Link.IFLA_BRPORT_VLAN_TUNNEL, - # Link.IFLA_BRPORT_BCAST_FLOOD - ('bridge-l2protocol-tunnel', Link.IFLA_BRPORT_GROUP_FWD_MASK), + 'bridge-broadcast-flood': Link.IFLA_BRPORT_BCAST_FLOOD, + 'bridge-l2protocol-tunnel': Link.IFLA_BRPORT_GROUP_FWD_MASK, # Link.IFLA_BRPORT_PEER_LINK, # Link.IFLA_BRPORT_DUAL_LINK, - ('bridge-arp-nd-suppress', Link.IFLA_BRPORT_ARP_SUPPRESS), - ) + 'bridge-arp-nd-suppress': Link.IFLA_BRPORT_NEIGH_SUPPRESS, + } _ifla_brport_multicast_router_dict_to_int = { 'disabled': 0, @@ -571,6 +601,12 @@ class bridge(moduleBase): '2': 2, } + _ifla_brport_multicast_router_dict_int_to_str = { + 0: "disabled", + 1: "automatic", + 2: "enabled" + } + # callable to translate to netlink value _ifla_brport_attributes_translate_user_config_to_netlink_map = dict( ( @@ -581,18 +617,16 @@ class bridge(moduleBase): (Link.IFLA_BRPORT_LEARNING, utils.get_boolean_from_string), (Link.IFLA_BRPORT_UNICAST_FLOOD, utils.get_boolean_from_string), (Link.IFLA_BRPORT_MCAST_FLOOD, utils.get_boolean_from_string), + (Link.IFLA_BRPORT_BCAST_FLOOD, utils.get_boolean_from_string), (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x), - (Link.IFLA_BRPORT_ARP_SUPPRESS, utils.get_boolean_from_string) + (Link.IFLA_BRPORT_NEIGH_SUPPRESS, utils.get_boolean_from_string) ) ) def __init__(self, *args, **kargs): + Addon.__init__(self) moduleBase.__init__(self, *args, **kargs) - self.ipcmd = None self.name = self.__class__.__name__ - self.brctlcmd = None - self._running_vidinfo = {} - self._running_vidinfo_valid = False self._resv_vlan_range = self._get_reserved_vlan_range() self.logger.debug('%s: using reserved vlan range %s' % (self.__class__.__name__, str(self._resv_vlan_range))) @@ -659,6 +693,15 @@ def __init__(self, *args, **kargs): default=True ) + self.bridge_vxlan_arp_nd_suppress = utils.get_boolean_from_string( + policymanager.policymanager_api.get_module_globals( + module_name=self.__class__.__name__, + attr="bridge-vxlan-arp-nd-suppress" + ), + default=False + ) + self.bridge_vxlan_arp_nd_suppress_int = int(self.bridge_vxlan_arp_nd_suppress) + self.l2protocol_tunnel_callback = { 'all': self._l2protocol_tunnel_set_all, 'stp': self._l2protocol_tunnel_set_stp, @@ -677,6 +720,63 @@ def __init__(self, *args, **kargs): 'lacp': self._query_check_l2protocol_tunnel_lacp } + self._bridge_attribute_query_check_handler = { + "bridge-maxwait": (self._query_check_br_attr_wait, None), + "bridge-waitport": (self._query_check_br_attr_wait, None), + + "bridge-stp": (self._query_check_br_attr_stp, Link.IFLA_BR_STP_STATE), + + "bridge-mcstats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_MCAST_STATS_ENABLED), + "bridge-vlan-stats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_VLAN_STATS_ENABLED), + + "bridge-vlan-aware": (self._query_check_br_attr_boolean, Link.IFLA_BR_VLAN_FILTERING), + "bridge-mcqifaddr": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERY_USE_IFADDR), + "bridge-mcsnoop": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_SNOOPING), + "bridge-mcquerier": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERIER), + "bridge-mcrouter": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_ROUTER), + + "bridge-vlan-protocol": (self._query_check_br_attr_string, Link.IFLA_BR_VLAN_PROTOCOL), + + "bridge-mcsqc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT), + "bridge-mclmc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_LAST_MEMBER_CNT), + "bridge-hashmax": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_MAX), + "bridge-hashel": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_ELASTICITY), + "bridge-bridgeprio": (self._query_check_br_attr_int, Link.IFLA_BR_PRIORITY), + "bridge-igmp-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_IGMP_VERSION), + "bridge-mld-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_MLD_VERSION), + + "bridge-maxage": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MAX_AGE), + "bridge-fd": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_FORWARD_DELAY), + "bridge-hello": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_HELLO_TIME), + "bridge-ageing": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_AGEING_TIME), + "bridge-mcmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL), + "bridge-mcsqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL), + "bridge-mclmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL), + "bridge-mcqri": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL), + "bridge-mcqpi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERIER_INTVL), + "bridge-mcqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_INTVL), + } + + self._brport_attribute_query_check_handler = { + "bridge-pathcosts": self._query_check_brport_attr_int, + "bridge-portprios": self._query_check_brport_attr_int, + "bridge-portmcfl": self._query_check_brport_attr_boolean_yes_no, + "bridge-learning": self._query_check_brport_attr_boolean_on_off, + "bridge-arp-nd-suppress": self._query_check_brport_attr_boolean_on_off, + "bridge-unicast-flood": self._query_check_brport_attr_boolean_on_off, + "bridge-multicast-flood": self._query_check_brport_attr_boolean_on_off, + "bridge-broadcast-flood": self._query_check_brport_attr_boolean_on_off, + "bridge-portmcrouter": self._query_check_brport_attr_portmcrouter, + } + + self.bridge_vxlan_port_learning = utils.get_boolean_from_string( + policymanager.policymanager_api.get_module_globals( + self.__class__.__name__, + "bridge_vxlan_port_learning" + ), + default=True + ) + @staticmethod def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi): if not ifla_brport_group_maskhi: @@ -851,13 +951,8 @@ def _get_ifaceobj_bridge_ports(self, ifaceobj): return ' '.join(bridge_ports) - def _is_bridge_port(self, ifaceobj): - if self.brctlcmd.is_bridge_port(ifaceobj.name): - return True - return False - def check_valid_bridge(self, ifaceobj, ifname): - if LinkUtils.link_exists_nodryrun(ifname) and not LinkUtils.is_bridge(ifname): + if self.cache.link_exists(ifname) and not self.cache.link_is_bridge(ifname): self.log_error('misconfiguration of bridge attribute(s) on existing non-bridge interface (%s)' % ifname, ifaceobj=ifaceobj) return False return True @@ -879,10 +974,9 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): ifacenames_all) def get_dependent_ifacenames_running(self, ifaceobj): - self._init_command_handlers() - if not self.brctlcmd.bridge_exists(ifaceobj.name): + if not self.cache.bridge_exists(ifaceobj.name): return None - return self.brctlcmd.get_bridge_ports(ifaceobj.name) + return self.cache.get_slaves(ifaceobj.name) def _get_bridge_port_list(self, ifaceobj): @@ -944,7 +1038,7 @@ def _process_bridge_waitport(self, ifaceobj, portlist): starttime = time.time() while ((time.time() - starttime) < waitporttime): if all([False for p in waitportlist - if not self.ipcmd.link_exists(p)]): + if not self.cache.link_exists(p)]): break; time.sleep(1) except Exception, e: @@ -957,11 +1051,7 @@ def _enable_disable_ipv6(self, port, enable='1'): except Exception, e: self.logger.info(str(e)) - def handle_ipv6(self, ports, state, ifaceobj=None): - if (ifaceobj and - (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN) and - not ifaceobj.get_attr_value('address') and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE): - self._enable_disable_ipv6(ifaceobj.name, state) + def handle_ipv6(self, ports, state): for p in ports: self._enable_disable_ipv6(p, state) @@ -994,32 +1084,28 @@ def _add_ports(self, ifaceobj, ifaceobj_getfunc): bridgeportscondoneregex = self._get_bridge_port_condone_regex(ifaceobj) runningbridgeports = [] - self.ipcmd.batch_start() self._process_bridge_waitport(ifaceobj, bridgeports) - self.ipcmd.batch_start() # 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: for bport in runningbridgeports: if not bridgeports or bport not in bridgeports: if bridgeportscondoneregex and bridgeportscondoneregex.match(bport): self.logger.info("%s: port %s will stay enslaved as it matches with bridge-ports-condone-regex" % (ifaceobj.name, bport)) continue - self.ipcmd.link_set(bport, 'nomaster') + self.netlink.link_set_nomaster(bport) # set admin DOWN on all removed ports # that don't have config outside bridge if not ifaceobj_getfunc(bport): - netlink.link_set_updown(bport, "down") + self.netlink.link_down(bport) # enable ipv6 for ports that were removed self.handle_ipv6([bport], '0') else: runningbridgeports = [] if not bridgeports: - self.ipcmd.batch_commit() return [] err = 0 - ports = 0 newbridgeports = Set(bridgeports).difference(Set(runningbridgeports)) newly_enslaved_ports = [] @@ -1028,38 +1114,32 @@ def _add_ports(self, ifaceobj, ifaceobj_getfunc): if br_port in newbridgeports: newbridgeports_ordered.append(br_port) + self.iproute2.batch_start() + for bridgeport in newbridgeports_ordered: try: if (not ifupdownflags.flags.DRYRUN and - not self.ipcmd.link_exists(bridgeport)): + not self.cache.link_exists(bridgeport)): self.log_error('%s: bridge port %s does not exist' %(ifaceobj.name, bridgeport), ifaceobj) err += 1 continue - hwaddress = self.ipcmd.link_get_hwaddress(bridgeport) + hwaddress = self.cache.get_link_address(bridgeport) if not self._valid_ethaddr(hwaddress): self.log_warn('%s: skipping port %s, ' %(ifaceobj.name, bridgeport) + 'invalid ether addr %s' %hwaddress) continue - self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name) + self.iproute2.link_set_master(bridgeport, ifaceobj.name) newly_enslaved_ports.append(bridgeport) self.handle_ipv6([bridgeport], '1') - self.ipcmd.addr_flush(bridgeport) - ports += 1 - if ports == 250: - ports = 0 - self.ipcmd.batch_commit() - self.ipcmd.batch_start() + self.iproute2.addr_flush(bridgeport) except Exception, e: self.logger.error(str(e)) pass - try: - self.ipcmd.batch_commit() - except Exception, e: - self._pretty_print_add_ports_error(str(e), ifaceobj, - bridgeports) - pass + + self.iproute2.batch_commit() + self.cache.force_add_slave_list(ifaceobj.name, newly_enslaved_ports) if err: self.log_error('bridge configuration failed (missing ports)') @@ -1123,11 +1203,15 @@ def _compress_into_ranges(self, vids_ints): def _diff_vids(self, vids1_ints, vids2_ints): return Set(vids2_ints).difference(vids1_ints), Set(vids1_ints).difference(vids2_ints) - def _compare_vids(self, vids1, vids2, pvid=None): + def _compare_vids(self, vids1, vids2, pvid=None, expand_range=True): """ Returns true if the vids are same else return false """ - vids1_ints = self._ranges_to_ints(vids1) - vids2_ints = self._ranges_to_ints(vids2) + if expand_range: + vids1_ints = self._ranges_to_ints(vids1) + vids2_ints = self._ranges_to_ints(vids2) + else: + vids1_ints = self._ranges_to_ints(vids1) + vids2_ints = vids2 set_diff = Set(vids1_ints).symmetric_difference(vids2_ints) if pvid and int(pvid) in set_diff: set_diff.remove(int(pvid)) @@ -1144,7 +1228,7 @@ def _set_bridge_mcqv4src_compat(self, ifaceobj): if attrval: running_mcqv4src = {} if not ifupdownflags.flags.PERFMODE: - running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobj.name) + running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name) mcqs = {} srclist = attrval.split() for s in srclist: @@ -1153,25 +1237,14 @@ def _set_bridge_mcqv4src_compat(self, ifaceobj): k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys()) for v in k_to_del: - self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v) + self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v) for v in mcqs.keys(): - self.brctlcmd.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v]) + self.iproute2.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v]) elif not ifupdownflags.flags.PERFMODE: - running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobj.name) + running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name) if running_mcqv4src: for v in running_mcqv4src.keys(): - self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v) - - def _get_running_vidinfo(self): - if self._running_vidinfo_valid: - return self._running_vidinfo - self._running_vidinfo = {} - - # Removed check for PERFMODE. Need the get in all cases - # including reboot, so that we can configure the pvid correctly. - self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all_json() - self._running_vidinfo_valid = True - return self._running_vidinfo + self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v) def _set_bridge_vidinfo_compat(self, ifaceobj): # @@ -1196,13 +1269,13 @@ def _set_bridge_vidinfo_compat(self, ifaceobj): try: (port, pvid) = p.split('=') pvid = int(pvid) - running_pvid = self._get_running_pvid(port) + running_pvid = self.cache.get_pvid(port) if running_pvid: if running_pvid == pvid: continue else: - self.ipcmd.bridge_port_pvid_del(port, running_pvid) - self.ipcmd.bridge_port_pvid_add(port, pvid) + self.iproute2.bridge_vlan_del_pvid(port, running_pvid) + self.iproute2.bridge_vlan_add_pvid(port, pvid) except Exception, e: self.log_warn('%s: failed to set pvid `%s` (%s)' %(ifaceobj.name, p, str(e))) @@ -1219,18 +1292,18 @@ def _set_bridge_vidinfo_compat(self, ifaceobj): (port, val) = p.split('=') vids = val.split(',') vids_int = self._ranges_to_ints(vids) - running_vids = self.ipcmd.bridge_vlan_get_vids(port) + _, running_vids = self.cache.get_pvid_and_vids(port) if running_vids: (vids_to_del, vids_to_add) = \ self._diff_vids(vids_int, running_vids) if vids_to_del: - self.ipcmd.bridge_port_vids_del(port, + self.iproute2.bridge_vlan_del_vid_list(port, self._compress_into_ranges(vids_to_del)) if vids_to_add: - self.ipcmd.bridge_port_vids_add(port, + self.iproute2.bridge_vlan_add_vid_list(port, self._compress_into_ranges(vids_to_add)) else: - self.ipcmd.bridge_port_vids_add(port, vids_int) + self.iproute2.bridge_vlan_add_vid_list(port, vids_int) except Exception, e: self.log_warn('%s: failed to set vid `%s` (%s)' %(ifaceobj.name, p, str(e))) @@ -1271,7 +1344,8 @@ def fill_ifla_info_data_with_ifla_br_attribute(self, ifname, nl_attr, attr_name, - user_config): + user_config, + cached_value): try: translate_func = self._ifla_br_attributes_translate_user_config_to_netlink_map.get(nl_attr) @@ -1285,21 +1359,13 @@ def fill_ifla_info_data_with_ifla_br_attribute(self, attr=attr_name ) - old_cache_key = self._ifla_br_attributes_old_cache_key_map.get(nl_attr) - if old_cache_key and not link_just_created: - cached_value = self.brctlcmd.link_cache_get([ifname, 'linkinfo', old_cache_key]) - if not cached_value or cached_value == "None": - # the link already exists but we don't have any value - # cached for this attr, it probably means that the - # capability is not available on this system (i.e old kernel) - self.logger.debug('%s: ignoring %s %s: capability ' - 'probably not supported on this system' - % (ifname, attr_name, user_config)) - return - # we need to convert the cache value to "netlink" format - cached_value = translate_func(cached_value.lower()) - else: - cached_value = None + if not link_just_created and cached_value is None: + # the link already exists but we don't have any value + # cached for this attr, it probably means that the + # capability is not available on this system (i.e old kernel) + self.logger.debug("%s: ignoring %s %s: capability probably not supported on this system" + % (ifname, attr_name, user_config)) + return if not user_config and not link_just_created and cached_value is not None: # there is no user configuration for this attribute @@ -1335,16 +1401,28 @@ def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_awar ifla_info_data = dict() ifname = ifaceobj.name - self.logger.info('%s: apply bridge settings' % ifname) + self.logger.info('%s: applying bridge settings' % ifname) - for attr_name, nl_attr in self._ifla_br_attributes_map: + cached_ifla_info_data = self.cache.get_link_info_data(ifname) + + try: + # we compare the user value (or policy value) with the current running state + # we need to divide the cached value by 100 to ignore small difference. + # i.e. our default value is 31 but the kernel default seems to be 3125 + cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] /= 100 + cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] *= 100 + except: + pass + + for attr_name, nl_attr in self._ifla_br_attributes_map.iteritems(): self.fill_ifla_info_data_with_ifla_br_attribute( ifla_info_data=ifla_info_data, link_just_created=link_just_created, ifname=ifname, nl_attr=nl_attr, attr_name=attr_name, - user_config=ifaceobj.get_attr_value_first(attr_name) + user_config=ifaceobj.get_attr_value_first(attr_name), + cached_value=cached_ifla_info_data.get(nl_attr) ) # bridge-mcsnoop @@ -1354,7 +1432,9 @@ def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_awar ifname=ifname, nl_attr=Link.IFLA_BR_MCAST_SNOOPING, attr_name='bridge-mcsnoop', - user_config=self.get_bridge_mcsnoop_value(ifaceobj) + user_config=self.get_bridge_mcsnoop_value(ifaceobj), + cached_value=cached_ifla_info_data.get(Link.IFLA_BR_MCAST_SNOOPING) + ) # bridge-vlan-stats @@ -1365,7 +1445,8 @@ def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_awar ifname=ifname, nl_attr=Link.IFLA_BR_VLAN_STATS_ENABLED, attr_name='bridge-vlan-stats', - user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats + user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats, + cached_value=cached_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED) ) try: @@ -1385,7 +1466,7 @@ def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_awar self.logger.warning('%s: bridge stp: %s' % (ifname, str(e))) if ifla_info_data: - netlink.link_add_set(ifname=ifname, kind='bridge', ifla_info_data=ifla_info_data, link_exists=True) + self.netlink.link_set_bridge_info_data(ifname, ifla_info_data, link_just_created) def _check_vids(self, ifaceobj, vids): ret = True @@ -1405,22 +1486,8 @@ def _check_vids(self, ifaceobj, vids): %(ifaceobj.name, v)) return ret - def _get_running_pvid(self, ifacename): - pvid = 0 - - running_vidinfo = self._get_running_vidinfo() - for vinfo in running_vidinfo.get(ifacename, {}): - v = vinfo.get('vlan') - pvid = v if 'PVID' in vinfo.get('flags', []) else 0 - if pvid: - return pvid - return pvid - def _get_running_vids_n_pvid_str(self, ifacename): - vids = [] - pvid = None - - (vids, pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(ifacename) + pvid, vids = self.cache.get_pvid_and_vids(ifacename) if vids: ret_vids = self._compress_into_ranges(vids) @@ -1465,8 +1532,7 @@ def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid, if not self._check_vids(bportifaceobj, vids): return - (running_vids, running_pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid( - bportifaceobj.name) + running_pvid, running_vids = self.cache.get_pvid_and_vids(bportifaceobj.name) if not running_vids and not running_pvid: # There cannot be a no running pvid. @@ -1515,7 +1581,7 @@ def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid, if vids_to_del: if pvid_to_add in vids_to_del: vids_to_del.remove(pvid_to_add) - self.ipcmd.bridge_vids_del(bportifaceobj.name, + self.iproute2.bridge_vlan_del_vid_list_self(bportifaceobj.name, self._compress_into_ranges( vids_to_del), isbridge) except Exception, e: @@ -1524,7 +1590,7 @@ def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid, try: if pvid_to_del: - self.ipcmd.bridge_port_pvid_del(bportifaceobj.name, + self.iproute2.bridge_vlan_del_pvid(bportifaceobj.name, pvid_to_del) except Exception, e: self.log_warn('%s: failed to del pvid `%s` (%s)' @@ -1532,7 +1598,7 @@ def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid, try: if vids_to_add: - self.ipcmd.bridge_vids_add(bportifaceobj.name, + self.iproute2.bridge_vlan_add_vid_list_self(bportifaceobj.name, self._compress_into_ranges( vids_to_add), isbridge) except Exception, e: @@ -1542,7 +1608,7 @@ def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid, try: if pvid_to_add and pvid_to_add != running_pvid: - self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, + self.iproute2.bridge_vlan_add_pvid(bportifaceobj.name, pvid_to_add) except Exception, e: self.log_error('%s: failed to set pvid `%s` (%s)' @@ -1624,11 +1690,11 @@ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vla if not bridgeports: self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name) return - self.ipcmd.batch_start() + self.iproute2.batch_start() for bport in bridgeports: - # Use the brctlcmd bulk set method: first build a dictionary - # and then call set - if not self.ipcmd.bridge_port_exists(ifaceobj.name, bport): + # on link_set_master we need to wait until we cache the correct + # notification and register the brport as slave + if not self.cache.bridge_port_exists(ifaceobj.name, bport): self.logger.info('%s: skipping bridge config' %ifaceobj.name + ' for port %s (missing port)' %bport) continue @@ -1636,7 +1702,7 @@ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vla %(ifaceobj.name, bport)) bportifaceobjlist = ifaceobj_getfunc(bport) if not bportifaceobjlist: - continue + continue for bportifaceobj in bportifaceobjlist: # Dont process bridge port if it already has been processed # and there is no override on port_processed @@ -1657,7 +1723,7 @@ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vla err = True self.logger.warn('%s: %s' %(ifaceobj.name, str(e))) pass - self.ipcmd.bridge_batch_commit() + self.iproute2.batch_commit() if err: raise Exception('%s: errors applying port settings' %ifaceobj.name) @@ -1670,12 +1736,12 @@ def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfun self.warn_on_untagged_bridge_absence = False def bridge_port_get_bridge_name(self, ifaceobj): - bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name) + bridgename = self.cache.get_bridge_name_from_port(ifaceobj.name) if not bridgename: # bridge port is not enslaved to a bridge we need to find # the bridge in it's upper ifaces then enslave it for u in ifaceobj.upperifaces: - if self.ipcmd.is_bridge(u): + if self.cache.link_is_bridge(u): return True, u return False, None # return should_enslave port, bridgename @@ -1683,7 +1749,7 @@ def bridge_port_get_bridge_name(self, ifaceobj): def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port): if should_enslave_port: - netlink.link_set_master(ifaceobj.name, bridge_name) + self.netlink.link_set_master(ifaceobj.name, bridge_name) self.handle_ipv6([ifaceobj.name], '1') bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc) @@ -1701,7 +1767,7 @@ def up_bridge_port(self, ifaceobj, ifaceobj_getfunc): # bridge doesn't exist return - vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridge_name) + vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridge_name) if vlan_aware_bridge: self.up_bridge_port_vlan_aware_bridge(ifaceobj, ifaceobj_getfunc, @@ -1717,11 +1783,12 @@ def up_bridge_port(self, ifaceobj, ifaceobj_getfunc): ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED - def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_exists): + def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_just_created): if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE: if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc): return False - if link_exists: + if not link_just_created and not self.cache.bridge_is_vlan_aware(ifaceobj.name): + # if bridge-vlan-aware was added on a existing old-bridge, we need to reprocess all ports ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE return True return False @@ -1734,8 +1801,7 @@ def parse_interface_list_value(user_config): config[ifname] = value return config - def sync_bridge_learning_to_vxlan_brport(self, bridge_name, bridge_vlan_aware, brport_ifaceobj, - brport_name, brport_ifla_info_slave_data, brport_learning): + def sync_bridge_learning_to_vxlan_brport(self, bridge_name, brport_ifaceobj, brport_name, brport_ifla_info_slave_data, user_config_brport_learning_nl, cached_brport_learning): """ brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN and @@ -1749,62 +1815,77 @@ def sync_bridge_learning_to_vxlan_brport(self, bridge_name, bridge_vlan_aware, b kind = None ifla_info_data = {} - brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first('vxlan-learning') - # if the user explicitly defined vxlan-learning we need to honor his config - # and not sync vxlan-learning with bridge-learning + if user_config_brport_learning_nl is None: + user_config_brport_learning_nl = self.bridge_vxlan_port_learning + # bridge-learning is not configured by the user or by a policy + # use "bridge-vxlan-port-learning" policy to set bridge-learning (default on) - brport_vxlan_learning = self.ipcmd.get_vxlandev_learning(brport_name) + if user_config_brport_learning_nl != cached_brport_learning: + brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] \ + = cached_brport_learning \ + = user_config_brport_learning_nl - # if BRIDGE_LEARNING is in the desired configuration - # and differs from the running vxlan configuration - if brport_learning is not None and brport_learning != brport_vxlan_learning and not brport_vxlan_learning_config: - kind = 'vxlan' - ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_learning} - self.logger.info('%s: %s: vxlan learning and bridge learning out of sync: set %s' - % (bridge_name, brport_name, brport_learning)) + self.logger.info( + "%s: %s: set bridge-learning %s" + % (bridge_name, brport_name, "on" if user_config_brport_learning_nl else "off") + ) + else: + # in this case, the current bridge-learning value is properly configured and + # doesn't need to be reset. We need to make sure that BRPORT_LEARNING is not + # part of ifla_info_slave_data. + try: + del brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] + except: + pass + + # + # vxlan-learning sync: + # - elif brport_learning is None and bridge_vlan_aware: - # is bridge-learning is not configured but the bridge is vlan-aware + brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first("vxlan-learning") + # if vxlan-learning is defined by the user or via policy file we need + # to honor his config and not sync vxlan-learning with bridge-learning - running_value = self.ipcmd.get_brport_learning_bool(brport_name) - default_value = utils.get_boolean_from_string(self.get_mod_subattr('bridge-learning', 'default')) + if not brport_vxlan_learning_config: + # check policy file + brport_vxlan_learning_config = policymanager.policymanager_api.get_attr_default("vxlan", "vxlan-learning") - if default_value != running_value: - brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] = default_value + # convert vxlan-learning string to netlink value (if None use brport-learning value instead) + brport_vxlan_learning_config_nl = utils.get_boolean_from_string(brport_vxlan_learning_config) \ + if brport_vxlan_learning_config \ + else cached_brport_learning - if not brport_vxlan_learning_config: - kind = 'vxlan' - ifla_info_data = {Link.IFLA_VXLAN_LEARNING: default_value} - self.logger.info('%s: %s: reset brport learning to %s and sync vxlan learning' - % (bridge_name, brport_name, default_value)) + if brport_vxlan_learning_config_nl != self.cache.get_link_info_data_attribute(brport_name, Link.IFLA_VXLAN_LEARNING): + self.logger.info( + "%s: %s: vxlan learning and bridge learning out of sync: set vxlan-learning %s" + % (bridge_name, brport_name, "on" if brport_vxlan_learning_config_nl else "off") + ) + ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_vxlan_learning_config_nl} + kind = "vxlan" # if kind and ifla_info_data are set they will be added to the # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING return kind, ifla_info_data - def check_vxlan_brport_arp_suppress(self, ifaceobj, bridge_vlan_aware, brport_ifaceobj, brport_name, user_config): - if user_config: - if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN: - self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' ' - 'is not supported on a non-vxlan port' - % (ifaceobj.name, brport_name)) - raise Exception() - elif (bridge_vlan_aware and - (not self.arp_nd_suppress_only_on_vxlan or - (self.arp_nd_suppress_only_on_vxlan and - brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN))): - return self.get_mod_subattr('bridge-arp-nd-suppress', 'default') - return None - def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]): ifname = ifaceobj.name try: brports_ifla_info_slave_data = dict() brport_ifaceobj_dict = dict() + brport_name_list = [] + + cache_brports_ifla_info_slave_data = {} + + port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED_OVERRIDE - running_brports = self.brctlcmd.get_bridge_ports(ifname) + running_brports = self.cache.get_slaves(ifname) + # If target_ports is specified we want to configure only this + # sub-list of port, we need to check if these ports are already + # enslaved, if not they will be ignored. + # If target_ports is not populated we will apply the brport + # attributes on all running brport. if target_ports: new_targets = [] for brport_name in target_ports: @@ -1814,24 +1895,46 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw new_targets.append(brport_name) running_brports = new_targets - self.logger.info('%s: applying bridge port configuration: %s' % (ifname, running_brports)) - - # If target_ports is specified we want to configure only this - # sub-list of port we need to check if these ports are already - # enslaved, if not they will be ignored. - # If target_ports is not populated we will apply the brport - # attributes on all running brport. - for port in running_brports: brport_list = ifaceobj_getfunc(port) if brport_list: - brport_ifaceobj_dict[port] = brport_list[0] - brports_ifla_info_slave_data[port] = dict() + port_already_processed = False + + # ports just added to the bridge have to be processed + if port not in newly_enslaved_ports: + # check if brport was already processed + for brportifaceobj in brport_list: + if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED: + # skip port if already processed (probably by `up_bridge_port`) + port_already_processed = True + self.logger.info("%s: port %s: already processed" % (ifname, port)) + break + + if not port_already_processed: + brport_name_list.append(port) + brport_ifaceobj_dict[port] = brport_list[0] + brports_ifla_info_slave_data[port] = dict() + + if not ifupdownflags.flags.PERFMODE and port not in newly_enslaved_ports: + # if the port has just been enslaved, info_slave_data is not cached yet + cache_brports_ifla_info_slave_data[port] = self.cache.get_link_info_slave_data(port) + else: + cache_brports_ifla_info_slave_data[port] = {} + + if not brport_name_list: + self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj) + return + + self.logger.info('%s: applying bridge port configuration: %s' % (ifname, brport_name_list)) + + cached_bridge_mcsnoop = self.cache.get_bridge_multicast_snooping(ifname) bridge_ports_learning = {} + bridge_ports_vxlan_arp_suppress = {} + cached_bridge_ports_learning = {} # we iterate through all IFLA_BRPORT supported attributes - for attr_name, nl_attr in self._ifla_brport_attributes_map: + for attr_name, nl_attr in self._ifla_brport_attributes_map.iteritems(): br_config = ifaceobj.get_attr_value_first(attr_name) translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr) @@ -1871,9 +1974,8 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw brport_config = brport_ifaceobj.get_attr_value_first(attr_name) brport_name = brport_ifaceobj.name - if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports: - # if the port has just been enslaved, info_slave_data is not cached yet - cached_value = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', nl_attr]) + if not ifupdownflags.flags.PERFMODE: + cached_value = cache_brports_ifla_info_slave_data.get(brport_name, {}).get(nl_attr, None) else: cached_value = None @@ -1901,15 +2003,21 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw # attribute specific work # This shouldn't be here but we don't really have a choice otherwise this # will require too much code duplication and will make the code very complex - if nl_attr == Link.IFLA_BRPORT_ARP_SUPPRESS: + if nl_attr == Link.IFLA_BRPORT_NEIGH_SUPPRESS: + bridge_ports_vxlan_arp_suppress[brport_name] = user_config try: - arp_suppress = self.check_vxlan_brport_arp_suppress(ifaceobj, - bridge_vlan_aware, - brport_ifaceobj, - brport_name, - user_config) - if arp_suppress: - user_config = arp_suppress + if user_config: + if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN: + self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' ' + 'is not supported on a non-vxlan port' + % (ifaceobj.name, brport_name)) + continue + elif bridge_vlan_aware: + if not self.arp_nd_suppress_only_on_vxlan: + user_config = self.get_mod_subattr('bridge-arp-nd-suppress', 'default') + elif self.arp_nd_suppress_only_on_vxlan and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN: + # ignore the case of VXLAN brport - handled later in the code + continue except: continue elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK: @@ -1941,7 +2049,9 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw # IFLA_BRPORT_LEARNING if the user value is already configured and running # nevertheless we still need to check if the vxlan-learning is rightly synced with # the brport since it might go out of sync for X and Y reasons. + # we also store the cached value to avoid an extra cache lookup. bridge_ports_learning[brport_name] = user_config_nl + cached_bridge_ports_learning[brport_name] = cached_value elif cached_value is not None: # no config found, do we need to reset to default? @@ -1949,18 +2059,30 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw if default: default_netlink = translate_func(default) - if (nl_attr == Link.IFLA_BRPORT_LEARNING - and not ifupdownflags.flags.PERFMODE - and brport_name not in newly_enslaved_ports): - try: - if self.ipcmd.get_brport_peer_link(brport_name): - if default_netlink != cached_value: - self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning' - % (ifname, brport_name)) - continue - bridge_ports_learning[brport_name] = default_netlink - except Exception as e: - self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e))) + if nl_attr == Link.IFLA_BRPORT_LEARNING: + # for vxlan-learning sync purposes we need to save the user config for each brports. + # The dictionary 'brports_ifla_info_slave_data' might not contain any value for + # IFLA_BRPORT_LEARNING if the user value is already configured and running + # nevertheless we still need to check if the vxlan-learning is rightly synced with + # the brport since it might go out of sync for X and Y reasons. + # we also store the cached value to avoid an extra cache lookup. + cached_bridge_ports_learning[brport_name] = cached_value + bridge_ports_learning[brport_name] = self.bridge_vxlan_port_learning + + if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN: + # bridge-learning for vxlan device is handled separatly in sync_bridge_learning_to_vxlan_brport + continue + + if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports: + # We don't query new slaves and not during boot + try: + if self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_PEER_LINK): + if default_netlink != cached_value: + self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning' + % (ifname, brport_name)) + continue + except Exception as e: + self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e))) if default_netlink != cached_value: self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s' @@ -1968,6 +2090,12 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw self.logger.debug('(cache %s)' % cached_value) brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink + # is the current bridge (ifaceobj) a QinQ bridge? + # This variable is initialized to None and will be + # change to True/False, so that the check is only + # performed once + qinq_bridge = None + # applying bridge port configuration via netlink for brport_name, brport_ifla_info_slave_data in brports_ifla_info_slave_data.items(): @@ -1978,13 +2106,11 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1} kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name, - bridge_vlan_aware, brport_ifaceobj, brport_name, brport_ifla_info_slave_data, - bridge_ports_learning.get(brport_name)) - - cached_bridge_mcsnoop = self.brctlcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BR_MCAST_SNOOPING]) + bridge_ports_learning.get(brport_name), + cached_bridge_ports_learning.get(brport_name)) if (self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string( self.get_bridge_mcsnoop_value(ifaceobj) @@ -1992,30 +2118,118 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter" # is on and mcsnoop is on (or mcsnoop is already enabled on the # bridge, set 'bridge-portmcrouter 2' on vxlan ports (if not set by the user) - if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER): + if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER) \ + and self.cache.get_bridge_port_multicast_router(brport_name) != 2: brport_ifla_info_slave_data[Link.IFLA_BRPORT_MULTICAST_ROUTER] = 2 self.logger.info("%s: %s: vxlan bridge igmp snooping: enable port multicast router" % (ifname, brport_name)) + + # + # handling attribute: bridge-arp-nd-suppress + # defaults to bridge-vxlan-arp-nd-suppress policy (default False) + # + user_config_neigh_suppress = bridge_ports_vxlan_arp_suppress.get(brport_name) + + if user_config_neigh_suppress is None: + + if qinq_bridge is None: + # QinQ bridge hasn't been checked yet + qinq_bridge = self.is_qinq_bridge( + ifaceobj, + brport_name, + running_brports, + brport_ifaceobj_dict, + ifaceobj_getfunc + ) + + if qinq_bridge: + # exclude QinQ bridge from arp-nd-suppress default policy on + config_neigh_suppress = 0 + self.logger.info("%s: QinQ bridge detected: %s: set bridge-arp-nd-suppress off" % (ifname, brport_name)) + else: + config_neigh_suppress = self.bridge_vxlan_arp_nd_suppress_int + else: + config_neigh_suppress = int(utils.get_boolean_from_string(user_config_neigh_suppress)) + + brport_neigh_suppress_cached_value = self.cache.get_link_info_slave_data_attribute( + brport_name, + Link.IFLA_BRPORT_NEIGH_SUPPRESS + ) + + if config_neigh_suppress != brport_neigh_suppress_cached_value: + brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS] = config_neigh_suppress + + if not user_config_neigh_suppress: + # if the configuration is not explicitely defined by the user + # we need report that the default behavior is enabled by policy + self.logger.info( + "%s: set bridge-arp-nd-suppress %s by default on vxlan port (%s)" + % (ifname, "on" if self.bridge_vxlan_arp_nd_suppress else "off", brport_name) + ) + else: + # the user configuration (or policy) is already configured and running + # we need to remove this attribute from the request dictionary + try: + del brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS] + except: + pass + + # + # + # + else: kind = None ifla_info_data = {} if brport_ifla_info_slave_data or ifla_info_data: try: - netlink.link_add_set(ifname=brport_name, - kind=kind, - ifla_info_data=ifla_info_data, - slave_kind='bridge', - ifla_info_slave_data=brport_ifla_info_slave_data) + self.netlink.link_set_brport_with_info_slave_data( + ifname=brport_name, + kind=kind, + ifla_info_data=ifla_info_data, + ifla_info_slave_data=brport_ifla_info_slave_data + ) except Exception as e: self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e))) - self._set_bridge_vidinfo_compat(ifaceobj) - self._set_bridge_mcqv4src_compat(ifaceobj) - self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj)) + self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj) except Exception as e: self.log_error(str(e), ifaceobj) + def is_qinq_bridge(self, ifaceobj, brport_name, running_brports, brport_ifaceobj_dict, ifaceobj_getfunc): + """ Detect QinQ bridge + Potential improvement: We could add a ifaceobj.link_privflags called + BRIDGE_QINQ but for now it is not necessary. + """ + + # bridge-vlan-aware case + if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE: + return (ifaceobj.get_attr_value_first("bridge-vlan-protocol") or "").lower() == "802.1ad" + + # old-bridge + else: + for qinq_running_brport in running_brports: + if qinq_running_brport == brport_name: + continue + + qinq_running_brport_ifaceobj = brport_ifaceobj_dict.get(qinq_running_brport) + + if not qinq_running_brport_ifaceobj: + continue + + if qinq_running_brport_ifaceobj.link_kind & ifaceLinkKind.VLAN: + for lower_iface in qinq_running_brport_ifaceobj.lowerifaces or []: + for lower_ifaceobj in ifaceobj_getfunc(lower_iface) or []: + if (lower_ifaceobj.get_attr_value_first("vlan-protocol") or "").lower() == "802.1ad": + return True + return False + + def bridge_process_vidinfo_mcqv4src_maxwait(self, ifaceobj): + self._set_bridge_vidinfo_compat(ifaceobj) + self._set_bridge_mcqv4src_compat(ifaceobj) + self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj)) + def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask): """ Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI @@ -2037,7 +2251,7 @@ def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slav ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi) # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above - cached_ifla_brport_group_fwd_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI]) + cached_ifla_brport_group_fwd_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI) log_mask_change = True # if user specify bridge-l2protocol-tunnel stp cdp @@ -2086,18 +2300,18 @@ def up_bridge(self, ifaceobj, ifaceobj_getfunc): ifname = ifaceobj.name if ifupdownflags.flags.PERFMODE: - link_just_created = True link_exists = False else: - link_exists = self.ipcmd.link_exists(ifaceobj.name) - link_just_created = not link_exists + link_exists = self.cache.link_exists(ifaceobj.name) if not link_exists: - netlink.link_add_bridge(ifname, self.get_bridge_mtu(ifaceobj)) + self.netlink.link_add_bridge(ifname, mtu=self.get_bridge_mtu(ifaceobj)) + link_just_created = True else: + link_just_created = False self.logger.info('%s: bridge already exists' % ifname) - bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, not link_just_created) + bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, link_just_created) self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware) @@ -2110,10 +2324,9 @@ def up_bridge(self, ifaceobj, ifaceobj_getfunc): running_ports = '' try: - running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name) + running_ports = self.cache.get_slaves(ifaceobj.name) if not running_ports: return - self.handle_ipv6([], '1', ifaceobj=ifaceobj) self._apply_bridge_port_settings_all(ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc, bridge_vlan_aware=bridge_vlan_aware) @@ -2123,17 +2336,18 @@ def up_bridge(self, ifaceobj, ifaceobj_getfunc): self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e))) finally: if ifaceobj.link_type != ifaceLinkType.LINK_NA: + self.iproute2.batch_start() for p in running_ports: ifaceobj_list = ifaceobj_getfunc(p) if (ifaceobj_list and ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN): - netlink.link_set_updown(p, "down") + self.iproute2.link_down(p) continue - try: - netlink.link_set_updown(p, "up") - except Exception, e: - self.logger.debug('%s: %s: link set up (%s)' - % (ifaceobj.name, p, str(e))) - pass + self.iproute2.link_up(p) + try: + self.iproute2.batch_commit() + except Exception as e: + # link set up on bridge ports failed - ignore and log debug + self.logger.debug("%s: %s" % (ifname, str(e))) try: self._up_bridge_mac(ifaceobj, ifaceobj_getfunc) @@ -2158,11 +2372,11 @@ def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc): if iface_user_configured_hwaddress: iface_mac = iface_user_configured_hwaddress - if not iface_mac and not self.ipcmd.link_exists(bridge_mac_intf): + if not iface_mac and not self.cache.link_exists(bridge_mac_intf): continue if not iface_mac: - iface_mac = self.ipcmd.cache_get('link', [bridge_mac_intf, 'hwaddress']) + iface_mac = self.cache.get_link_address(bridge_mac_intf) # if hwaddress attribute is not configured we use the running mac addr self.bridge_mac_iface = (bridge_mac_intf, iface_mac) @@ -2183,7 +2397,7 @@ def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc): # what we have in the cache (data retrieved via a netlink dump by # nlmanager). nlmanager return all macs in lower-case else: - iface_mac = self.ipcmd.link_get_hwaddress(port) + iface_mac = self.cache.get_link_address(port) if iface_mac: self.bridge_mac_iface = (port, iface_mac) @@ -2193,7 +2407,7 @@ def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc): def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac): if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'): - self.ipcmd.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None) + self.iproute2.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None) def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc): """ @@ -2224,20 +2438,20 @@ def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc): # the cache_check won't match because nlmanager return "08:00:27:42:42:04" # from the kernel. The only way to counter that is to convert all mac to int # and compare the ints, it will increase perfs and be safer. - cached_value = self.ipcmd.cache_get('link', [ifname, 'hwaddress']) + cached_value = self.cache.get_link_address(ifname) self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value)) - if cached_value and self.ipcmd.mac_str_to_int(cached_value) == self.ipcmd.mac_str_to_int(bridge_mac): + if cached_value and utils.mac_str_to_int(cached_value) == utils.mac_str_to_int(bridge_mac): # the bridge mac is already set to the bridge_mac_intf's mac return self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf)) try: - self.ipcmd.link_set(ifname, 'address', value=bridge_mac, force=True) + self.netlink.link_set_address(ifname, bridge_mac) # force=True except Exception as e: self.logger.info('%s: %s' % (ifname, str(e))) # log info this error because the user didn't explicitly configured this else: - self._add_bridge_mac_to_fdb(ifaceobj, self.ipcmd.link_get_hwaddress(ifname)) + self._add_bridge_mac_to_fdb(ifaceobj, self.cache.get_link_address(ifname)) def _up(self, ifaceobj, ifaceobj_getfunc=None): if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT: @@ -2258,26 +2472,18 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): if not self._is_bridge(ifaceobj): return ifname = ifaceobj.name - if not self.ipcmd.link_exists(ifname): + if not self.cache.link_exists(ifname): return try: - running_ports = self.brctlcmd.get_bridge_ports(ifname) + running_ports = self.cache.get_slaves(ifname) if running_ports: self.handle_ipv6(running_ports, '0') if ifaceobj.link_type != ifaceLinkType.LINK_NA: - map(lambda p: netlink.link_set_updown(p, 'down'), running_ports) + map(lambda p: self.netlink.link_down(p), running_ports) except Exception as e: self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) try: - netlink.link_del(ifname) - - if utils.get_boolean_from_string(ifupdownconfig.config.get("ifreload_down_changed")): - self.ipcmd.del_cache_entry(ifname) - for upper in ifaceobj.upperifaces or []: - try: - self.ipcmd.del_cache_entry(upper) - except: - pass + self.netlink.link_del(ifname) except Exception as e: ifaceobj.set_status(ifaceStatus.ERROR) self.logger.error(str(e)) @@ -2289,7 +2495,7 @@ def _query_running_vidinfo_compat(self, ifaceobjrunning, ports): running_bridge_port_vids = '' for p in ports: try: - running_vids = self._get_runing_vids(p) + _, running_vids = self.cache.get_pvid_and_vids(p) if running_vids: running_bridge_port_vids += ' %s=%s' %(p, ','.join(running_vids)) @@ -2300,7 +2506,7 @@ def _query_running_vidinfo_compat(self, ifaceobjrunning, ports): running_bridge_port_pvid = '' for p in ports: try: - running_pvid = self._get_runing_pvid(p) + running_pvid = self.cache.get_pvid(p) if running_pvid: running_bridge_port_pvid += ' %s=%s' %(p, running_pvid) @@ -2308,7 +2514,7 @@ def _query_running_vidinfo_compat(self, ifaceobjrunning, ports): pass running_attrs['bridge-port-pvids'] = running_bridge_port_pvid - running_bridge_vids = self.ipcmd.bridge_vlan_get_vids(ifaceobjrunning.name) + _, running_bridge_vids = self.cache.get_pvid_and_vids(ifaceobjrunning.name) if running_bridge_vids: running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids)) return running_attrs @@ -2375,7 +2581,7 @@ def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc, return running_attrs def _query_running_mcqv4src(self, ifaceobjrunning): - running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobjrunning.name) + running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobjrunning.name) mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()] mcqs.sort() mcq = ' '.join(mcqs) @@ -2383,58 +2589,109 @@ def _query_running_mcqv4src(self, ifaceobjrunning): def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc, bridge_vlan_aware=False): + + ifname = ifaceobjrunning.name bridgeattrdict = {} userspace_stp = 0 ports = None - skip_kernel_stp_attrs = 0 - try: if self.systcl_get_net_bridge_stp_user_space() == '1': userspace_stp = 1 except Exception as e: self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e))) - tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name) - if not tmpbridgeattrdict: - self.logger.warn('%s: unable to get bridge attrs' - %ifaceobjrunning.name) - return bridgeattrdict + bridge_ifla_info_data = self.cache.get_link_info_data(ifname) + # Fill bridge_ports and bridge stp attributes first - ports = tmpbridgeattrdict.get('ports') - if ports: - bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())] - stp = tmpbridgeattrdict.get('stp', 'no') - if stp != self.get_mod_subattr('bridge-stp', 'default'): - bridgeattrdict['bridge-stp'] = [stp] - - if stp == 'yes' and userspace_stp: - skip_kernel_stp_attrs = 1 - - vlan_stats = utils.get_onff_from_onezero( - tmpbridgeattrdict.get('vlan-stats', None)) - if (vlan_stats and - vlan_stats != self.get_mod_subattr('bridge-vlan-stats', 'default')): - bridgeattrdict['bridge-vlan-stats'] = [vlan_stats] - - bool2str = {'0': 'no', '1': 'yes'} - # pick all other attributes - for k,v in tmpbridgeattrdict.items(): - if not v: - continue - if k == 'ports' or k == 'stp': - continue + # + # bridge-ports + # + bridgeattrdict["bridge-ports"] = [" ".join(self.cache.get_slaves(ifname))] + + # + # bridge-stp + # + cached_stp = bool(bridge_ifla_info_data.get(Link.IFLA_BR_STP_STATE)) + + if cached_stp != utils.get_boolean_from_string( + self.get_mod_subattr("bridge-stp", "default") + ): + bridgeattrdict['bridge-stp'] = ["yes" if cached_stp else "no"] + + skip_kernel_stp_attrs = cached_stp and userspace_stp + + if skip_kernel_stp_attrs: + bridge_attributes_map = { + "bridge-mcqifaddr": Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, + "bridge-mcquerier": Link.IFLA_BR_MCAST_QUERIER, + "bridge-mcrouter": Link.IFLA_BR_MCAST_ROUTER, + "bridge-mcstats": Link.IFLA_BR_MCAST_STATS_ENABLED, + "bridge-mcsnoop": Link.IFLA_BR_MCAST_SNOOPING, + "bridge-mclmc": Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, + "bridge-mclmi": Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, + "bridge-mcqri": Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + "bridge-mcqpi": Link.IFLA_BR_MCAST_QUERIER_INTVL, + "bridge-mcsqc": Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, + "bridge-mcsqi": Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + "bridge-mcmi": Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, + "bridge-mcqi": Link.IFLA_BR_MCAST_QUERY_INTVL, + } + else: + bridge_attributes_map = dict(self._ifla_br_attributes_map) + try: + del bridge_attributes_map[Link.IFLA_BR_STP_STATE] + except: + pass + + # + # bridge-vlan-stats + # + cached_vlan_stats = bridge_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED) + + if cached_vlan_stats != utils.get_boolean_from_string( + self.get_mod_subattr("bridge-vlan-stats", "default") + ): + bridgeattrdict['bridge-vlan-stats'] = ["on" if cached_vlan_stats else "off"] + + try: + del bridge_attributes_map[Link.IFLA_BR_VLAN_STATS_ENABLED] + except: + pass - if skip_kernel_stp_attrs and k[:2] != 'mc': - # only include igmp attributes if kernel stp is off + lambda_nl_value_int_divide100 = lambda x: str(x / 100) + lambda_nl_value_to_yes_no_boolean = lambda x: "yes" if x else "no" + + bridge_attr_value_netlink_to_string_dict = { + Link.IFLA_BR_VLAN_PROTOCOL: lambda x: x.lower(), # return lower case vlan protocol + Link.IFLA_BR_AGEING_TIME: lambda_nl_value_int_divide100, + Link.IFLA_BR_MAX_AGE: lambda_nl_value_int_divide100, + Link.IFLA_BR_FORWARD_DELAY: lambda_nl_value_int_divide100, + Link.IFLA_BR_HELLO_TIME: lambda_nl_value_int_divide100, + Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL: lambda_nl_value_int_divide100, + Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: lambda_nl_value_int_divide100, + Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL: lambda_nl_value_int_divide100, + Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: lambda_nl_value_int_divide100, + Link.IFLA_BR_MCAST_QUERIER_INTVL: lambda_nl_value_int_divide100, + Link.IFLA_BR_MCAST_QUERY_INTVL: lambda_nl_value_int_divide100, + Link.IFLA_BR_VLAN_FILTERING: lambda_nl_value_to_yes_no_boolean, + Link.IFLA_BR_MCAST_QUERY_USE_IFADDR: lambda_nl_value_to_yes_no_boolean, + Link.IFLA_BR_MCAST_SNOOPING: lambda_nl_value_to_yes_no_boolean, + Link.IFLA_BR_MCAST_QUERIER: lambda_nl_value_to_yes_no_boolean, + Link.IFLA_BR_MCAST_ROUTER: lambda_nl_value_to_yes_no_boolean, + } + + for attr_name, attr_nl in bridge_attributes_map.iteritems(): + default_value = self.get_mod_subattr(attr_name, "default") + cached_value = bridge_ifla_info_data.get(attr_nl) + + if cached_value is None: continue - attrname = 'bridge-' + k - mod_default = self.get_mod_subattr(attrname, 'default') - if v != mod_default: - # convert '0|1' running values to 'no|yes' - if v in bool2str.keys() and bool2str[v] == mod_default: - continue - bridgeattrdict[attrname] = [v] + + cached_value_string = bridge_attr_value_netlink_to_string_dict.get(attr_nl, str)(cached_value) + + if default_value != cached_value_string: + bridgeattrdict[attr_name] = [cached_value_string] if bridge_vlan_aware: if not ports: @@ -2463,45 +2720,44 @@ def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc, 'bridge-learning' : '', 'bridge-unicast-flood' : '', 'bridge-multicast-flood' : '', + 'bridge-broadcast-flood' : '', 'bridge-arp-nd-suppress' : '', } for p, v in ports.items(): - v = self.brctlcmd.bridge_get_pathcost(ifaceobjrunning.name, p) + v = str(self.cache.get_brport_cost(p)) if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'): portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v) - v = self.brctlcmd.bridge_get_portprio(ifaceobjrunning.name, p) + v = str(self.cache.get_brport_priority(p)) if v and v != self.get_mod_subattr('bridge-portprios', 'default'): portconfig['bridge-portprios'] += ' %s=%s' %(p, v) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'learning')) + v = utils.get_onff_from_onezero(self.cache.get_brport_learning(p)) if (v and v != self.get_mod_subattr('bridge-learning', 'default')): portconfig['bridge-learning'] += ' %s=%s' %(p, v) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'unicast-flood')) + v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(p)) if (v and v != self.get_mod_subattr('bridge-unicast-flood', 'default')): portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'multicast-flood')) + v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(p)) if (v and v != self.get_mod_subattr('bridge-multicast-flood', 'default')): portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name, - p, 'arp-nd-suppress')) + v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(p)) + if (v and + v != self.get_mod_subattr('bridge-broadcast-flood', + 'default')): + portconfig['bridge-broadcast-flood'] += ' %s=%s' %(p, v) + + v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(p)) if (v and v != self.get_mod_subattr('bridge-arp-nd-suppress', 'default')): @@ -2522,69 +2778,64 @@ def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr): ifaceobjcurr.update_config_with_status('bridge-mcqv4src', running_mcqs, 1 if running_mcqs != mcqsout else 0) - def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr): - err = 0 - attrval = ifaceobj.get_attr_value_first('bridge-port-vids') - if attrval: - running_bridge_port_vids = '' - portlist = self.parse_port_list(ifaceobj.name, attrval) - if not portlist: - self.log_warn('%s: could not parse \'bridge-port-vids %s\'' - %(ifaceobj.name, attrval)) + def _query_check_bridge_vidinfo(self, ifname, ifaceobj, ifaceobjcurr): + # + # bridge-port-vids + # + bridge_port_vids_user_config = ifaceobj.get_attr_value_first("bridge-port-vids") + if bridge_port_vids_user_config: + + port_list = self.parse_port_list(ifname, bridge_port_vids_user_config) + + if not port_list: + self.log_warn("%s: could not parse 'bridge-port-vids %s'" + % (ifname, bridge_port_vids_user_config)) + ifaceobjcurr.update_config_with_status("bridge-port-vids", "ERROR", 1) return - err = 0 - for p in portlist: + + error = False + for port_config in port_list: try: - (port, val) = p.split('=') - vids = val.split(',') - running_vids = self.ipcmd.bridge_vlan_get_vids(port) - if running_vids: - if not self._compare_vids(vids, running_vids): - err += 1 - running_bridge_port_vids += ' %s=%s' %(port, - ','.join(running_vids)) - else: - running_bridge_port_vids += ' %s' %p - else: - err += 1 - except Exception, e: - self.log_warn('%s: failure checking vid %s (%s)' - %(ifaceobj.name, p, str(e))) - if err: - ifaceobjcurr.update_config_with_status('bridge-port-vids', - running_bridge_port_vids, 1) - else: - ifaceobjcurr.update_config_with_status('bridge-port-vids', - attrval, 0) + port, vids_raw = port_config.split("=") + packed_vids = vids_raw.split(",") + + running_pvid, running_vids = self.cache.get_pvid_and_vids(port) + + if not self._compare_vids(packed_vids, running_vids, pvid=running_pvid, expand_range=False): + error = True + except Exception as e: + self.log_warn("%s: failure checking vid %s (%s)" % (ifname, port_config, str(e))) + + ifaceobjcurr.update_config_with_status("bridge-port-vids", bridge_port_vids_user_config, error) + + # + # bridge-port-pvids + # attrval = ifaceobj.get_attr_value_first('bridge-port-pvids') if attrval: portlist = self.parse_port_list(ifaceobj.name, attrval) if not portlist: self.log_warn('%s: could not parse \'bridge-port-pvids %s\'' - %(ifaceobj.name, attrval)) + % (ifname, attrval)) return - running_bridge_port_pvids = '' - err = 0 + + error = False + running_pvid_config = [] for p in portlist: - try: - (port, pvid) = p.split('=') - running_pvid = self.ipcmd.bridge_vlan_get_vids(port) - if running_pvid and running_pvid == pvid: - running_bridge_port_pvids += ' %s' %p - else: - err += 1 - running_bridge_port_pvids += ' %s=%s' %(port, - running_pvid) - except Exception, e: - self.log_warn('%s: failure checking pvid %s (%s)' - %(ifaceobj.name, pvid, str(e))) - if err: - ifaceobjcurr.update_config_with_status('bridge-port-pvids', - running_bridge_port_pvids, 1) - else: - ifaceobjcurr.update_config_with_status('bridge-port-pvids', - running_bridge_port_pvids, 0) + (port, pvid) = p.split('=') + running_pvid, _ = self.cache.get_pvid_and_vids(port) + + running_pvid_config.append("%s=%s" % (port, running_pvid)) + + if running_pvid != int(pvid): + error = True + + ifaceobjcurr.update_config_with_status( + "bridge-port-pvids", + " ".join(running_pvid_config), + int(error) + ) vids = self.get_ifaceobj_bridge_vids(ifaceobj) if vids[1]: @@ -2596,170 +2847,198 @@ def _query_check_snooping_wdefault(self, ifaceobj): and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN): ifaceobj.replace_config('bridge-mcsnoop', 'no') - def _query_check_bridge(self, ifaceobj, ifaceobjcurr, - ifaceobj_getfunc=None): + def _query_check_bridge(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): if not self._is_bridge(ifaceobj): return - if not self.brctlcmd.bridge_exists(ifaceobj.name): - self.logger.info('%s: bridge: does not exist' %(ifaceobj.name)) - return - self._query_check_snooping_wdefault(ifaceobj) + ifname = ifaceobj.name - ifaceattrs = self.dict_key_subset(ifaceobj.config, - self.get_mod_attrs()) - #Add default attributes if --with-defaults is set - if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in ifaceattrs: - ifaceattrs.append('bridge-stp') - if not ifaceattrs: + if not self.cache.bridge_exists(ifname): + self.logger.info("%s: bridge: does not exist" % (ifname)) return - try: - runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name) - if not runningattrs: - self.logger.debug('%s: bridge: unable to get bridge attrs' - %ifaceobj.name) - runningattrs = {} - except Exception, e: - self.logger.warn(str(e)) - runningattrs = {} - self._query_check_support_yesno_attrs(runningattrs, ifaceobj) + self._query_check_snooping_wdefault(ifaceobj) - filterattrs = ['bridge-vids', 'bridge-trunk', 'bridge-port-vids', - 'bridge-port-pvids'] + user_config_attributes = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs()) - diff = Set(ifaceattrs).difference(filterattrs) + # add default attributes if --with-defaults is set + if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in user_config_attributes: + user_config_attributes.append('bridge-stp') - if 'bridge-l2protocol-tunnel' in diff: - diff.remove('bridge-l2protocol-tunnel') - # bridge-l2protocol-tunnel requires separate handling + if not user_config_attributes: + return - if 'bridge-ports' in diff: - self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, runningattrs.get('ports', {}).keys(), ifaceobj_getfunc) - diff.remove('bridge-ports') + if "bridge-ports" in user_config_attributes: + self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, self.cache.get_slaves(ifname), ifaceobj_getfunc) - if "bridge-ports-condone-regex" in diff: + if "bridge-ports-condone-regex" in user_config_attributes: ifaceobjcurr.update_config_with_status( "bridge-ports-condone-regex", self._get_bridge_port_condone_regex(ifaceobj, True), 0 ) - diff.remove("bridge-ports-condone-regex") - - for k in diff: - # get the corresponding ifaceobj attr - v = ifaceobj.get_attr_value_first(k) - if not v: - if ifupdownflags.flags.WITHDEFAULTS and k == 'bridge-stp': - v = 'on' if self.default_stp_on else 'off' - else: - continue - rv = runningattrs.get(k[7:]) - if k == 'bridge-mcqv4src': - continue - if k == 'bridge-maxwait' or k == 'bridge-waitport': - ifaceobjcurr.update_config_with_status(k, v, 0) + + # Those attributes require separate handling + filter_attributes = [ + "bridge-trunk", + "bridge-ports", + "bridge-vids", + "bridge-trunk", + "bridge-mcqv4src", + "bridge-port-vids", + "bridge-port-pvids", + "bridge-l2protocol-tunnel", + "bridge-ports-condone-regex" + ] + + ignore_attributes = ( + # bridge-pvid and bridge-vids on a bridge does not correspond + # directly to a running config on the bridge. They correspond to + # default values for the bridge ports. And they are already checked + # against running config of the bridge port and reported against a + # bridge port. So, ignore these attributes under the bridge. Use '2' + # for ignore today. XXX: '2' will be mapped to a defined value in + # subsequent patches. + "bridge-pvid", + "bridge-allow-untagged", + ) + for attr in ignore_attributes: + if attr in user_config_attributes: + ifaceobjcurr.update_config_with_status(attr, ifaceobj.get_attr_value_first(attr), 2) + filter_attributes.append(attr) + + bridge_config = Set(user_config_attributes).difference(filter_attributes) + cached_ifla_info_data = self.cache.get_link_info_data(ifname) + + self._query_check_bridge_attributes(ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data) + self._query_check_brport_attributes_on_bridge(ifname, ifaceobj, ifaceobjcurr, bridge_config) + self._query_check_bridge_vidinfo(ifname, ifaceobj, ifaceobjcurr) + self._query_check_mcqv4src(ifaceobj, ifaceobjcurr) + self._query_check_l2protocol_tunnel_on_bridge(ifname, ifaceobj, ifaceobjcurr) + + def _query_check_bridge_attributes(self, ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data): + for attr in list(bridge_config): + query_check_handler, netlink_attr = self._bridge_attribute_query_check_handler.get(attr, (None, None)) + + if callable(query_check_handler): + query_check_handler(attr, ifaceobj.get_attr_value_first(attr), ifaceobjcurr, cached_ifla_info_data.get(netlink_attr)) + bridge_config.remove(attr) + + def _query_check_brport_attributes_on_bridge(self, ifname, ifaceobj, ifaceobjcurr, bridge_config): + brports_info_slave_data = {} + # bridge_config should only have bridge-port-list attributes + for attr in bridge_config: + attr_nl = self._ifla_brport_attributes_map.get(attr) + brport_query_check_handler = self._brport_attribute_query_check_handler.get(attr) + + if not attr_nl or not brport_query_check_handler: + self.logger.warning("%s: query-check: missing handler for attribute: %s (%s)" % (ifname, attr, attr_nl)) continue - if k == 'bridge-vlan-aware': - rv = self.ipcmd.bridge_is_vlan_aware(ifaceobj.name) - if (rv and v == 'yes') or (not rv and v == 'no'): - ifaceobjcurr.update_config_with_status('bridge-vlan-aware', - v, 0) - else: - ifaceobjcurr.update_config_with_status('bridge-vlan-aware', - v, 1) - elif k == 'bridge-stp': - # special case stp compare because it may - # contain more than one valid values - stp_on_vals = ['on', 'yes'] - stp_off_vals = ['off', 'no'] - 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('bridge-stp', - rv, 0) - else: - ifaceobjcurr.update_config_with_status('bridge-stp', - rv, 1) - elif k in ['bridge-pathcosts', - 'bridge-portprios', - 'bridge-portmcrouter', - 'bridge-portmcfl', - 'bridge-learning', - 'bridge-unicast-flood', - 'bridge-multicast-flood', - 'bridge-arp-nd-suppress', - ]: - if k == 'bridge-arp-nd-suppress': - brctlcmdattrname = k[7:] - else: - brctlcmdattrname = k[7:].rstrip('s') - # for port attributes, the attributes are in a list - # = - status = 0 - currstr = '' - vlist = self.parse_port_list(ifaceobj.name, v) - if not vlist: - continue - for vlistitem in vlist: - try: - (p, v) = vlistitem.split('=') - if k in ['bridge-learning', - 'bridge-unicast-flood', - 'bridge-multicast-flood', - 'bridge-arp-nd-suppress', - ]: - currv = utils.get_onoff_bool( - self.brctlcmd.get_bridgeport_attr( - ifaceobj.name, p, - brctlcmdattrname)) - else: - currv = self.brctlcmd.get_bridgeport_attr( - ifaceobj.name, p, - brctlcmdattrname) - if currv: - currstr += ' %s=%s' %(p, currv) - else: - currstr += ' %s=%s' %(p, 'None') - - if k == 'bridge-portmcrouter': - if self._ifla_brport_multicast_router_dict_to_int.get(v) != int(currv): - status = 1 - elif currv != v: - status = 1 - except Exception, e: - self.log_warn(str(e)) - pass - ifaceobjcurr.update_config_with_status(k, currstr, status) - elif k == 'bridge-vlan-stats' or k == 'bridge-mcstats': - rv = utils.get_onff_from_onezero(rv) - if v != rv: - ifaceobjcurr.update_config_with_status(k, rv, 1) + + running_config = [] + status = 0 + + for port_config in self.parse_port_list(ifname, ifaceobj.get_attr_value_first(attr)) or []: + port, config = port_config.split("=") + + if not port in brports_info_slave_data: + info_slave_data = brports_info_slave_data[port] = self.cache.get_link_info_slave_data(port) else: - ifaceobjcurr.update_config_with_status(k, rv, 0) - elif not rv: - if k == 'bridge-pvid' or k == 'bridge-vids' or k == 'bridge-trunk' or k == 'bridge-allow-untagged': - # bridge-pvid and bridge-vids on a bridge does - # not correspond directly to a running config - # on the bridge. They correspond to default - # values for the bridge ports. And they are - # already checked against running config of the - # bridge port and reported against a bridge port. - # So, ignore these attributes under the bridge. - # Use '2' for ignore today. XXX: '2' will be - # mapped to a defined value in subsequent patches. - ifaceobjcurr.update_config_with_status(k, v, 2) - else: - ifaceobjcurr.update_config_with_status(k, 'notfound', 1) - continue - elif v.upper() != rv.upper(): - ifaceobjcurr.update_config_with_status(k, rv, 1) + info_slave_data = brports_info_slave_data[port] + + port_config, port_status = brport_query_check_handler(port, config, info_slave_data.get(attr_nl)) + + running_config.append(port_config) + + if port_status: + status = 1 + + ifaceobjcurr.update_config_with_status( + attr, + " ".join(running_config), + status + ) + + @staticmethod + def _query_check_br_attr_wait(attr, wait_value, ifaceobjcurr, __): + ifaceobjcurr.update_config_with_status(attr, wait_value, 0) + + def _query_check_br_attr_stp(self, attr, stp_value, ifaceobjcurr, cached_value): + if not stp_value: + if ifupdownflags.flags.WITHDEFAULTS: + stp_value = "on" if self.default_stp_on else "off" else: - ifaceobjcurr.update_config_with_status(k, rv, 0) + return - self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr) + user_config_to_nl = utils.get_boolean_from_string(stp_value) - self._query_check_mcqv4src(ifaceobj, ifaceobjcurr) - self._query_check_l2protocol_tunnel_on_bridge(ifaceobj, ifaceobjcurr, runningattrs) + ifaceobjcurr.update_config_with_status( + attr, + "yes" if cached_value else "no", + user_config_to_nl != bool(cached_value) + ) + + @staticmethod + def _query_check_br_attr_int(attr, user_config, ifaceobjcurr, cached_value): + ifaceobjcurr.update_config_with_status( + attr, + str(cached_value), + int(user_config) != cached_value + ) + + @staticmethod + def _query_check_br_attr_int_divided100(attr, user_config, ifaceobjcurr, cached_value): + value = cached_value / 100 + ifaceobjcurr.update_config_with_status( + attr, + str(value), + int(user_config) != value + ) + + @staticmethod + def _query_check_br_attr_boolean(attr, user_config, ifaceobjcurr, cached_value): + ifaceobjcurr.update_config_with_status( + attr, + "yes" if cached_value else "no", + utils.get_boolean_from_string(user_config) != cached_value + ) + + @staticmethod + def _query_check_br_attr_boolean_on_off(attr, user_config, ifaceobjcurr, cached_value): + ifaceobjcurr.update_config_with_status( + attr, + "on" if cached_value else "off", + utils.get_boolean_from_string(user_config) != cached_value + ) + + @staticmethod + def _query_check_br_attr_string(attr, user_config, ifaceobjcurr, cached_value): + ifaceobjcurr.update_config_with_status( + attr, + cached_value, + user_config.lower() != cached_value + ) + + @staticmethod + def _query_check_brport_attr_boolean_on_off(port, user_config, cached_value): + return "%s=%s" % (port, "on" if cached_value else "off"), utils.get_boolean_from_string(user_config) != cached_value + + @staticmethod + def _query_check_brport_attr_boolean_yes_no(port, user_config, cached_value): + return "%s=%s" % (port, "yes" if cached_value else "no"), utils.get_boolean_from_string(user_config) != cached_value + + @staticmethod + def _query_check_brport_attr_int(port, user_config, cached_value): + return "%s=%s" % (port, cached_value), int(user_config) != cached_value + + @classmethod + def _query_check_brport_attr_portmcrouter(cls, port, user_config, cached_value): + return ( + "%s=%s" % (port, cls._ifla_brport_multicast_router_dict_int_to_str.get(cached_value)), + cls._ifla_brport_multicast_router_dict_to_int.get(user_config) != cached_value + ) + + #################################################################################################################### def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc): bridge_all_ports = [] @@ -2833,55 +3112,67 @@ def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc): break return pvid - def _get_bridge_name(self, ifaceobj): - return self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name) - - def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr, - ifaceobj_getfunc, bridgename): - attr_name = 'bridge-access' - vid = ifaceobj.get_attr_value_first(attr_name) - if vid: - (running_vids, running_pvid) = self._get_running_vids_n_pvid_str( - ifaceobj.name) - if (not running_pvid or running_pvid != vid or - (running_vids and running_vids[0] != vid)): - ifaceobjcurr.update_config_with_status(attr_name, - running_pvid, 1) - else: - ifaceobjcurr.update_config_with_status(attr_name, vid, 0) + def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc): + running_pvid, running_vids = self.cache.get_pvid_and_vids(ifname) + + # + # bridge-access + # + brport_vid_access_user_config = ifaceobj.get_attr_value_first("bridge-access") + + if brport_vid_access_user_config: + try: + vid_int = int(brport_vid_access_user_config) + except ValueError as e: + ifaceobjcurr.update_config_with_status("bridge-access", brport_vid_access_user_config, 1) + raise Exception("%s: bridge-access invalid value: %s" % (ifname, str(e))) + + ifaceobjcurr.update_config_with_status( + "bridge-access", + str(running_pvid), + running_pvid != vid_int or running_vids[0] != vid_int + ) return - (running_vids, running_pvid) = self._get_running_vids_n_pvid_str( - ifaceobj.name) - attr_name = 'bridge-pvid' - pvid = ifaceobj.get_attr_value_first('bridge-pvid') - if pvid: - if running_pvid and running_pvid == pvid: - ifaceobjcurr.update_config_with_status(attr_name, - running_pvid, 0) - else: - ifaceobjcurr.update_config_with_status(attr_name, - running_pvid, 1) + # + # bridge-pvid + # + brport_pvid_user_config = ifaceobj.get_attr_value_first("bridge-pvid") + + if brport_pvid_user_config: + try: + pvid = int(brport_pvid_user_config) + except ValueError as e: + ifaceobjcurr.update_config_with_status("bridge-pvid", brport_pvid_user_config, 1) + raise Exception("%s: bridge-pvid invalid value: %s" % (ifname, str(e))) + + ifaceobjcurr.update_config_with_status( + "bridge-pvid", + str(running_pvid), + running_pvid != pvid + ) elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or ((ifaceobj.flags & iface.HAS_SIBLINGS) and (ifaceobj.flags & iface.OLDEST_SIBLING))): # if the interface has multiple iface sections, # we check the below only for the oldest sibling # or the last iface section - pvid = self._get_bridge_pvid(bridgename, ifaceobj_getfunc) + try: + pvid = int(self._get_bridge_pvid(bridge_name, ifaceobj_getfunc)) + except (TypeError, ValueError): + pvid = 0 if pvid: if not running_pvid or running_pvid != pvid: ifaceobjcurr.status = ifaceStatus.ERROR ifaceobjcurr.status_str = 'bridge pvid error' - elif not running_pvid or running_pvid != '1': + elif not running_pvid or running_pvid != 1: ifaceobjcurr.status = ifaceStatus.ERROR ifaceobjcurr.status_str = 'bridge pvid error' attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj) if vids: vids = re.split(r'[\s\t]\s*', vids) - if not running_vids or not self._compare_vids(vids, running_vids, - running_pvid): + if not running_vids or not self._compare_vids(vids, running_vids, running_pvid, expand_range=False): ifaceobjcurr.update_config_with_status(attr_name, ' '.join(running_vids), 1) else: @@ -2895,80 +3186,130 @@ def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr, # or the last iface section # check if it matches the bridge vids - bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc) + bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc) if (bridge_vids and (not running_vids or - not self._compare_vids(bridge_vids, running_vids, running_pvid))): + not self._compare_vids(bridge_vids, running_vids, running_pvid, expand_range=False))): ifaceobjcurr.status = ifaceStatus.ERROR ifaceobjcurr.status_str = 'bridge vid error' + _query_check_brport_attributes = ( + "bridge-pvid", + "bridge-vids", + "bridge-trunk", + "bridge-access", + "bridge-pathcosts", + "bridge-portprios", + "bridge-portmcrouter", + "bridge-learning", + "bridge-portmcfl", + "bridge-unicast-flood", + "bridge-multicast-flood", + "bridge-broadcast-flood", + "bridge-arp-nd-suppress", + "bridge-l2protocol-tunnel" + ) + def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc): - if not self._is_bridge_port(ifaceobj): - # Mark all bridge attributes as failed - ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, - ['bridge-vids', 'bridge-trunk', 'bridge-pvid', 'bridge-access', - 'bridge-pathcosts', 'bridge-portprios', - 'bridge-portmcrouter', - 'bridge-learning', - 'bridge-portmcfl', 'bridge-unicast-flood', - 'bridge-multicast-flood', - 'bridge-arp-nd-suppress', 'bridge-l2protocol-tunnel' - ], 1) + + ifname = ifaceobj.name + + if not self.cache.link_is_bridge_port(ifname): + # Mark all bridge brport attributes as failed + ifaceobjcurr.check_n_update_config_with_status_many( + ifaceobj, self._query_check_brport_attributes, 1 + ) return - bridgename = self._get_bridge_name(ifaceobj) - if not bridgename: - self.logger.warn('%s: unable to determine bridge name' - %ifaceobj.name) + + bridge_name = self.cache.get_bridge_name_from_port(ifname) + if not bridge_name: + self.logger.warn("%s: unable to determine bridge name" % ifname) return - if self.ipcmd.bridge_is_vlan_aware(bridgename): - self._query_check_bridge_port_vidinfo(ifaceobj, ifaceobjcurr, - ifaceobj_getfunc, - bridgename) - for attr, dstattr in {'bridge-pathcosts' : 'pathcost', - 'bridge-portprios' : 'portprio', - 'bridge-portmcrouter' : 'portmcrouter', - 'bridge-portmcfl' : 'portmcfl', - 'bridge-learning' : 'learning', - 'bridge-unicast-flood' : 'unicast-flood', - 'bridge-multicast-flood' : 'multicast-flood', - 'bridge-arp-nd-suppress' : 'arp-nd-suppress', - }.items(): - attrval = ifaceobj.get_attr_value_first(attr) - if not attrval: + if self.cache.bridge_is_vlan_aware(bridge_name): + self._query_check_bridge_port_vidinfo(ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc) + + brport_info_slave_data = self.cache.get_link_info_slave_data(ifname) + + # + # bridge-portmcfl + # + portmcfl = ifaceobj.get_attr_value_first("bridge-portmcfl") + + if portmcfl: + cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE) + + ifaceobjcurr.update_config_with_status( + "bridge-portmcfl", + "yes" if cached_value else "no", + utils.get_boolean_from_string(portmcfl) != cached_value + ) + + # + # bridge-portmcrouter + # + portmcrouter = ifaceobj.get_attr_value_first("bridge-portmcrouter") + + if portmcrouter: + cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER) + + ifaceobjcurr.update_config_with_status( + "bridge-portmcrouter", + self._ifla_brport_multicast_router_dict_int_to_str.get(cached_value), + self._ifla_brport_multicast_router_dict_to_int.get(portmcrouter) != cached_value + ) + + # + # bridge-learning + # bridge-unicast-flood + # bridge-multicast-flood + # bridge-broadcast-flood + # bridge-arp-nd-suppress + # + for attr_name, attr_nl in ( + ("bridge-learning", Link.IFLA_BRPORT_LEARNING), + ("bridge-unicast-flood", Link.IFLA_BRPORT_UNICAST_FLOOD), + ("bridge-multicast-flood", Link.IFLA_BRPORT_MCAST_FLOOD), + ("bridge-broadcast-flood", Link.IFLA_BRPORT_BCAST_FLOOD), + ("bridge-arp-nd-suppress", Link.IFLA_BRPORT_NEIGH_SUPPRESS), + ): + attribute_value = ifaceobj.get_attr_value_first(attr_name) + + if not attribute_value: + continue + + cached_value = brport_info_slave_data.get(attr_nl) + + ifaceobjcurr.update_config_with_status( + attr_name, + "on" if cached_value else "off", + utils.get_boolean_from_string(attribute_value) != cached_value + ) + + # + # bridge-pathcosts + # bridge-portprios + # + for attr_name, attr_nl in ( + ("bridge-pathcosts", Link.IFLA_BRPORT_COST), + ("bridge-portprios", Link.IFLA_BRPORT_PRIORITY), + ): + attribute_value = ifaceobj.get_attr_value_first(attr_name) + + if not attribute_value: continue + cached_value = brport_info_slave_data.get(attr_nl) + try: - running_attrval = self.brctlcmd.get_bridgeport_attr( - bridgename, ifaceobj.name, dstattr) - - if dstattr == 'portmcfl': - if not utils.is_binary_bool(attrval) and running_attrval: - running_attrval = utils.get_yesno_boolean( - utils.get_boolean_from_string(running_attrval)) - elif dstattr == 'portmcrouter': - if self._ifla_brport_multicast_router_dict_to_int.get(attrval) == int(running_attrval): - ifaceobjcurr.update_config_with_status(attr, attrval, 0) - else: - ifaceobjcurr.update_config_with_status(attr, attrval, 1) - continue - elif dstattr in ['learning', - 'unicast-flood', - 'multicast-flood', - 'arp-nd-suppress', - ]: - if not utils.is_binary_bool(attrval) and running_attrval: - running_attrval = utils.get_onff_from_onezero( - running_attrval) - - if running_attrval != attrval: - ifaceobjcurr.update_config_with_status(attr, - running_attrval, 1) - else: - ifaceobjcurr.update_config_with_status(attr, - running_attrval, 0) - except Exception, e: - self.log_warn('%s: %s' %(ifaceobj.name, str(e))) + ifaceobjcurr.update_config_with_status( + attr_name, + str(cached_value), + int(attribute_value) != cached_value + ) + except ValueError as e: + ifaceobjcurr.update_config_with_status(attr_name, str(cached_value), 1) + raise Exception("%s: %s invalid value: %s" % (ifname, attr_name, str(e))) self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr) @@ -2984,7 +3325,7 @@ def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr): result = 1 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result) - def _query_check_l2protocol_tunnel_on_bridge(self, ifaceobj, ifaceobjcurr, bridge_running_attrs): + def _query_check_l2protocol_tunnel_on_bridge(self, ifname, ifaceobj, ifaceobjcurr): """ In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport We need to make sure that all ports comply with the mask given under the bridge @@ -3001,8 +3342,8 @@ def _query_check_l2protocol_tunnel_on_bridge(self, ifaceobj, ifaceobjcurr, bridg return else: config_per_port_dict = {} - brport_list = bridge_running_attrs.get('ports', {}).keys() - result = 1 + brport_list = self.cache.get_slaves(ifname) + try: for brport_name in brport_list: self._query_check_l2protocol_tunnel( @@ -3016,8 +3357,8 @@ def _query_check_l2protocol_tunnel_on_bridge(self, ifaceobj, ifaceobjcurr, bridg ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result) def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel): - cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI]) - cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK]) + cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI) + cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK) for protocol in re.split(',|\s*', user_config_l2protocol_tunnel): callback = self.query_check_l2protocol_tunnel_callback.get(protocol) @@ -3028,8 +3369,8 @@ def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tun % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi)) def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None): - cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI]) - cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK]) + cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI) + cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK) running_protocols = [] for protocol_name, callback in self.query_check_l2protocol_tunnel_callback.items(): if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi): @@ -3057,7 +3398,7 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): ifaceobj_getfunc) def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc): - if self.ipcmd.bridge_is_vlan_aware(ifaceobjrunning.name): + if self.cache.bridge_is_vlan_aware(ifaceobjrunning.name): ifaceobjrunning.update_config('bridge-vlan-aware', 'yes') ifaceobjrunning.update_config_dict(self._query_running_attrs( ifaceobjrunning, @@ -3071,18 +3412,18 @@ def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename): if self.systcl_get_net_bridge_stp_user_space() == '1': return - v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name) + v = str(self.cache.get_brport_cost(ifaceobjrunning.name)) if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'): ifaceobjrunning.update_config('bridge-pathcosts', v) - v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name) + v = str(self.cache.get_brport_priority(ifaceobjrunning.name)) if v and v != self.get_mod_subattr('bridge-portprios', 'default'): ifaceobjrunning.update_config('bridge-portprios', v) def _query_running_bridge_port(self, ifaceobjrunning, ifaceobj_getfunc=None): - bridgename = self.ipcmd.bridge_port_get_bridge_name( + bridgename = self.cache.get_bridge_name_from_port( ifaceobjrunning.name) bridge_vids = None bridge_pvid = None @@ -3091,7 +3432,7 @@ def _query_running_bridge_port(self, ifaceobjrunning, %ifaceobjrunning.name) return - if not self.ipcmd.bridge_is_vlan_aware(bridgename): + if not self.cache.bridge_is_vlan_aware(bridgename): try: self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0]) except Exception as e: @@ -3125,31 +3466,23 @@ def _query_running_bridge_port(self, ifaceobjrunning, ifaceobjrunning.update_config('bridge-pvid', bridge_port_pvid) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, - 'learning')) + v = utils.get_onff_from_onezero(self.cache.get_brport_learning(ifaceobjrunning.name)) if v and v != self.get_mod_subattr('bridge-learning', 'default'): ifaceobjrunning.update_config('bridge-learning', v) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, - 'unicast-flood')) + v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(ifaceobjrunning.name)) if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'): ifaceobjrunning.update_config('bridge-unicast-flood', v) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, - 'multicast-flood')) + v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(ifaceobjrunning.name)) if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'): ifaceobjrunning.update_config('bridge-multicast-flood', v) - v = utils.get_onff_from_onezero( - self.brctlcmd.get_bridgeport_attr(bridgename, - ifaceobjrunning.name, - 'arp-nd-suppress')) + v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(ifaceobjrunning.name)) + if v and v != self.get_mod_subattr('bridge-broadcast-flood', 'default'): + ifaceobjrunning.update_config('bridge-broadcast-flood', v) + + v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(ifaceobjrunning.name)) # Display running 'arp-nd-suppress' only on vxlan ports # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes' # otherwise, display on all bridge-ports @@ -3166,9 +3499,9 @@ def _query_running_bridge_port(self, ifaceobjrunning, def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): try: - if self.brctlcmd.bridge_exists(ifaceobjrunning.name): + if self.cache.bridge_exists(ifaceobjrunning.name): self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc) - elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name): + elif self.cache.link_is_bridge_port(ifaceobjrunning.name): self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc) except Exception as e: raise Exception('%s: %s' % (ifaceobjrunning.name, str(e))) @@ -3181,47 +3514,52 @@ def _query(self, ifaceobj, **kwargs): if self.default_stp_on: ifaceobj.update_config('bridge-stp', 'yes') - def _query_check_support_yesno_attrs(self, runningattrs, ifaceobj): - for attrl in [['mcqifaddr', 'bridge-mcqifaddr'], - ['mcquerier', 'bridge-mcquerier'], - ['mcsnoop', 'bridge-mcsnoop']]: - value = ifaceobj.get_attr_value_first(attrl[1]) - if value and not utils.is_binary_bool(value): - if attrl[0] in runningattrs: - bool = utils.get_boolean_from_string(runningattrs[attrl[0]]) - runningattrs[attrl[0]] = utils.get_yesno_boolean(bool) - - self._query_check_mcrouter(ifaceobj, runningattrs) - self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'portmcfl', ifaceobj.get_attr_value_first('bridge-portmcfl')) - self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'learning', ifaceobj.get_attr_value_first('bridge-learning')) - self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'unicast-flood', ifaceobj.get_attr_value_first('bridge-unicast-flood')) - self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'multicast-flood', ifaceobj.get_attr_value_first('bridge-multicast-flood')) - self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'arp-nd-suppress', ifaceobj.get_attr_value_first('bridge-arp-nd-suppress')) - - def _query_check_mcrouter(self, ifaceobj, running_attrs): + def __re_evaluate_bridge_vxlan(self, ifaceobj, ifaceobj_getfunc=None): """ - bridge-mcrouter and bridge-portmcrouter supports: yes-no-0-1-2 + Quick fix for BRIDGE_VXLAN + + BRIDGE_VXLAN is not set on the bridge because the VXLAN hasn't been processed yet + (because its defined after the bridge in /e/n/i), here is what happens: + + - ifupdownmain:populate_dependency_info() + - loops over all the intf from /e/n/i (with the example config: + ['lo', 'eth0', 'swp1', 'swp2', 'bridge', 'vni-10', 'bridge.100', 'vlan100']) + ----> bridge is first in the list of interface (that we care about) + + - ifupdownmain:query_lowerifaces() + - bridge:get_dependent is called (debug: bridge: evaluating port expr '['swp1', 'swp2', 'vni-10']') + - ifupdownmain:preprocess_dependency_list() + - calls ifupdownmain:_set_iface_role_n_kind() on all the brports: + + in _set_iface_role_n_kind: + ifaceobj is the brport + upperifaceobj is the bridge + + it tries to see if the bridge has a VXLAN: + + if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \ + and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE): + upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN + + but because the bridge is first in the /e/n/i ifupdown2 didn't + call vxlan:get_dependent_ifacenames so VXLAN is not set on ifaceobj + + :return: """ - if 'mcrouter' in running_attrs: - value = ifaceobj.get_attr_value_first('bridge-mcrouter') - if value: - try: - int(value) - except: - running_attrs['mcrouter'] = 'yes' if utils.get_boolean_from_string(running_attrs['mcrouter']) else 'no' + if not ifaceobj_getfunc: + return - def _query_check_support_yesno_attr_port(self, runningattrs, ifaceobj, attr, attrval): - if attrval: - portlist = self.parse_port_list(ifaceobj.name, attrval) - if portlist: - to_convert = [] - for p in portlist: - (port, val) = p.split('=') - if not utils.is_binary_bool(val): - to_convert.append(port) - for port in to_convert: - runningattrs['ports'][port][attr] = utils.get_yesno_boolean( - utils.get_boolean_from_string(runningattrs['ports'][port][attr])) + if ifaceobj.link_kind & ifaceLinkKind.BRIDGE and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN: + for port in self._get_bridge_port_list(ifaceobj) or []: + for brport_ifaceobj in ifaceobj_getfunc(port): + if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN: + ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN + return + + elif ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN: + for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []: + for bridge_ifaceobj in ifaceobj_getfunc(iface) or []: + bridge_ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN _run_ops = { 'pre-up': _up, @@ -3235,10 +3573,6 @@ 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, ifaceobj_getfunc=None): """ run bridge configuration on the interface object passed as argument. Can create bridge interfaces if they dont exist already @@ -3260,14 +3594,16 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): op_handler = self._run_ops.get(operation) if not op_handler: return - self._init_command_handlers() - if (not LinkUtils.bridge_utils_is_installed + if (not self.requirements.bridge_utils_is_installed and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE) - and LinkUtils.bridge_utils_missing_warning): + and self.bridge_utils_missing_warning): self.logger.warning('%s: missing - bridge operation may not work as expected. ' 'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd) - LinkUtils.bridge_utils_missing_warning = False + self.bridge_utils_missing_warning = False + + # make sure BRIDGE_VXLAN is set if we have a vxlan port + self.__re_evaluate_bridge_vxlan(ifaceobj, ifaceobj_getfunc) if operation == 'query-checkcurr': op_handler(self, ifaceobj, query_ifaceobj, diff --git a/ifupdown2/addons/bridgevlan.py b/ifupdown2/addons/bridgevlan.py index 3cc4fb8b..9d47c2e4 100644 --- a/ifupdown2/addons/bridgevlan.py +++ b/ifupdown2/addons/bridgevlan.py @@ -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' : ['', ], - '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": [""], + "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 @@ def _up(self, ifaceobj): (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 @@ def _up(self, ifaceobj): 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr): 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index 30886b42..4d667bc4 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -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 @@ 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 @@ 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 @@ def _up(self, ifaceobj): 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 @@ def _up(self, ifaceobj): #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 @@ def _dhcp_down(self, ifaceobj): 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr): 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 @@ 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 dhcp configuration on the interface object passed as argument @@ -262,7 +232,6 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py index c4f9aa17..09ea519f 100644 --- a/ifupdown2/addons/ethtool.py +++ b/ifupdown2/addons/ethtool.py @@ -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 @@ 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 @@ 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 @@ def do_fec_settings(self, ifaceobj): 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 @@ def do_fec_settings(self, ifaceobj): (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 @@ def do_speed_settings(self, ifaceobj, operation='post_up'): 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 @@ def get_fec_encoding(self,ethtool_output=None): """ 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 @@ def get_running_attr(self,attr='',ifaceobj=None): (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 @@ def _query_running(self, ifaceobj, ifaceobj_getfunc=None): """ # 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 @@ 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 ethtool configuration on the interface object passed as argument @@ -423,7 +434,6 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/addons/link.py b/ifupdown2/addons/link.py index 3aaa969a..2c5f33e9 100644 --- a/ifupdown2/addons/link.py +++ b/ifupdown2/addons/link.py @@ -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 ']}, - '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 "] + }, + "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 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr): 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 324de02e..8727dd53 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -9,27 +9,27 @@ 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 @@ 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': [''], - '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' : [''], - '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' : [''], - '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' : [''], - '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' : [''], - 'required' : False, - 'example' : ['under the bridge: mstpctl-bpduguard swp1=yes swp2=no', - 'under the port (recommended): mstpctl-bpduguard yes']}, - 'mstpctl-treeportprio' : - { 'help': 'Sets the \'s priority MSTI instance. ' - 'The priority value must be a number between 0 and 240 and a multiple of 16.', - 'default' : '128', - 'validvals': [''], - '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' : [''], - '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' : [''], - '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' : [''], - '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' : [''], - '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": [""], + "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": [""], + "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": [""], + "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": [""], + "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": [""], + "required": False, + "example": [ + "under the bridge: mstpctl-bpduguard swp1=yes swp2=no", + "under the port (recommended): mstpctl-bpduguard yes" + ] + }, + "mstpctl-treeportprio": { + "help": "Sets the 's priority MSTI instance. " + "The priority value must be a number between 0 and 240 " + "and a multiple of 16.", + "default": "128", + "validvals": [""], + "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": [""], + "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": [""], + "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": [""], + "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": [""], + "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 @@ def syntax_check(self, ifaceobj, ifaceobj_getfunc): 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 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): 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 @@ def _add_ports(self, ifaceobj): 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 @@ def _add_ports(self, ifaceobj): 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 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): 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 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): 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 @@ def _get_default_val(self, attr, ifaceobj, bridgeifaceobj): 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 @@ def _apply_bridge_port_settings(self, ifaceobj, bridgename=None, %(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 @@ def _apply_bridge_port_settings_all(self, ifaceobj, 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 @@ def _apply_bridge_port_settings_all(self, ifaceobj, 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 @@ def _is_running_userspace_stp_state_on(self, bridgename): 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 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _query_running_attrs(self, ifaceobjrunning, bridge_vlan_aware=False): 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 @@ def _get_config_stp(self, ifaceobj): 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 @@ def _query_check_bridge(self, ifaceobj, ifaceobjcurr, 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 @@ def _query_check_bridge(self, ifaceobj, ifaceobjcurr, # 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 @@ def _query_check_bridge_vxlan_port(self, ifaceobj, ifaceobjcurr, 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 @@ def _query_check_bridge_vxlan_port(self, ifaceobj, ifaceobjcurr, 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 @@ def _query_bridge_port_attr(self, ifaceobjrunning, bridgename, attr, value_cmp): 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 @@ def _query_running_bridge_port(self, ifaceobjrunning): # 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 @@ def _query_running_bridge(self, ifaceobjrunning): 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 @@ def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs): 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() diff --git a/ifupdown2/addons/ppp.py b/ifupdown2/addons/ppp.py index 014600bd..628433c8 100644 --- a/ifupdown2/addons/ppp.py +++ b/ifupdown2/addons/ppp.py @@ -4,28 +4,28 @@ 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 @@ def _up(self, ifaceobj): # 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 @@ def _up(self, ifaceobj): 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 @@ def _down(self, ifaceobj): # 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 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): 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 @@ def _query_running(self, ifaceobjrunning): 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/addons/tunnel.py b/ifupdown2/addons/tunnel.py index 611a4715..63f8d985 100644 --- a/ifupdown2/addons/tunnel.py +++ b/ifupdown2/addons/tunnel.py @@ -4,18 +4,20 @@ # -- Mon 10 Oct 2016 10:53:13 PM CEST # try: + from ifupdown2.lib.addon import Addon + from ifupdown2.nlmanager.nlmanager import Link + from ifupdown2.ifupdown.iface import * - from ifupdown2.ifupdown.netlink import netlink - 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 nlmanager.nlmanager import Link + from ifupdown.iface import * - from ifupdown.netlink import netlink - from ifupdownaddons.LinkUtils import LinkUtils from ifupdownaddons.modulebase import moduleBase import ifupdown.ifupdownflags as ifupdownflags @@ -24,38 +26,38 @@ # # 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': ['', ''], 'required': True, 'example': ['local 192.2.0.42'] }, - 'endpoint': { + 'tunnel-endpoint': { 'help': 'IP of remote tunnel endpoint', 'validvals': ['', ''], 'required': True, 'example': ['endpoint 192.2.0.23'] }, - 'ttl': { + 'tunnel-ttl': { 'help': 'TTL for tunnel packets', 'validvals': [''], 'required': False, 'example': ['ttl 64'] }, - 'tunnel-physdev': { + 'tunnel-dev': { 'help': 'Physical underlay device to use for tunnel packets', 'validvals': [''], '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 @@ def _up(self, ifaceobj): # 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 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): 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 @@ def _query_check_n_update(ifaceobj, ifaceobjcurr, attrname, attrval, running_att 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr): 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index caa19414..86928fd2 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -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: - import ifupdown.ifupdownflags as ifupdownflags - + from lib.addon import Addon from ifupdown.iface import * - from ifupdown.netlink import netlink - - from ifupdownaddons.LinkUtils import LinkUtils + from nlmanager.nlmanager import Link from ifupdownaddons.modulebase import moduleBase + import ifupdown.ifupdownflags as ifupdownflags - -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': ['']}, - '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": [""] + }, + "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 @@ def _is_vlan_device(self, ifaceobj): 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 @@ def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None): 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 @@ def _up(self, ifaceobj): 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 @@ def _up(self, ifaceobj): 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 @@ def _down(self, ifaceobj): 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}) - - _run_ops = {'pre-up' : _up, - 'post-down' : _down, - 'query-checkcurr' : _query_check, - 'query-running' : _query_running} + if '.' in ifname: + return + + 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index 5d7f562e..c08088b5 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -13,69 +13,86 @@ 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): + +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', ''], - 'example': ['vrf-table auto', 'vrf-table 1001']}, - 'vrf': - {'help' : 'vrf the interface is part of.', - 'validvals': [''], - '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' + + _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", ""], + "example": ["vrf-table auto", "vrf-table 1001"] + }, + "vrf": { + "help": "vrf the interface is part of", + "validvals": [""], + "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 @@ def __init__(self, *args, **kargs): 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 @@ def _iproute2_vrf_map_initialize(self, writetodisk=True): 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 @@ def _iproute2_vrf_map_initialize(self, writetodisk=True): 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 @@ def _iproute2_vrf_map_initialize(self, writetodisk=True): 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 @@ def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False): 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 @@ def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id): 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 @@ def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj, vrf_master_ 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 @@ def enable_ipv6_if_prev_brport(self, ifname): 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 @@ def _down_dhcp_slave(self, ifaceobj, vrfname): 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 @@ def _down_dhcp_slave(self, ifaceobj, vrfname): 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 @@ def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None, 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 @@ def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None, # 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 @@ def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None, 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 @@ def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves, # - check if it is also a macvlan device of the # format -v 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 @@ def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _check_vrf_dev_processed_flag(self, ifaceobj): 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 @@ def _create_vrf_dev(self, ifaceobj, vrf_table): 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 @@ def _create_vrf_dev(self, ifaceobj, vrf_table): 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 @@ def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True, 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 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _down_vrf_helper(self, ifaceobj, vrf_table): 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 @@ def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): 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 @@ def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): 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 @@ def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): 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 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): 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 @@ def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf): 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 @@ def _query_check(self, ifaceobj, ifaceobjcurr): 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 @@ def _query(self, ifaceobj, **kwargs): 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, 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 diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index 5f409c8c..d3ade8e2 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -6,112 +6,150 @@ from sets import Set -from ipaddr import IPNetwork, IPv4Address, IPv4Network, AddressValueError +from ipaddr import IPNetwork, IPAddress, IPv4Address, IPv4Network, AddressValueError try: import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags + from ifupdown2.lib.addon import Addon + from ifupdown2.lib.nlcache import NetlinkCacheIfnameNotFoundError + 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.ifupdownaddons.cache import * - from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils from ifupdown2.ifupdownaddons.modulebase import moduleBase except ImportError: import ifupdown.policymanager as policymanager import ifupdown.ifupdownflags as ifupdownflags + from lib.addon import Addon + from lib.nlcache import NetlinkCacheIfnameNotFoundError + from nlmanager.nlmanager import Link from ifupdown.iface import * from ifupdown.utils import utils - from ifupdown.netlink import netlink from ifupdownaddons.cache import * - from ifupdownaddons.LinkUtils import LinkUtils from ifupdownaddons.modulebase import moduleBase -class vxlan(moduleBase): - _modinfo = {'mhelp' : 'vxlan module configures vxlan interfaces.', - 'attrs' : { - 'vxlan-id' : - {'help' : 'vxlan id', - 'validrange' : ['1', '16777214'], - 'required' : True, - 'example': ['vxlan-id 100']}, - 'vxlan-local-tunnelip' : - {'help' : 'vxlan local tunnel ip', - 'validvals' : [''], - 'example': ['vxlan-local-tunnelip 172.16.20.103']}, - 'vxlan-svcnodeip' : - {'help' : 'vxlan id', - 'validvals' : [''], - 'example': ['vxlan-svcnodeip 172.16.22.125']}, - 'vxlan-remoteip' : - {'help' : 'vxlan remote ip', - 'validvals' : [''], - 'example': ['vxlan-remoteip 172.16.22.127'], - 'multiline': True}, - 'vxlan-learning' : - {'help' : 'vxlan learning yes/no', - 'validvals' : ['yes', 'no', 'on', 'off'], - 'example': ['vxlan-learning no'], - 'default': 'yes'}, - 'vxlan-ageing' : - {'help' : 'vxlan aging timer', - 'validrange' : ['0', '4096'], - 'example': ['vxlan-ageing 300'], - 'default': '300'}, - 'vxlan-purge-remotes' : - {'help' : 'vxlan purge existing remote entries', - 'validvals' : ['yes', 'no'], - 'example': ['vxlan-purge-remotes yes'],}, - 'vxlan-port': { - 'help': 'vxlan UDP port (transmitted to vxlan driver)', - 'example': ['vxlan-port 4789'], - 'validrange': ['1', '65536'], - 'default': '4789', - }, - 'vxlan-physdev': - {'help': 'vxlan physical device', - 'example': ['vxlan-physdev eth1']}, - "vxlan-ttl": { - "help": "specifies the TTL value to use in outgoing packets (range 1..255)", - "validvals": ['', 'auto'], - "example": ['vxlan-ttl 42'], - } - }} - _clagd_vxlan_anycast_ip = "" - _vxlan_local_tunnelip = None +class vxlan(Addon, moduleBase): + _modinfo = { + "mhelp": "vxlan module configures vxlan interfaces.", + "attrs": { + "vxlan-id": { + "help": "vxlan id", + "validrange": ["1", "16777214"], + "required": True, + "example": ["vxlan-id 100"] + }, + "vxlan-local-tunnelip": { + "help": "vxlan local tunnel ip", + "validvals": [""], + "example": ["vxlan-local-tunnelip 172.16.20.103"] + }, + "vxlan-svcnodeip": { + "help": "vxlan id", + "validvals": [""], + "example": ["vxlan-svcnodeip 172.16.22.125"] + }, + "vxlan-remoteip": { + "help": "vxlan remote ip", + "validvals": [""], + "example": ["vxlan-remoteip 172.16.22.127"], + "multiline": True + }, + "vxlan-learning": { + "help": "vxlan learning yes/no", + "validvals": ["yes", "no", "on", "off"], + "example": ["vxlan-learning no"], + "default": "yes" + }, + "vxlan-ageing": { + "help": "vxlan aging timer", + "validrange": ["0", "4096"], + "example": ["vxlan-ageing 300"], + "default": "300" + }, + "vxlan-purge-remotes": { + "help": "vxlan purge existing remote entries", + "validvals": ["yes", "no"], + "example": ["vxlan-purge-remotes yes"], + }, + "vxlan-port": { + "help": "vxlan UDP port (transmitted to vxlan driver)", + "example": ["vxlan-port 4789"], + "validrange": ["1", "65536"], + "default": "4789", + }, + "vxlan-physdev": { + "help": "vxlan physical device", + "example": ["vxlan-physdev eth1"] + }, + "vxlan-ttl": { + "help": "specifies the TTL value to use in outgoing packets " + "(range 0..255), 0=auto", + "default": "0", + "validvals": ["0", "255"], + "example": ['vxlan-ttl 42'], + }, + "vxlan-mcastgrp": { + "help": "vxlan multicast group", + "validvals": [""], + "example": ["vxlan-mcastgrp 172.16.22.127"], + } + } + } + + VXLAN_PHYSDEV_MCASTGRP_DEFAULT = "ipmr-lo" def __init__(self, *args, **kargs): + Addon.__init__(self) moduleBase.__init__(self, *args, **kargs) - self.ipcmd = None - purge_remotes = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vxlan-purge-remotes') - if purge_remotes: - self._purge_remotes = utils.get_boolean_from_string(purge_remotes) - else: - self._purge_remotes = False + + self._vxlan_purge_remotes = utils.get_boolean_from_string( + policymanager.policymanager_api.get_module_globals( + module_name=self.__class__.__name__, + attr="vxlan-purge-remotes" + ) + ) + self._vxlan_local_tunnelip = None + self._clagd_vxlan_anycast_ip = "" + + # If mcastgrp is specified we need to rely on a user-configred device (via physdev) + # or via a policy variable "vxlan-physdev_mcastgrp". If the device doesn't exist we + # create it as a dummy device. We need to keep track of the user configuration to + # know when to delete this dummy device (when user remove mcastgrp from it's config) + self.vxlan_mcastgrp_ref = False + self.vxlan_physdev_mcast = policymanager.policymanager_api.get_module_globals( + module_name=self.__class__.__name__, + attr="vxlan-physdev-mcastgrp" + ) or self.VXLAN_PHYSDEV_MCASTGRP_DEFAULT + + def reset(self): + # in daemon mode we need to reset mcastgrp_ref for every new command + # this variable has to be set in get_dependent_ifacenames + self.vxlan_mcastgrp_ref = False def syntax_check(self, ifaceobj, ifaceobj_getfunc): if self._is_vxlan_device(ifaceobj): - if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not vxlan._vxlan_local_tunnelip: + if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not self._vxlan_local_tunnelip: self.logger.warning('%s: missing vxlan-local-tunnelip' % ifaceobj.name) return False return self.syntax_check_localip_anycastip_equal( ifaceobj.name, - ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or vxlan._vxlan_local_tunnelip, - vxlan._clagd_vxlan_anycast_ip + ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or self._vxlan_local_tunnelip, + self._clagd_vxlan_anycast_ip ) return True def syntax_check_localip_anycastip_equal(self, ifname, local_ip, anycast_ip): try: - if IPNetwork(local_ip) == IPNetwork(anycast_ip): + if local_ip and anycast_ip and IPNetwork(local_ip) == IPNetwork(anycast_ip): self.logger.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)' % (ifname, local_ip)) return False @@ -123,13 +161,19 @@ def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None): if self._is_vxlan_device(ifaceobj): ifaceobj.link_kind |= ifaceLinkKind.VXLAN self._set_global_local_ip(ifaceobj) + + # if we detect a vxlan we check if mcastgrp is set (if so we set vxlan_mcastgrp_ref) + # to know when to delete this device. + if not self.vxlan_mcastgrp_ref and ifaceobj.get_attr_value("vxlan-mcastgrp"): + self.vxlan_mcastgrp_ref = True + elif ifaceobj.name == 'lo': clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip') if clagd_vxlan_list: if len(clagd_vxlan_list) != 1: self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one' % (ifaceobj.name,)) - vxlan._clagd_vxlan_anycast_ip = clagd_vxlan_list[0] + self._clagd_vxlan_anycast_ip = clagd_vxlan_list[0] self._set_global_local_ip(ifaceobj) @@ -144,52 +188,23 @@ def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None): def _set_global_local_ip(self, ifaceobj): vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip') - if vxlan_local_tunnel_ip and not vxlan._vxlan_local_tunnelip: - vxlan._vxlan_local_tunnelip = vxlan_local_tunnel_ip + if vxlan_local_tunnel_ip and not self._vxlan_local_tunnelip: + self._vxlan_local_tunnelip = vxlan_local_tunnel_ip - def _is_vxlan_device(self, ifaceobj): - if ifaceobj.get_attr_value_first('vxlan-id'): - return True - return False + @staticmethod + def _is_vxlan_device(ifaceobj): + return ifaceobj.link_kind & ifaceLinkKind.VXLAN or ifaceobj.get_attr_value_first('vxlan-id') - def _get_purge_remotes(self, ifaceobj): + def __get_vlxan_purge_remotes(self, ifaceobj): if not ifaceobj: - return self._purge_remotes + return self._vxlan_purge_remotes purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes') if purge_remotes: purge_remotes = utils.get_boolean_from_string(purge_remotes) else: - purge_remotes = self._purge_remotes + purge_remotes = self._vxlan_purge_remotes return purge_remotes - def should_create_set_vxlan(self, link_exists, ifname, vxlan_id, local, learning, ageing, group, ttl): - """ - should we issue a netlink: ip link add dev %ifname type vxlan ...? - checking each attribute against the cache - """ - if not link_exists: - return True - - try: - if ageing: - ageing = int(ageing) - except: - pass - - if ttl is not None and not self.ipcmd.cache_check((ifname, 'linkinfo', Link.IFLA_VXLAN_TTL), ttl): - return True - - for attr_list, value in ( - ((ifname, 'linkinfo', Link.IFLA_VXLAN_ID), vxlan_id), - ((ifname, 'linkinfo', Link.IFLA_VXLAN_AGEING), ageing), - ((ifname, 'linkinfo', Link.IFLA_VXLAN_LOCAL), local), - ((ifname, 'linkinfo', Link.IFLA_VXLAN_LEARNING), learning), - ((ifname, 'linkinfo', Link.IFLA_VXLAN_GROUP), group), - ): - if value and not self.ipcmd.cache_check(attr_list, value): - return True - return False - def get_vxlan_ttl_from_string(self, ttl_config): ttl = 0 if ttl_config: @@ -199,252 +214,549 @@ def get_vxlan_ttl_from_string(self, ttl_config): ttl = int(ttl_config) return ttl - def _vxlan_create(self, ifaceobj): - vxlanid = ifaceobj.get_attr_value_first('vxlan-id') - if vxlanid: - ifname = ifaceobj.name - anycastip = self._clagd_vxlan_anycast_ip - group = ifaceobj.get_attr_value_first('vxlan-svcnodeip') + def __config_vxlan_id(self, ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + """ + Get vxlan-id user config and check it's value before inserting it in our netlink dictionary + :param ifname: + :param ifaceobj: + :param vxlan_id_str: + :param user_request_vxlan_info_data: + :param cached_vxlan_ifla_info_data: + :return: + """ + try: + vxlan_id = int(vxlan_id_str) + cached_vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID) + + if cached_vxlan_id and cached_vxlan_id != vxlan_id: + self.log_error( + "%s: Cannot change running vxlan id (%s): Operation not supported" + % (ifname, cached_vxlan_id), + ifaceobj + ) + user_request_vxlan_info_data[Link.IFLA_VXLAN_ID] = vxlan_id + except ValueError: + self.log_error("%s: invalid vxlan-id '%s'" % (ifname, vxlan_id_str), ifaceobj) + + def __get_vxlan_ageing_int(self, ifname, ifaceobj, link_exists): + """ + Get vxlan-ageing user config or via policy, return integer value, None or raise on error + :param ifname: + :param ifaceobj: + :param link_exists: + :return: + """ + vxlan_ageing_str = ifaceobj.get_attr_value_first("vxlan-ageing") + try: + if vxlan_ageing_str: + return int(vxlan_ageing_str) + + vxlan_ageing_str = policymanager.policymanager_api.get_attr_default( + module_name=self.__class__.__name__, + attr="vxlan-ageing" + ) + + if not vxlan_ageing_str and link_exists: + # if link doesn't exist we let the kernel define ageing + vxlan_ageing_str = self.get_attr_default_value("vxlan-ageing") + + if vxlan_ageing_str: + return int(vxlan_ageing_str) + except: + self.log_error("%s: invalid vxlan-ageing '%s'" % (ifname, vxlan_ageing_str), ifaceobj) - local = ifaceobj.get_attr_value_first('vxlan-local-tunnelip') - if not local and vxlan._vxlan_local_tunnelip: - local = vxlan._vxlan_local_tunnelip + def __config_vxlan_ageing(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + """ + Check user config vxlan-ageing and insert it in our netlink dictionary if needed + """ + vxlan_ageing = self.__get_vxlan_ageing_int(ifname, ifaceobj, link_exists) + + if not vxlan_ageing or (link_exists and vxlan_ageing == cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_AGEING)): + return + + self.logger.info("%s: set vxlan-ageing %s" % (ifname, vxlan_ageing)) + user_request_vxlan_info_data[Link.IFLA_VXLAN_AGEING] = vxlan_ageing + + def __config_vxlan_port(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + """ + Check vxlan-port user config, validate the integer value and insert it in the netlink dictionary if needed + :param ifname: + :param ifaceobj: + :param link_exists: + :param user_request_vxlan_info_data: + :param cached_vxlan_ifla_info_data: + :return: + """ + vxlan_port_str = ifaceobj.get_attr_value_first("vxlan-port") + try: + if not vxlan_port_str: + vxlan_port_str = policymanager.policymanager_api.get_attr_default( + module_name=self.__class__.__name__, + attr="vxlan-port" + ) - ttl_config = ifaceobj.get_attr_value_first('vxlan-ttl') try: - if ttl_config: - ttl = self.get_vxlan_ttl_from_string(ttl_config) - else: - ttl = self.get_vxlan_ttl_from_string( - policymanager.policymanager_api.get_attr_default( - module_name=self.__class__.__name__, - attr='vxlan-ttl' - ) + vxlan_port = int(vxlan_port_str) + except TypeError: + # TypeError means vxlan_port was None + # ie: not provided by the user or the policy + vxlan_port = self.netlink.VXLAN_UDP_PORT + except ValueError as e: + self.logger.warning( + "%s: vxlan-port: using default %s: invalid configured value %s" + % (ifname, self.netlink.VXLAN_UDP_PORT, str(e)) + ) + vxlan_port = self.netlink.VXLAN_UDP_PORT + + cached_vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT) + + if link_exists: + if vxlan_port != cached_vxlan_port: + self.logger.warning( + "%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s" + % (ifname, cached_vxlan_port, ifname, ifname) ) - except: - self.log_error('%s: invalid vxlan-ttl \'%s\'' % (ifname, ttl_config), ifaceobj) return - self.syntax_check_localip_anycastip_equal(ifname, local, anycastip) - # if both local-ip and anycast-ip are identical the function prints a warning + self.logger.info("%s: set vxlan-port %s" % (ifname, vxlan_port)) + user_request_vxlan_info_data[Link.IFLA_VXLAN_PORT] = vxlan_port + except: + self.log_error("%s: invalid vxlan-port '%s'" % (ifname, vxlan_port_str), ifaceobj) + + def __config_vxlan_ttl(self, ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + """ + Get vxlan-ttl from user config or policy, validate integer value and insert in netlink dict + :param ifname: + :param ifaceobj: + :param user_request_vxlan_info_data: + :param cached_vxlan_ifla_info_data: + :return: + """ + vxlan_ttl_str = ifaceobj.get_attr_value_first("vxlan-ttl") + try: + if vxlan_ttl_str: + vxlan_ttl = self.get_vxlan_ttl_from_string(vxlan_ttl_str) + else: + vxlan_ttl = self.get_vxlan_ttl_from_string( + policymanager.policymanager_api.get_attr_default( + module_name=self.__class__.__name__, + attr="vxlan-ttl" + ) + ) + + cached_ifla_vxlan_ttl = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_TTL) + if vxlan_ttl != cached_ifla_vxlan_ttl: + + if cached_ifla_vxlan_ttl is not None: + self.logger.info("%s: set vxlan-ttl %s (cache %s)" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl, cached_ifla_vxlan_ttl)) + else: + self.logger.info("%s: set vxlan-ttl %s" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl)) + + user_request_vxlan_info_data[Link.IFLA_VXLAN_TTL] = vxlan_ttl + except: + self.log_error("%s: invalid vxlan-ttl '%s'" % (ifname, vxlan_ttl_str), ifaceobj) + + def __config_vxlan_local_tunnelip(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + """ + Get vxlan-local-tunnelip user config or policy, validate ip address format and insert in netlink dict + :param ifname: + :param ifaceobj: + :param link_exists: + :param user_request_vxlan_info_data: + :param cached_vxlan_ifla_info_data: + :return: + """ + local = ifaceobj.get_attr_value_first("vxlan-local-tunnelip") + + if not local and self._vxlan_local_tunnelip: + local = self._vxlan_local_tunnelip + + if link_exists: + # on ifreload do not overwrite anycast_ip to individual ip + # if clagd has modified + running_localtunnelip = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL) + + if self._clagd_vxlan_anycast_ip and running_localtunnelip: + anycastip = IPAddress(self._clagd_vxlan_anycast_ip) + if anycastip == running_localtunnelip: + local = running_localtunnelip + + if not local: + local = policymanager.policymanager_api.get_attr_default( + module_name=self.__class__.__name__, + attr="vxlan-local-tunnelip" + ) + + if local: + try: + local = IPv4Address(local) + except AddressValueError: + try: + local_ip = IPv4Network(local).ip + self.logger.warning("%s: vxlan-local-tunnelip %s: netmask ignored" % (ifname, local)) + local = local_ip + except: + raise Exception("%s: invalid vxlan-local-tunnelip %s: must be in ipv4 format" % (ifname, local)) + + cached_ifla_vxlan_local = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL) + + if local: + if local != cached_ifla_vxlan_local: + self.logger.info("%s: set vxlan-local-tunnelip %s" % (ifname, local)) + user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = local + + # if both local-ip and anycast-ip are identical the function prints a warning + self.syntax_check_localip_anycastip_equal(ifname, local, self._clagd_vxlan_anycast_ip) + elif cached_ifla_vxlan_local: + self.logger.info("%s: removing vxlan-local-tunnelip (cache %s)" % (ifname, cached_ifla_vxlan_local)) + user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = None + + return local + + def __get_vxlan_mcast_grp(self, ifaceobj): + """ + Get vxlan-mcastgrp user config or policy + :param ifaceobj: + :return: + """ + vxlan_mcast_grp = ifaceobj.get_attr_value_first("vxlan-mcastgrp") + + if not vxlan_mcast_grp: + vxlan_mcast_grp = policymanager.policymanager_api.get_attr_default( + module_name=self.__class__.__name__, + attr="vxlan-mcastgrp" + ) + + return vxlan_mcast_grp + + def __get_vxlan_svcnodeip(self, ifaceobj): + """ + Get vxlan-svcnodeip user config or policy + :param ifaceobj: + :return: + """ + vxlan_svcnodeip = ifaceobj.get_attr_value_first('vxlan-svcnodeip') - ageing = ifaceobj.get_attr_value_first('vxlan-ageing') - vxlan_port = ifaceobj.get_attr_value_first('vxlan-port') - physdev = ifaceobj.get_attr_value_first('vxlan-physdev') - purge_remotes = self._get_purge_remotes(ifaceobj) + if not vxlan_svcnodeip: + vxlan_svcnodeip = policymanager.policymanager_api.get_attr_default( + module_name=self.__class__.__name__, + attr="vxlan-svcnodeip" + ) - link_exists = self.ipcmd.link_exists(ifname) + return vxlan_svcnodeip - if (not link_exists or - not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT): - vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning') - if not vxlan_learning: - vxlan_learning = self.get_attr_default_value('vxlan-learning') - learning = utils.get_boolean_from_string(vxlan_learning) + def __config_vxlan_group(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + """ + vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive + this function validates ip format for both attribute and tries to understand + what the user really want (remote or group option). + + :param ifname: + :param ifaceobj: + :param mcast_grp: + :param group: + :param physdev: + :param user_request_vxlan_info_data: + :param cached_vxlan_ifla_info_data: + :return: + """ + if mcast_grp and group: + self.log_error("%s: both group (vxlan-mcastgrp %s) and " + "remote (vxlan-svcnodeip %s) cannot be specified" + % (ifname, mcast_grp, group), ifaceobj) + + attribute_name = "vxlan-svcnodeip" + multicast_group_change = False + + if group: + try: + group = IPv4Address(group) + except AddressValueError: + try: + group_ip = IPv4Network(group).ip + self.logger.warning("%s: vxlan-svcnodeip %s: netmask ignored" % (ifname, group)) + group = group_ip + except: + raise Exception("%s: invalid vxlan-svcnodeip %s: must be in ipv4 format" % (ifname, group)) + + if group.is_multicast: + self.logger.warning("%s: vxlan-svcnodeip %s: invalid group address, " + "for multicast IP please use attribute \"vxlan-mcastgrp\"" % (ifname, group)) + # if svcnodeip is used instead of mcastgrp we warn the user + # if mcast_grp is not provided by the user we can instead + # use the svcnodeip value + if not physdev: + self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname)) + + elif mcast_grp: + try: + mcast_grp = IPv4Address(mcast_grp) + except AddressValueError: + try: + group_ip = IPv4Network(mcast_grp).ip + self.logger.warning("%s: vxlan-mcastgrp %s: netmask ignored" % (ifname, mcast_grp)) + mcast_grp = group_ip + except: + raise Exception("%s: invalid vxlan-mcastgrp %s: must be in ipv4 format" % (ifname, mcast_grp)) + + if not mcast_grp.is_multicast: + self.logger.warning("%s: vxlan-mcastgrp %s: invalid group address, " + "for non-multicast IP please use attribute \"vxlan-svcnodeip\"" + % (ifname, mcast_grp)) + # if mcastgrp is specified with a non-multicast address + # we warn the user. If the svcnodeip wasn't specified by + # the user we can use the mcastgrp value as svcnodeip + if not group: + group = mcast_grp + mcast_grp = None else: - learning = utils.get_boolean_from_string( - self.ipcmd.get_vxlandev_learning(ifname)) + attribute_name = "vxlan-mcastgrp" + + if mcast_grp: + group = mcast_grp + + if not physdev: + self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname)) + + cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP) + + if group != cached_ifla_vxlan_group: + + if not group: + group = IPAddress("0.0.0.0") + attribute_name = "vxlan-svcnodeip/vxlan-mcastgrp" + + self.logger.info("%s: set %s %s" % (ifname, attribute_name, group)) + user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP] = group + + # if the mcastgrp address is changed we need to signal this to the upper function + # in this case vxlan needs to be down before applying changes then up'd + multicast_group_change = True if link_exists: - vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifname) - # on ifreload do not overwrite anycast_ip to individual ip - # if clagd has modified - if vxlanattrs: - running_localtunnelip = vxlanattrs.get('local') - if (anycastip and running_localtunnelip and - anycastip == running_localtunnelip): - local = running_localtunnelip - if vxlanattrs.get('vxlanid') != vxlanid: - self.log_error('%s: Cannot change running vxlan id: ' - 'Operation not supported' % ifname, ifaceobj) + if cached_ifla_vxlan_group: + self.logger.info( + "%s: vxlan-mcastgrp configuration changed (cache %s): flapping vxlan device required" + % (ifname, cached_ifla_vxlan_group) + ) else: - device_link_kind = self.ipcmd.link_get_kind(ifname) - if not device_link_kind: - self.logger.error("%s: device already exists and is not a vxlan" % ifname) - ifaceobj.set_status(ifaceStatus.ERROR) - return - elif device_link_kind != "vxlan": - self.logger.error("%s: device already exists and is not a vxlan (type %s)" % (ifname, device_link_kind)) - ifaceobj.set_status(ifaceStatus.ERROR) - return + self.logger.info( + "%s: vxlan-mcastgrp configuration changed: flapping vxlan device required" % ifname + ) - try: - vxlanid = int(vxlanid) - except: - self.log_error('%s: invalid vxlan-id \'%s\'' % (ifname, vxlanid), ifaceobj) + return group, multicast_group_change - if not group: - group = policymanager.policymanager_api.get_attr_default( - module_name=self.__class__.__name__, - attr='vxlan-svcnodeip' - ) + def __config_vxlan_learning(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + if not link_exists or not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT: + vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning') + if not vxlan_learning: + vxlan_learning = self.get_attr_default_value('vxlan-learning') + vxlan_learning = utils.get_boolean_from_string(vxlan_learning) + else: + vxlan_learning = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING) - if group: - try: - group = IPv4Address(group) - except AddressValueError: - try: - group_ip = IPv4Network(group).ip - self.logger.warning('%s: vxlan-svcnodeip %s: netmask ignored' % (ifname, group)) - group = group_ip - except: - raise Exception('%s: invalid vxlan-svcnodeip %s: must be in ipv4 format' % (ifname, group)) - - if not local: - local = policymanager.policymanager_api.get_attr_default( - module_name=self.__class__.__name__, - attr='vxlan-local-tunnelip' - ) + if vxlan_learning != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING): + self.logger.info("%s: set vxlan-learning %s" % (ifaceobj.name, "on" if vxlan_learning else "off")) + user_request_vxlan_info_data[Link.IFLA_VXLAN_LEARNING] = vxlan_learning + + def __get_vxlan_physdev(self, ifaceobj, mcastgrp): + """ + vxlan-physdev wrapper, special handling is required for mcastgrp is provided + the vxlan needs to use a dummy or real device for tunnel endpoint communication + This wrapper will get the physdev from user config or policy. IF the device + doesnt exists we create a dummy device. + + :param ifaceobj: + :param mcastgrp: + :return physdev: + """ + physdev = ifaceobj.get_attr_value_first("vxlan-physdev") - if local: + # if the user provided a physdev we need to honor his config + # or if mcastgrp wasn't specified we don't need to go further + if physdev or not mcastgrp: + return physdev + + physdev = self.vxlan_physdev_mcast + + if not self.cache.link_exists(physdev): + self.logger.info("%s: needs a dummy device (%s) to use for " + "multicast termination (vxlan-mcastgrp %s)" + % (ifaceobj.name, physdev, mcastgrp)) + self.netlink.link_add_with_attributes(ifname=physdev, kind="dummy", ifla={Link.IFLA_MTU: 16000, Link.IFLA_LINKMODE: 1}) + self.netlink.link_up(physdev) + + return physdev + + def __config_vxlan_physdev(self, link_exists, ifaceobj, vxlan_physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): + if vxlan_physdev: + try: + vxlan_physdev_ifindex = self.cache.get_ifindex(vxlan_physdev) + except NetlinkCacheIfnameNotFoundError: try: - local = IPv4Address(local) - except AddressValueError: - try: - local_ip = IPv4Network(local).ip - self.logger.warning('%s: vxlan-local-tunnelip %s: netmask ignored' % (ifname, local)) - local = local_ip - except: - raise Exception('%s: invalid vxlan-local-tunnelip %s: must be in ipv4 format' % (ifname, local)) - - if not ageing: - ageing = policymanager.policymanager_api.get_attr_default( - module_name=self.__class__.__name__, - attr='vxlan-ageing' - ) + vxlan_physdev_ifindex = int(self.sysfs.read_file_oneline("/sys/class/net/%s/ifindex" % vxlan_physdev)) + except: + self.logger.error("%s: physdev %s doesn't exists" % (ifaceobj.name, vxlan_physdev)) + return - if not ageing and link_exists: - # if link doesn't exist we let the kernel define ageing - ageing = self.get_attr_default_value('vxlan-ageing') + if vxlan_physdev_ifindex != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LINK): + self.logger.info("%s: set vxlan-physdev %s" % (ifaceobj.name, vxlan_physdev)) + user_request_vxlan_info_data[Link.IFLA_VXLAN_LINK] = vxlan_physdev_ifindex - if not vxlan_port: - vxlan_port = policymanager.policymanager_api.get_attr_default( - module_name=self.__class__.__name__, - attr='vxlan-port' + # if the vxlan exists we need to return True, meaning that the vxlan + # needs to be flapped because we detected a vxlan-physdev change + if link_exists: + self.logger.info("%s: vxlan-physdev configuration changed: flapping vxlan device required" % ifaceobj.name) + return True + + return False + + def _up(self, ifaceobj): + vxlan_id_str = ifaceobj.get_attr_value_first("vxlan-id") + + if not vxlan_id_str: + return + + ifname = ifaceobj.name + link_exists = self.cache.link_exists(ifname) + + if link_exists: + # if link already exists make sure this is a vxlan + device_link_kind = self.cache.get_link_kind(ifname) + + if device_link_kind != "vxlan": + self.logger.error( + "%s: device already exists and is not a vxlan (type %s)" + % (ifname, device_link_kind) ) + ifaceobj.set_status(ifaceStatus.ERROR) + return - try: - vxlan_port = int(vxlan_port) - except TypeError: - # TypeError means vxlan_port was None - # ie: not provided by the user or the policy - vxlan_port = netlink.VXLAN_UDP_PORT - except ValueError as e: - self.logger.warning('%s: vxlan-port: using default %s: invalid configured value %s' % (ifname, netlink.VXLAN_UDP_PORT, str(e))) - vxlan_port = netlink.VXLAN_UDP_PORT + # get vxlan running attributes + cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname) + else: + cached_vxlan_ifla_info_data = {} - if link_exists and vxlanattrs and not ifupdownflags.flags.DRYRUN: - cache_port = vxlanattrs.get(Link.IFLA_VXLAN_PORT) - if vxlan_port != cache_port: - self.logger.warning('%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s' - % (ifname, cache_port, ifname, ifname)) - vxlan_port = cache_port + user_request_vxlan_info_data = {} - if self.should_create_set_vxlan(link_exists, ifname, vxlanid, local, learning, ageing, group, ttl): + self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) + self.__config_vxlan_learning(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) + self.__config_vxlan_ageing(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) + self.__config_vxlan_port(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) + self.__config_vxlan_ttl(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) + local = self.__config_vxlan_local_tunnelip(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) + + vxlan_mcast_grp = self.__get_vxlan_mcast_grp(ifaceobj) + vxlan_svcnodeip = self.__get_vxlan_svcnodeip(ifaceobj) + vxlan_physdev = self.__get_vxlan_physdev(ifaceobj, vxlan_mcast_grp) + + vxlan_physdev_changed = self.__config_vxlan_physdev( + link_exists, + ifaceobj, + vxlan_physdev, + user_request_vxlan_info_data, + cached_vxlan_ifla_info_data + ) + + group, multicast_group_changed = self.__config_vxlan_group( + ifname, + ifaceobj, + link_exists, + vxlan_mcast_grp, + vxlan_svcnodeip, + vxlan_physdev, + user_request_vxlan_info_data, + cached_vxlan_ifla_info_data + ) + + flap_vxlan_device = link_exists and (multicast_group_changed or vxlan_physdev_changed) + + if user_request_vxlan_info_data: + + if link_exists and not len(user_request_vxlan_info_data) > 1: + # if the vxlan already exists it's already cached + # user_request_vxlan_info_data always contains at least one + # element: vxlan-id + self.logger.info('%s: vxlan already exists - no change detected' % ifname) + else: try: - netlink.link_add_vxlan(ifname, vxlanid, - local=local, - learning=learning, - ageing=ageing, - group=group, - dstport=vxlan_port, - physdev=physdev, - ttl=ttl) - except Exception as e_netlink: - self.logger.debug('%s: vxlan netlink: %s' % (ifname, str(e_netlink))) - try: - self.ipcmd.link_create_vxlan(ifname, vxlanid, - localtunnelip=local, - svcnodeip=group, - remoteips=ifaceobj.get_attr_value('vxlan-remoteip'), - learning='on' if learning else 'off', - ageing=ageing, - ttl=ttl) - except Exception as e_iproute2: - self.logger.warning('%s: vxlan add/set failed: %s' % (ifname, str(e_iproute2))) - return + if flap_vxlan_device: + self.netlink.link_down_force(ifname) + + self.netlink.link_add_vxlan_with_info_data(ifname, user_request_vxlan_info_data) + + if flap_vxlan_device: + self.netlink.link_up_force(ifname) + except Exception as e: + if link_exists: + self.log_error("%s: applying vxlan change failed: %s" % (ifname, str(e)), ifaceobj) + else: + self.log_error("%s: vxlan creation failed: %s" % (ifname, str(e)), ifaceobj) + return + + vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj) + + remoteips = ifaceobj.get_attr_value('vxlan-remoteip') + if remoteips: + try: + for remoteip in remoteips: + IPv4Address(remoteip) + except Exception as e: + self.log_error('%s: vxlan-remoteip: %s' % (ifaceobj.name, str(e))) + + if vxlan_purge_remotes or remoteips: + # figure out the diff for remotes and do the bridge fdb updates + # only if provisioned by user and not by an vxlan external + # controller. + peers = self.iproute2.get_vxlan_peers(ifaceobj.name, group) + if local and remoteips and local in remoteips: + remoteips.remove(local) + cur_peers = set(peers) + if remoteips: + new_peers = set(remoteips) + del_list = cur_peers.difference(new_peers) + add_list = new_peers.difference(cur_peers) + else: + del_list = cur_peers + add_list = [] + for addr in del_list: try: - # manually adding an entry to the caching after creating/updating the vxlan - if not ifname in linkCache.links: - linkCache.links[ifname] = {'linkinfo': {}} - linkCache.links[ifname]['linkinfo'].update({ - 'learning': learning, - Link.IFLA_VXLAN_LEARNING: learning, - 'vxlanid': str(vxlanid), - Link.IFLA_VXLAN_ID: vxlanid - }) - if ageing: - linkCache.links[ifname]['linkinfo'].update({ - 'ageing': ageing, - Link.IFLA_VXLAN_AGEING: int(ageing) - }) + self.iproute2.bridge_fdb_del( + ifaceobj.name, + "00:00:00:00:00:00", + None, True, addr + ) except: pass - else: - self.logger.info('%s: vxlan already exists' % ifname) - # if the vxlan already exists it's already cached - remoteips = ifaceobj.get_attr_value('vxlan-remoteip') - if remoteips: + for addr in add_list: try: - for remoteip in remoteips: - IPv4Address(remoteip) - except Exception as e: - self.log_error('%s: vxlan-remoteip: %s' %(ifaceobj.name, str(e))) - - if purge_remotes or remoteips: - # figure out the diff for remotes and do the bridge fdb updates - # only if provisioned by user and not by an vxlan external - # controller. - peers = self.ipcmd.get_vxlan_peers(ifaceobj.name, group) - if local and remoteips and local in remoteips: - remoteips.remove(local) - cur_peers = set(peers) - if remoteips: - new_peers = set(remoteips) - del_list = cur_peers.difference(new_peers) - add_list = new_peers.difference(cur_peers) - else: - del_list = cur_peers - add_list = [] - - for addr in del_list: - try: - self.ipcmd.bridge_fdb_del(ifaceobj.name, - '00:00:00:00:00:00', - None, True, addr) - except: - pass - - for addr in add_list: - try: - self.ipcmd.bridge_fdb_append(ifaceobj.name, - '00:00:00:00:00:00', - None, True, addr) - except: - pass - - def _up(self, ifaceobj): - self._vxlan_create(ifaceobj) + self.iproute2.bridge_fdb_append( + ifaceobj.name, + "00:00:00:00:00:00", + None, True, addr + ) + except: + pass def _down(self, ifaceobj): try: - self.ipcmd.link_delete(ifaceobj.name) + self.netlink.link_del(ifaceobj.name) except Exception, e: self.log_warn(str(e)) - def _query_check_n_update(self, ifaceobj, ifaceobjcurr, attrname, attrval, - running_attrval): + @staticmethod + def _query_check_n_update(ifaceobj, ifaceobjcurr, attrname, attrval, running_attrval): if not ifaceobj.get_attr_value_first(attrname): return if running_attrval and attrval == running_attrval: - ifaceobjcurr.update_config_with_status(attrname, attrval, 0) + ifaceobjcurr.update_config_with_status(attrname, attrval, 0) else: - ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1) + ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1) - def _query_check_n_update_addresses(self, ifaceobjcurr, attrname, - addresses, running_addresses): + @staticmethod + def _query_check_n_update_addresses(ifaceobjcurr, attrname, addresses, running_addresses): if addresses: for a in addresses: if a in running_addresses: @@ -453,153 +765,198 @@ def _query_check_n_update_addresses(self, ifaceobjcurr, attrname, ifaceobjcurr.update_config_with_status(attrname, a, 1) running_addresses = Set(running_addresses).difference( Set(addresses)) - [ifaceobjcurr.update_config_with_status(attrname, a, 1) - for a in running_addresses] + [ifaceobjcurr.update_config_with_status(attrname, a, 1) for a in running_addresses] def _query_check(self, ifaceobj, ifaceobjcurr): - if not self.ipcmd.link_exists(ifaceobj.name): - return - # Update vxlan object - vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobj.name) - if not vxlanattrs: - ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, - self.get_mod_attrs(), -1) + ifname = ifaceobj.name + + if not self.cache.link_exists(ifname): return - self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-id', - ifaceobj.get_attr_value_first('vxlan-id'), - vxlanattrs.get('vxlanid')) - self._query_check_n_update( - ifaceobj, - ifaceobjcurr, - 'vxlan-port', - ifaceobj.get_attr_value_first('vxlan-port'), - str(vxlanattrs.get(Link.IFLA_VXLAN_PORT)) - ) + cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname) + + if not cached_vxlan_ifla_info_data: + ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1) + return + + for vxlan_attr_str, vxlan_attr_nl, callable_type in ( + ('vxlan-id', Link.IFLA_VXLAN_ID, int), + ('vxlan-ttl', Link.IFLA_VXLAN_TTL, int), + ('vxlan-port', Link.IFLA_VXLAN_PORT, int), + ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, int), + ('vxlan-mcastgrp', Link.IFLA_VXLAN_GROUP, IPv4Address), + ('vxlan-svcnodeip', Link.IFLA_VXLAN_GROUP, IPv4Address), + ('vxlan-physdev', Link.IFLA_VXLAN_LINK, lambda x: self.cache.get_ifindex(x)), + ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda boolean_str: utils.get_boolean_from_string(boolean_str)), + ): + vxlan_attr_value = ifaceobj.get_attr_value_first(vxlan_attr_str) - running_attrval = vxlanattrs.get('local') + if not vxlan_attr_value: + continue + + cached_vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl) + + try: + vxlan_attr_value_nl = callable_type(vxlan_attr_value) + except Exception as e: + self.logger.warning('%s: %s: %s' % (ifname, vxlan_attr_str, str(e))) + ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1) + continue + + if vxlan_attr_value_nl == cached_vxlan_attr_value: + ifaceobjcurr.update_config_with_status(vxlan_attr_str, vxlan_attr_value, 0) + else: + ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1) + + # + # vxlan-local-tunnelip + # + running_attrval = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL) attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip') if not attrval: - attrval = vxlan._vxlan_local_tunnelip + attrval = self._vxlan_local_tunnelip + # TODO: vxlan._vxlan_local_tunnelip should be a IPNetwork obj ifaceobj.update_config('vxlan-local-tunnelip', attrval) - if running_attrval == self._clagd_vxlan_anycast_ip: + if str(running_attrval) == self._clagd_vxlan_anycast_ip: # if local ip is anycast_ip, then let query_check to go through attrval = self._clagd_vxlan_anycast_ip - self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-local-tunnelip', - attrval, running_attrval) - self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-svcnodeip', - ifaceobj.get_attr_value_first('vxlan-svcnodeip'), - vxlanattrs.get('svcnode')) + self._query_check_n_update( + ifaceobj, + ifaceobjcurr, + 'vxlan-local-tunnelip', + str(attrval), + str(running_attrval) + ) - purge_remotes = self._get_purge_remotes(ifaceobj) + # + # vxlan-remoteip + # + purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj) if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'): # If purge remotes or if vxlan-remoteip's are set # in the config file, we are owners of the installed # remote-ip's, lets check and report any remote ips we don't # understand - self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-remoteip', - ifaceobj.get_attr_value('vxlan-remoteip'), - self.ipcmd.get_vxlan_peers(ifaceobj.name, vxlanattrs.get('svcnode'))) - - learning = ifaceobj.get_attr_value_first('vxlan-learning') - if learning: - running_learning = vxlanattrs.get('learning') - if learning == 'yes' and running_learning == 'on': - running_learning = 'yes' - elif learning == 'no' and running_learning == 'off': - running_learning = 'no' - if learning == running_learning: - ifaceobjcurr.update_config_with_status('vxlan-learning', - running_learning, 0) - else: - ifaceobjcurr.update_config_with_status('vxlan-learning', - running_learning, 1) - ageing = ifaceobj.get_attr_value_first('vxlan-ageing') - if not ageing: - ageing = self.get_mod_subattr('vxlan-ageing', 'default') - self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-ageing', - ageing, vxlanattrs.get('ageing')) + cached_svcnode = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP) - physdev = ifaceobj.get_attr_value_first('vxlan-physdev') + self._query_check_n_update_addresses( + ifaceobjcurr, + 'vxlan-remoteip', + ifaceobj.get_attr_value('vxlan-remoteip'), + self.iproute2.get_vxlan_peers(ifaceobj.name, str(cached_svcnode)) + ) - if physdev: - ifla_vxlan_link = vxlanattrs.get(Link.IFLA_VXLAN_LINK) - - if ifla_vxlan_link: - self._query_check_n_update( - ifaceobj, - ifaceobjcurr, - 'vxlan-physdev', - physdev, - netlink.get_iface_name(ifla_vxlan_link) - ) - else: - ifaceobjcurr.update_config_with_status('vxlan-physdev', physdev, 1) + def _query_running(self, ifaceobjrunning): + ifname = ifaceobjrunning.name + if not self.cache.link_exists(ifname): + return - def _query_running(self, ifaceobjrunning): - vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobjrunning.name) - if not vxlanattrs: + if not self.cache.get_link_kind(ifname) == 'vxlan': return - attrval = vxlanattrs.get('vxlanid') - if attrval: - ifaceobjrunning.update_config('vxlan-id', vxlanattrs.get('vxlanid')) - else: - # if there is no vxlan id, this is not a vxlan port + + cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname) + + if not cached_vxlan_ifla_info_data: return - ifaceobjrunning.update_config('vxlan-port', vxlanattrs.get(Link.IFLA_VXLAN_PORT)) + # + # vxlan-id + # + vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID) + + if not vxlan_id: + # no vxlan id, meaning this not a vxlan + return + + ifaceobjrunning.update_config('vxlan-id', str(vxlan_id)) + + # + # vxlan-port + # + vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT) + + if vxlan_port: + ifaceobjrunning.update_config('vxlan-port', vxlan_port) + + # + # vxlan-svcnode + # + vxlan_svcnode_value = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP) + + if vxlan_svcnode_value: + vxlan_svcnode_value = str(vxlan_svcnode_value) + ifaceobjrunning.update_config('vxlan-svcnode', vxlan_svcnode_value) - attrval = vxlanattrs.get('local') - if attrval: - ifaceobjrunning.update_config('vxlan-local-tunnelip', attrval) - attrval = vxlanattrs.get('svcnode') - if attrval: - ifaceobjrunning.update_config('vxlan-svcnode', attrval) - purge_remotes = self._get_purge_remotes(None) + # + # vxlan-remoteip + # + purge_remotes = self.__get_vlxan_purge_remotes(None) if purge_remotes: # if purge_remotes is on, it means we own the # remote ips. Query them and add it to the running config - attrval = self.ipcmd.get_vxlan_peers(ifaceobjrunning.name, vxlanattrs.get('svcnode')) + attrval = self.iproute2.get_vxlan_peers(ifname, vxlan_svcnode_value) if attrval: - [ifaceobjrunning.update_config('vxlan-remoteip', a) - for a in attrval] - attrval = vxlanattrs.get('learning') - if attrval and attrval == 'on': - ifaceobjrunning.update_config('vxlan-learning', 'on') - attrval = vxlanattrs.get('ageing') - if attrval: - ifaceobjrunning.update_config('vxlan-ageing', vxlanattrs.get('ageing')) - - ifla_vxlan_link = vxlanattrs.get(Link.IFLA_VXLAN_LINK) - if ifla_vxlan_link: - ifaceobjrunning.update_config( - 'vxlan-physdev', - netlink.get_iface_name(ifla_vxlan_link) - ) + [ifaceobjrunning.update_config('vxlan-remoteip', a) for a in attrval] + + # + # vxlan-link + # vxlan-ageing + # vxlan-learning + # vxlan-local-tunnelip + # + for vxlan_attr_name, vxlan_attr_nl, callable_netlink_value_to_string in ( + ('vxlan-physdev', Link.IFLA_VXLAN_LINK, self._get_ifname_for_ifindex), + ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, str), + ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda value: 'on' if value else 'off'), + ('vxlan-local-tunnelip', Link.IFLA_VXLAN_LOCAL, str), + ): + vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl) + + if vxlan_attr_value is not None: + vxlan_attr_value_str = callable_netlink_value_to_string(vxlan_attr_nl) + + if vxlan_attr_value: + ifaceobjrunning.update_config(vxlan_attr_name, vxlan_attr_value_str) + + def _get_ifname_for_ifindex(self, ifindex): + """ + we need this middle-man function to query the cache + cache.get_ifname can raise KeyError, we need to catch + it and return None + """ + try: + return self.cache.get_ifname(ifindex) + except KeyError: + return None - _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-running": _query_running, + "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: return - if (operation != 'query-running' and - not self._is_vxlan_device(ifaceobj)): - return - self._init_command_handlers() + + if operation != 'query-running': + if not self._is_vxlan_device(ifaceobj): + return + + if not self.vxlan_mcastgrp_ref \ + and self.vxlan_physdev_mcast \ + and self.cache.link_exists(self.vxlan_physdev_mcast): + self.netlink.link_del(self.vxlan_physdev_mcast) + self.reset() + if operation == 'query-checkcurr': op_handler(self, ifaceobj, query_ifaceobj) else: diff --git a/ifupdown2/addons/xfrm.py b/ifupdown2/addons/xfrm.py index d2e4e7f6..9b0917dc 100644 --- a/ifupdown2/addons/xfrm.py +++ b/ifupdown2/addons/xfrm.py @@ -11,11 +11,13 @@ 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 @@ 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 @@ 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 @@ def _up(self, ifaceobj): 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 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): """ 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 @@ def _query_running(self, ifaceobjrunning): 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 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): 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: diff --git a/ifupdown2/ifupdown/argv.py b/ifupdown2/ifupdown/argv.py index 5c442679..4e1da408 100644 --- a/ifupdown2/ifupdown/argv.py +++ b/ifupdown2/ifupdown/argv.py @@ -12,10 +12,10 @@ 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 @@ def __init__(self, argv): 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 @@ def get_op(self): 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 @@ def update_argparser(self, argparser): 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 @@ def update_ifreload_argparser(self, argparser): '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') diff --git a/ifupdown2/ifupdown/client.py b/ifupdown2/ifupdown/client.py new file mode 100644 index 00000000..27768805 --- /dev/null +++ b/ifupdown2/ifupdown/client.py @@ -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() diff --git a/ifupdown2/ifupdown/exceptions.py b/ifupdown2/ifupdown/exceptions.py index 9537f21d..0dd16a60 100644 --- a/ifupdown2/ifupdown/exceptions.py +++ b/ifupdown2/ifupdown/exceptions.py @@ -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 diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py index bb095358..729fc7e7 100644 --- a/ifupdown2/ifupdown/iface.py +++ b/ifupdown2/ifupdown/iface.py @@ -14,9 +14,12 @@ """ 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 @@ def to_str(cls, kind): 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 @@ def __getstate__(self): 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 @@ def __setstate__(self, dict): 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 @@ def dump(self, logger): 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 @@ def _dump_pretty(self, family, first, addr_method, with_status=False, use_realna 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') diff --git a/ifupdown2/ifupdown/iff.py b/ifupdown2/ifupdown/iff.py deleted file mode 100644 index c7c5d653..00000000 --- a/ifupdown2/ifupdown/iff.py +++ /dev/null @@ -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 diff --git a/ifupdown2/ifupdown/ifupdownbase.py b/ifupdown2/ifupdown/ifupdownbase.py deleted file mode 100644 index fd7c7305..00000000 --- a/ifupdown2/ifupdown/ifupdownbase.py +++ /dev/null @@ -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") diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 57b17a27..b6afe58d 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -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 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 @@ 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 @@ def __init__(self, builtin=False, noconfig=False): self.BUILTIN = builtin self.NOCONFIG = noconfig -class ifupdownMain(ifupdownBase): +class ifupdownMain: """ ifupdown2 main class """ scripts_dir = '/etc/network' @@ -111,7 +114,7 @@ def run_up(self, ifaceobj): 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 @@ def _keep_link_down(self, ifaceobj): # 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 @@ def run_down(self, ifaceobj): 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 @@ def run_sched_ifaceobj_posthook(self, ifaceobj, op): 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 + + 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.ifupdown.statemanager.reset() - ifupdown2.ifupdown.policymanager.reset() - ifupdown2.ifupdown.ifupdownflags.reset() - ifupdownConfig.reset() - ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset() - ifupdown2.ifupdownaddons.LinkUtils.LinkUtils.reset() + 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 - ifupdown2.ifupdownaddons.cache.linkCache.reset() - ifupdown2.ifupdownaddons.cache.MSTPAttrsCache.invalidate() + 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 @@ def __init__(self, config={}, 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: # { '' : [, ..] } @@ -444,9 +495,6 @@ def get_ifaceobjcurr(self, ifacename, idx=0): 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 @@ def _set_iface_role(self, ifaceobj, role, upperifaceobj): (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 @@ def _keyword_ip_prefixlen(self, value, validrange=None): def _keyword_mac_ip_prefixlen_list(self, value, validrange=None): """ - [ ...] + MAC address followed by optional list of ip addresses + [ ...] 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 @@ def _ifaceobj_syntax_checker(self, ifaceobj): 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 @@ def read_iface_config(self): 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 @@ def load_addon_modules(self, modules_dir_list): 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 @@ def _process_delay_admin_state_queue(self, op): 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 @@ def _process_delay_admin_state_queue(self, op): 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 @@ def up(self, ops, auto=False, allow_classes=None, ifacenames=None, except Exception: raise + if excludepats: + excludepats = self._preprocess_excludepats(excludepats) + filtered_ifacenames = None if ifacenames: ifacenames = self._preprocess_ifacenames(ifacenames) @@ -1695,6 +1778,35 @@ def up(self, ops, auto=False, allow_classes=None, ifacenames=None, 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 @@ def down(self, ops, auto=False, allow_classes=None, ifacenames=None, 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 @@ def query(self, ops, auto=False, format_list=False, allow_classes=None, 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 @@ def query(self, ops, auto=False, format_list=False, allow_classes=None, 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 @@ def _reload_default(self, upops, downops, auto=False, allow=None, 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 @@ def reload(self, *args, **kargs): 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 @@ def print_ifaceobjs_list(self, ifacenames): 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 @@ def print_ifaceobjs_pretty(self, ifacenames, format='native'): 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: diff --git a/ifupdown2/ifupdown/log.py b/ifupdown2/ifupdown/log.py deleted file mode 100644 index cd09e346..00000000 --- a/ifupdown2/ifupdown/log.py +++ /dev/null @@ -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() diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index e54ace87..7847ba15 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -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 @@ def parse_argv(self, argv): 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 @@ def main(self, stdin_buffer=None): 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 @@ def main(self, stdin_buffer=None): 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 @@ def run_reload(self, args): 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) diff --git a/ifupdown2/ifupdown/netlink.py b/ifupdown2/ifupdown/netlink.py deleted file mode 100644 index 099208b1..00000000 --- a/ifupdown2/ifupdown/netlink.py +++ /dev/null @@ -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() diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index 6cfe456e..30ebc3ae 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -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 @@ def __init__(self, interfacesfile='/etc/network/interfaces', 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 @@ def parse_iface(self, lines, cur_idx, lineno, ifaceobj): 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 @@ def parse_iface(self, lines, cur_idx, lineno, ifaceobj): 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]) diff --git a/ifupdown2/ifupdown/policymanager.py b/ifupdown2/ifupdown/policymanager.py index 4dadc0d8..f99d1968 100644 --- a/ifupdown2/ifupdown/policymanager.py +++ b/ifupdown2/ifupdown/policymanager.py @@ -40,6 +40,8 @@ def __init__(self): 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 @@ def __init__(self): 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 = {} diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index 940c4f13..00130ad9 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -14,22 +14,20 @@ 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 @@ def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None): 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 @@ def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops): # 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 @@ def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None, # 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: diff --git a/ifupdown2/ifupdown/statemanager.py b/ifupdown2/ifupdown/statemanager.py index 5e8f1c74..394b502b 100644 --- a/ifupdown2/ifupdown/statemanager.py +++ b/ifupdown2/ifupdown/statemanager.py @@ -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 @@ def __init__(self): 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 @@ def ifaceobj_sync(self, ifaceobj, op): 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: diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 9c259ceb..08091e5a 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -15,6 +15,7 @@ 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: diff --git a/ifupdown2/ifupdownaddons/LinkUtils.py b/ifupdown2/ifupdownaddons/LinkUtils.py deleted file mode 100644 index 615241a8..00000000 --- a/ifupdown2/ifupdownaddons/LinkUtils.py +++ /dev/null @@ -1,2791 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. -# Author: Roopa Prabhu, roopa@cumulusnetworks.com -# Julien Fortin, julien@cumulusnetworks.com -# - -import os -import re -import glob -import shlex -import signal -import socket -import subprocess - -from string import maketrans -from ipaddr import IPNetwork, IPv6Network - -try: - import ifupdown2.ifupdown.statemanager as statemanager - import ifupdown2.ifupdown.ifupdownflags as ifupdownflags - - from ifupdown2.nlmanager.nlmanager import Link, Route - - from ifupdown2.ifupdown.iface import * - from ifupdown2.ifupdown.utils import utils - from ifupdown2.ifupdown.netlink import netlink - - from ifupdown2.ifupdownaddons.utilsbase import utilsBase - from ifupdown2.ifupdownaddons.cache import linkCache, MSTPAttrsCache -except ImportError: - import ifupdown.ifupdownflags as ifupdownflags - import ifupdown.statemanager as statemanager - - from nlmanager.nlmanager import Link, Route - - from ifupdown.iface import * - from ifupdown.utils import utils - from ifupdown.netlink import netlink - - from ifupdownaddons.utilsbase import utilsBase - from ifupdownaddons.cache import linkCache, MSTPAttrsCache - - -class LinkUtils(utilsBase): - """ - This class contains helper methods to cache and manipulate interfaces through - non-netlink APIs (sysfs, iproute2, brctl...) - """ - _CACHE_FILL_DONE = False - VXLAN_UDP_PORT = 4789 - - ipbatchbuf = '' - ipbatch = False - ipbatch_pause = False - - bridge_utils_is_installed = os.path.exists(utils.brctl_cmd) - bridge_utils_missing_warning = True - - DEFAULT_IP_METRIC = 1024 - ADDR_METRIC_SUPPORT = None - - mac_translate_tab = maketrans(":.-,", " ") - - def __init__(self, *args, **kargs): - utilsBase.__init__(self, *args, **kargs) - - self.supported_command = { - '%s -c -json vlan show' % utils.bridge_cmd: True, - 'showmcqv4src': True - } - self.bridge_vlan_cache = {} - self.bridge_vlan_cache_fill_done = False - - if not ifupdownflags.flags.PERFMODE and not LinkUtils._CACHE_FILL_DONE: - self._fill_cache() - - if LinkUtils.ADDR_METRIC_SUPPORT is None: - try: - cmd = [utils.ip_cmd, 'addr', 'help'] - self.logger.info('executing %s addr help' % utils.ip_cmd) - - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - LinkUtils.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or '' - self.logger.info('address metric support: %s' % ('OK' if LinkUtils.ADDR_METRIC_SUPPORT else 'KO')) - except Exception: - LinkUtils.ADDR_METRIC_SUPPORT = False - self.logger.info('address metric support: KO') - - @classmethod - def mac_str_to_int(cls, mac): - mac_int = 0 - if mac: - for n in mac.translate(cls.mac_translate_tab).split(): - mac_int += int(n, 16) - return mac_int - - @classmethod - def addr_metric_support(cls): - return cls.ADDR_METRIC_SUPPORT - - @classmethod - def get_default_ip_metric(cls): - return cls.DEFAULT_IP_METRIC - - @classmethod - def reset(cls): - LinkUtils._CACHE_FILL_DONE = False - LinkUtils.ipbatchbuf = '' - LinkUtils.ipbatch = False - LinkUtils.ipbatch_pause = False - - def _fill_cache(self): - if not LinkUtils._CACHE_FILL_DONE: - self._link_fill() - self._addr_fill() - LinkUtils._CACHE_FILL_DONE = True - return True - return False - - @staticmethod - def _get_vland_id(citems, i, warn): - try: - sub = citems[i:] - index = sub.index('id') - int(sub[index + 1]) - return sub[index + 1] - except: - if warn: - raise Exception('invalid use of \'vlan\' keyword') - return None - - def _link_fill(self, ifacename=None, refresh=False): - """ fills cache with link information - - if ifacename argument given, fill cache for ifacename, else - fill cache for all interfaces in the system - """ - - if LinkUtils._CACHE_FILL_DONE and not refresh: - return - try: - # if ifacename already present, return - if (ifacename and not refresh and - linkCache.get_attr([ifacename, 'ifflag'])): - return - except: - pass - - if True: - try: - [linkCache.update_attrdict([ifname], linkattrs) - for ifname, linkattrs in netlink.link_dump(ifacename).items()] - except Exception as e: - self.logger.info('%s' % str(e)) - # this netlink call replaces the call to _link_fill_iproute2_cmd() - # We shouldn't have netlink calls in the iproute2 module, this will - # be removed in the future. We plan to release, a flexible backend - # (netlink+iproute2) by default we will use netlink backend but with - # a CLI arg we can switch to iproute2 backend. - # Until we decide to create this "backend" switch capability, - # we have to put the netlink call inside the iproute2 module. - else: - self._link_fill_iproute2_cmd(ifacename, refresh) - - self._fill_bond_info(ifacename) - self._fill_bridge_info(ifacename) - - def _fill_bridge_info(self, ifacename): - - if True: # netlink - brports = {} - - if ifacename: - cache_dict = {ifacename: linkCache.links.get(ifacename, {})} - else: - cache_dict = linkCache.links - - for ifname, obj in cache_dict.items(): - slave_kind = obj.get('slave_kind') - if not slave_kind and slave_kind != 'bridge': - continue - - info_slave_data = obj.get('info_slave_data') - if not info_slave_data: - continue - - ifla_master = obj.get('master') - if not ifla_master: - raise Exception('No master associated with bridge port %s' % ifname) - - for nl_attr in [ - Link.IFLA_BRPORT_STATE, - Link.IFLA_BRPORT_COST, - Link.IFLA_BRPORT_PRIORITY, - ]: - if nl_attr not in info_slave_data and LinkUtils.bridge_utils_is_installed: - self._fill_bridge_info_brctl() - return - - brport_attrs = { - 'pathcost': str(info_slave_data.get(Link.IFLA_BRPORT_COST, 0)), - 'fdelay': format(float(info_slave_data.get(Link.IFLA_BRPORT_FORWARD_DELAY_TIMER, 0) / 100), '.2f'), - 'portmcrouter': str(info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER, 0)), - 'portmcfl': str(info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE, 0)), - 'portprio': str(info_slave_data.get(Link.IFLA_BRPORT_PRIORITY, 0)), - 'unicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_UNICAST_FLOOD, 0)), - 'multicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_MCAST_FLOOD, 0)), - 'learning': str(info_slave_data.get(Link.IFLA_BRPORT_LEARNING, 0)), - 'arp-nd-suppress': str(info_slave_data.get(Link.IFLA_BRPORT_ARP_SUPPRESS, 0)) - } - - if ifla_master in brports: - brports[ifla_master][ifname] = brport_attrs - else: - brports[ifla_master] = {ifname: brport_attrs} - - linkCache.update_attrdict([ifla_master, 'linkinfo', 'ports'], brports[ifla_master]) - else: - if LinkUtils.bridge_utils_is_installed: - self._fill_bridge_info_brctl() - - def _fill_bridge_info_brctl(self): - brctlout = utils.exec_command('%s show' % utils.brctl_cmd) - if not brctlout: - return - - for bline in brctlout.splitlines()[1:]: - bitems = bline.split() - if len(bitems) < 2: - continue - try: - linkCache.update_attrdict([bitems[0], 'linkinfo'], - {'stp': bitems[2]}) - except KeyError: - linkCache.update_attrdict([bitems[0]], - {'linkinfo': {'stp': bitems[2]}}) - self._bridge_attrs_fill(bitems[0]) - - def _bridge_attrs_fill(self, bridgename): - battrs = {} - bports = {} - - try: - # Get all bridge attributes - # battrs['pathcost'] = broutlines[3].split('path cost')[1].strip() - - try: - battrs['maxage'] = self.read_file_oneline( - '/sys/class/net/%s/bridge/max_age' % bridgename) - except: - pass - - - try: - battrs['hello'] = self.read_file_oneline( - '/sys/class/net/%s/bridge/hello_time' % bridgename) - except: - pass - - try: - battrs['fd'] = self.read_file_oneline( - '/sys/class/net/%s/bridge/forward_delay' % bridgename) - except: - pass - - try: - battrs['ageing'] = self.read_file_oneline( - '/sys/class/net/%s/bridge/ageing_time' % bridgename) - except: - pass - - try: - battrs['mcrouter'] = self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_router' % bridgename) - except: - pass - - try: - battrs['bridgeprio'] = self.read_file_oneline( - '/sys/class/net/%s/bridge/priority' % bridgename) - except: - pass - - try: - battrs['vlan-protocol'] = VlanProtocols.ID_TO_ETHERTYPES[ - self.read_file_oneline( - '/sys/class/net/%s/bridge/vlan_protocol' % bridgename)] - except: - pass - - try: - battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename)) - except: - pass - - # XXX: comment this out until mc attributes become available - # with brctl again - - # battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip() - # battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip() - except Exception, e: - self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e))) - pass - - linkCache.update_attrdict([bridgename, 'linkinfo'], battrs) - - names = [os.path.basename(x) for x in glob.glob("/sys/class/net/%s/brif/*" % bridgename)] - for pname in names: - bportattrs = {} - try: - - bportattrs['pathcost'] = self.read_file_oneline( - '/sys/class/net/%s/brport/path_cost' % pname) - bportattrs['fdelay'] = self.read_file_oneline( - '/sys/class/net/%s/brport/forward_delay_timer' % pname) - bportattrs['portmcrouter'] = self.read_file_oneline( - '/sys/class/net/%s/brport/multicast_router' % pname) - bportattrs['portmcfl'] = self.read_file_oneline( - '/sys/class/net/%s/brport/multicast_fast_leave' % pname) - bportattrs['portprio'] = self.read_file_oneline( - '/sys/class/net/%s/brport/priority' % pname) - bportattrs['unicast-flood'] = self.read_file_oneline( - '/sys/class/net/%s/brport/unicast_flood' % pname) - bportattrs['multicast-flood'] = self.read_file_oneline( - '/sys/class/net/%s/brport/multicast_flood' % pname) - bportattrs['learning'] = self.read_file_oneline( - '/sys/class/net/%s/brport/learning' % pname) - bportattrs['arp-nd-suppress'] = self.read_file_oneline( - '/sys/class/net/%s/brport/neigh_suppress' % pname) - - #bportattrs['mcrouters'] = self.read_file_oneline( - # '/sys/class/net/%s/brport/multicast_router' % pname) - #bportattrs['mc fast leave'] = self.read_file_oneline( - # '/sys/class/net/%s/brport/multicast_fast_leave' % pname) - - except Exception, e: - self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e))) - bports[pname] = bportattrs - linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports) - - _bridge_sysfs_mcattrs = { - 'mclmc': 'multicast_last_member_count', - 'mcrouter': 'multicast_router', - 'mcsnoop': 'multicast_snooping', - 'mcsqc': 'multicast_startup_query_count', - 'mcqifaddr': 'multicast_query_use_ifaddr', - 'mcquerier': 'multicast_querier', - 'hashel': 'hash_elasticity', - 'hashmax': 'hash_max', - 'mclmi': 'multicast_last_member_interval', - 'mcmi': 'multicast_membership_interval', - 'mcqpi': 'multicast_querier_interval', - 'mcqi': 'multicast_query_interval', - 'mcqri': 'multicast_query_response_interval', - 'mcsqi': 'multicast_startup_query_interval', - 'igmp-version': 'multicast_igmp_version', - 'mld-version': 'multicast_mld_version', - 'vlan-stats': 'vlan_stats_enabled', - 'mcstats': 'multicast_stats_enabled', - } - - def _bridge_get_mcattrs_from_sysfs(self, bridgename): - mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi'] - mcattrs = {} - - for m, s in self._bridge_sysfs_mcattrs.items(): - n = self.read_file_oneline('/sys/class/net/%s/bridge/%s' % (bridgename, s)) - if m in mcattrsdivby100: - try: - v = int(n) / 100 - mcattrs[m] = str(v) - except Exception, e: - self.logger.warn('error getting mc attr %s (%s)' % (m, str(e))) - pass - else: - mcattrs[m] = n - return mcattrs - - def _fill_bond_info(self, ifacename): - bonding_masters = self.read_file_oneline('/sys/class/net/bonding_masters') - if not bonding_masters: - return - - bond_masters_list = bonding_masters.split() - - if ifacename: - if ifacename in bond_masters_list: - bond_masters_list = [ifacename] - else: - # we want to refresh this interface only if it's a bond master - return - - for bondname in bond_masters_list: - try: - if bondname not in linkCache.links: - linkCache.set_attr([bondname], {'linkinfo': {}}) - linkCache.set_attr([bondname, 'linkinfo', 'slaves'], - self.read_file_oneline('/sys/class/net/%s/bonding/slaves' - % bondname).split()) - try: - # if some attribute are missing we try to get the bond attributes via sysfs - bond_linkinfo = linkCache.links[bondname]['linkinfo'] - for attr in [Link.IFLA_BOND_MODE, Link.IFLA_BOND_XMIT_HASH_POLICY, Link.IFLA_BOND_MIN_LINKS]: - if attr not in bond_linkinfo: - self._fill_bond_info_sysfs(bondname) - # after we fill in the cache we can continue to the next bond - break - except: - self._fill_bond_info_sysfs(bondname) - - except Exception as e: - self.logger.debug('LinkUtils: bond cache error: %s' % str(e)) - - def _fill_bond_info_sysfs(self, bondname): - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS], - self.read_file_oneline( - '/sys/class/net/%s/bonding/min_links' - % bondname)) - except Exception as e: - self.logger.debug(str(e)) - - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MODE], - self.read_file_oneline('/sys/class/net/%s/bonding/mode' - % bondname).split()[0]) - except Exception as e: - self.logger.debug(str(e)) - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY], - self.read_file_oneline( - '/sys/class/net/%s/bonding/xmit_hash_policy' - % bondname).split()[0]) - except Exception as e: - self.logger.debug(str(e)) - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE], - self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate' - % bondname).split()[1]) - except Exception as e: - self.logger.debug(str(e)) - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO], - self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_sys_prio' - % bondname)) - except Exception as e: - self.logger.debug(str(e)) - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM], - self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_system' - % bondname)) - except Exception as e: - self.logger.debug(str(e)) - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS], - self.read_file_oneline('/sys/class/net/%s/bonding/lacp_bypass' - % bondname).split()[1]) - except Exception as e: - self.logger.debug(str(e)) - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY], - self.read_file_oneline('/sys/class/net/%s/bonding/updelay' - % bondname)) - except Exception as e: - self.logger.debug(str(e)) - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY], - self.read_file_oneline('/sys/class/net/%s/bonding/downdelay' - % bondname)) - except Exception as e: - self.logger.debug(str(e)) - - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER], - self.read_file_oneline('/sys/class/net/%s/bonding/use_carrier' % bondname)) - except Exception as e: - self.logger.debug(str(e)) - - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON], - self.read_file_oneline('/sys/class/net/%s/bonding/miimon' % bondname)) - except Exception as e: - self.logger.debug(str(e)) - - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF], - self.read_file_oneline('/sys/class/net/%s/bonding/num_unsol_na' % bondname)) - except Exception as e: - self.logger.debug(str(e)) - - try: - linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF], - self.read_file_oneline('/sys/class/net/%s/bonding/num_grat_arp' % bondname)) - except Exception as e: - self.logger.debug(str(e)) - - - def _link_fill_iproute2_cmd(self, ifacename=None, refresh=False): - warn = True - linkout = {} - if LinkUtils._CACHE_FILL_DONE and not refresh: - return - try: - # if ifacename already present, return - if (ifacename and not refresh and - linkCache.get_attr([ifacename, 'ifflag'])): - return - except: - pass - cmdout = self.link_show(ifacename=ifacename) - if not cmdout: - return - for c in cmdout.splitlines(): - citems = c.split() - ifnamenlink = citems[1].split('@') - if len(ifnamenlink) > 1: - ifname = ifnamenlink[0] - iflink = ifnamenlink[1].strip(':') - else: - ifname = ifnamenlink[0].strip(':') - iflink = None - linkattrs = dict() - linkattrs['link'] = iflink - linkattrs['ifindex'] = citems[0].strip(':') - flags = citems[2].strip('<>').split(',') - linkattrs['flags'] = flags - linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN' - for i in range(0, len(citems)): - try: - if citems[i] == 'mtu': - linkattrs['mtu'] = citems[i + 1] - elif citems[i] == 'state': - linkattrs['state'] = citems[i + 1] - elif citems[i] == 'link/ether': - linkattrs['hwaddress'] = citems[i + 1] - elif citems[i] in ['link/gre', 'link/ipip', 'link/sit', 'link/gre6', 'link/tunnel6', 'gretap']: - linkattrs['kind'] = 'tunnel' - tunattrs = {'mode': citems[i].split('/')[-1], - 'endpoint' : None, - 'local' : None, - 'ttl' : None, - 'physdev' : None} - for j in range(i, len(citems)): - if citems[j] == 'local': - tunattrs['local'] = citems[j + 1] - elif citems[j] == 'remote': - tunattrs['endpoint'] = citems[j + 1] - elif citems[j] == 'ttl': - tunattrs['ttl'] = citems[j + 1] - elif citems[j] == 'dev': - tunattrs['physdev'] = citems[j + 1] - elif citems[j] in ['vti', 'vti6', 'ip6gre', 'ipip6', 'ip6ip6']: - tunattrs['mode'] = citems[j] - linkattrs['linkinfo'] = tunattrs - break - elif citems[i] == 'link/ppp': - linkattrs['kind'] = 'ppp' - elif citems[i] == 'vlan': - vlanid = self._get_vland_id(citems, i, warn) - if vlanid: - linkattrs['linkinfo'] = {'vlanid': vlanid} - linkattrs['kind'] = 'vlan' - elif citems[i] == 'dummy': - linkattrs['kind'] = 'dummy' - elif citems[i] == 'vxlan' and citems[i + 1] == 'id': - linkattrs['kind'] = 'vxlan' - vattrs = {'vxlanid': citems[i + 2], - 'svcnode': None, - 'remote': [], - 'ageing': citems[i + 2], - 'learning': 'on'} - for j in range(i + 2, len(citems)): - if citems[j] == 'local': - vattrs['local'] = citems[j + 1] - elif citems[j] == 'remote': - vattrs['svcnode'] = citems[j + 1] - elif citems[j] == 'ageing': - vattrs['ageing'] = citems[j + 1] - elif citems[j] == 'nolearning': - vattrs['learning'] = 'off' - elif citems[j] == 'dev': - vattrs['physdev'] = citems[j + 1] - linkattrs['linkinfo'] = vattrs - break - elif citems[i] == 'vrf' and citems[i + 1] == 'table': - vattrs = {'table': citems[i + 2]} - linkattrs['linkinfo'] = vattrs - linkattrs['kind'] = 'vrf' - linkCache.vrfs[ifname] = vattrs - break - elif citems[i] == 'veth': - linkattrs['kind'] = 'veth' - elif citems[i] == 'vrf_slave': - linkattrs['slave_kind'] = 'vrf_slave' - break - elif citems[i] == 'macvlan' and citems[i + 1] == 'mode': - linkattrs['kind'] = 'macvlan' - elif citems[i] == 'xfrm': - linkattrs['kind'] = 'xfrm' - except Exception as e: - if warn: - self.logger.debug('%s: parsing error: id, mtu, state, ' - 'link/ether, vlan, dummy, vxlan, local, ' - 'remote, ageing, nolearning, vrf, table, ' - 'vrf_slave are reserved keywords: %s' % - (ifname, str(e))) - warn = False - # linkattrs['alias'] = self.read_file_oneline( - # '/sys/class/net/%s/ifalias' %ifname) - linkout[ifname] = linkattrs - [linkCache.update_attrdict([ifname], linkattrs) - for ifname, linkattrs in linkout.items()] - - @staticmethod - def _addr_filter(ifname, addr, scope=None): - default_addrs = ['127.0.0.1/8', '::1/128', '0.0.0.0'] - if ifname == 'lo' and addr in default_addrs: - return True - if scope and scope == 'link': - return True - return False - - def _addr_fill(self, ifacename=None, refresh=False): - """ fills cache with address information - - if ifacename argument given, fill cache for ifacename, else - fill cache for all interfaces in the system - """ - if LinkUtils._CACHE_FILL_DONE and not refresh: - return - try: - # Check if ifacename is already full, in which case, return - if ifacename and not refresh: - linkCache.get_attr([ifacename, 'addrs']) - return - except: - pass - - if True: - try: - [linkCache.update_attrdict([ifname], linkattrs) - for ifname, linkattrs in netlink.addr_dump(ifname=ifacename).items()] - except Exception as e: - self.logger.info(str(e)) - - # this netlink call replaces the call to _addr_fill_iproute2_cmd() - # We shouldn't have netlink calls in the iproute2 module, this will - # be removed in the future. We plan to release, a flexible backend - # (netlink+iproute2) by default we will use netlink backend but with - # a CLI arg we can switch to iproute2 backend. - # Until we decide to create this "backend" switch capability, - # we have to put the netlink call inside the iproute2 module. - - else: - self._addr_fill_iproute2_cmd(ifacename, refresh) - - def _addr_fill_iproute2_cmd(self, ifacename=None, refresh=False): - """ fills cache with address information - - if ifacename argument given, fill cache for ifacename, else - fill cache for all interfaces in the system - """ - linkout = {} - if LinkUtils._CACHE_FILL_DONE and not refresh: - return - try: - # Check if ifacename is already full, in which case, return - if ifacename and not refresh: - linkCache.get_attr([ifacename, 'addrs']) - return - except: - pass - cmdout = self.addr_show(ifacename=ifacename) - if not cmdout: - return - for c in cmdout.splitlines(): - citems = c.split() - ifnamenlink = citems[1].split('@') - if len(ifnamenlink) > 1: - ifname = ifnamenlink[0] - else: - ifname = ifnamenlink[0].strip(':') - if not linkout.get(ifname): - linkattrs = dict() - linkattrs['addrs'] = OrderedDict({}) - try: - linkout[ifname].update(linkattrs) - except KeyError: - linkout[ifname] = linkattrs - if citems[2] == 'inet': - if self._addr_filter(ifname, citems[3], scope=citems[5]): - continue - addrattrs = dict() - addrattrs['scope'] = citems[5] - addrattrs['type'] = 'inet' - linkout[ifname]['addrs'][citems[3]] = addrattrs - elif citems[2] == 'inet6': - if self._addr_filter(ifname, citems[3], scope=citems[5]): - continue - if citems[5] == 'link': - continue # skip 'link' addresses - addrattrs = dict() - addrattrs['scope'] = citems[5] - addrattrs['type'] = 'inet6' - linkout[ifname]['addrs'][citems[3]] = addrattrs - [linkCache.update_attrdict([ifname], linkattrs) - for ifname, linkattrs in linkout.items()] - - def del_cache_entry(self, ifname): - try: - del linkCache.links[ifname] - except: - pass - - def cache_get(self, t, attrlist, refresh=False): - return self._cache_get(t, attrlist, refresh) - - def _cache_get(self, t, attrlist, refresh=False): - try: - if ifupdownflags.flags.DRYRUN: - return False - if ifupdownflags.flags.CACHE: - if self._fill_cache(): - # if we filled the cache, return new data - return linkCache.get_attr(attrlist) - if not refresh: - return linkCache.get_attr(attrlist) - if t == 'link': - self._link_fill(attrlist[0], refresh) - elif t == 'addr': - self._addr_fill(attrlist[0], refresh) - else: - self._link_fill(attrlist[0], refresh) - self._addr_fill(attrlist[0], refresh) - return linkCache.get_attr(attrlist) - except Exception, e: - self.logger.debug('_cache_get(%s) : [%s]' % (str(attrlist), str(e))) - return None - - def cache_check(self, attrlist, value, refresh=False): - return self._cache_check('link', attrlist, value, refresh=refresh) - - def _cache_check(self, t, attrlist, value, refresh=False): - try: - return self._cache_get(t, attrlist, refresh) == value - except Exception, e: - self.logger.debug('_cache_check(%s) : [%s]' - % (str(attrlist), str(e))) - return False - - def cache_update(self, attrlist, value): - return self._cache_update(attrlist, value) - - @staticmethod - def _cache_update(attrlist, value): - if ifupdownflags.flags.DRYRUN: - return - try: - if attrlist[-1] == 'slaves': - linkCache.append_to_attrlist(attrlist, value) - return - linkCache.set_attr(attrlist, value) - except: - pass - - @staticmethod - def _cache_delete(attrlist, value=None): - if ifupdownflags.flags.DRYRUN: - return - try: - if value: - linkCache.remove_from_attrlist(attrlist, value) - else: - linkCache.del_attr(attrlist) - except: - pass - - @staticmethod - def _cache_invalidate(): - linkCache.invalidate() - LinkUtils._CACHE_FILL_DONE = False - - @staticmethod - def batch_start(): - LinkUtils.ipbatcbuf = '' - LinkUtils.ipbatch = True - LinkUtils.ipbatch_pause = False - - @staticmethod - def add_to_batch(cmd): - LinkUtils.ipbatchbuf += cmd + '\n' - - @staticmethod - def batch_pause(): - LinkUtils.ipbatch_pause = True - - @staticmethod - def batch_resume(): - LinkUtils.ipbatch_pause = False - - def batch_commit(self): - if not LinkUtils.ipbatchbuf: - LinkUtils.ipbatchbuf = '' - LinkUtils.ipbatch = False - LinkUtils.ipbatch_pause = False - return - try: - utils.exec_command('%s -force -batch -' % utils.ip_cmd, - stdin=self.ipbatchbuf) - except: - raise - finally: - LinkUtils.ipbatchbuf = '' - LinkUtils.ipbatch = False - LinkUtils.ipbatch_pause = False - - def bridge_batch_commit(self): - if not LinkUtils.ipbatchbuf: - LinkUtils.ipbatchbuf = '' - LinkUtils.ipbatch = False - LinkUtils.ipbatch_pause = False - return - try: - utils.exec_command('%s -force -batch -' - % utils.bridge_cmd, stdin=self.ipbatchbuf) - except: - raise - finally: - LinkUtils.ipbatchbuf = '' - LinkUtils.ipbatch = False - LinkUtils.ipbatch_pause = False - - def addr_show(self, ifacename=None): - if ifacename: - if not self.link_exists(ifacename): - return - return utils.exec_commandl([utils.ip_cmd, - '-o', 'addr', 'show', 'dev', ifacename]) - else: - return utils.exec_commandl([utils.ip_cmd, - '-o', 'addr', 'show']) - - @staticmethod - def link_show(ifacename=None): - if ifacename: - return utils.exec_commandl([utils.ip_cmd, - '-o', '-d', 'link', 'show', 'dev', ifacename]) - else: - return utils.exec_commandl([utils.ip_cmd, - '-o', '-d', 'link', 'show']) - - def addr_add(self, ifacename, address, broadcast=None, - peer=None, scope=None, preferred_lifetime=None, metric=None): - if not address: - return - cmd = 'addr add %s' % address - if broadcast: - cmd += ' broadcast %s' % broadcast - if peer: - cmd += ' peer %s' % peer - if scope: - cmd += ' scope %s' % scope - if preferred_lifetime: - cmd += ' preferred_lft %s' % preferred_lifetime - cmd += ' dev %s' % ifacename - - if metric: - cmd += ' metric %s' % metric - - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - self._cache_update([ifacename, 'addrs', address], {}) - - def addr_del(self, ifacename, address, broadcast=None, - peer=None, scope=None): - """ Delete ipv4 address """ - if not address: - return - if not self._cache_get('addr', [ifacename, 'addrs', address]): - return - cmd = 'addr del %s' % address - if broadcast: - cmd += ' broadcast %s' % broadcast - if peer: - cmd += ' peer %s' % peer - if scope: - cmd += ' scope %s' % scope - cmd += ' dev %s' % ifacename - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - self._cache_delete([ifacename, 'addrs', address]) - - def addr_flush(self, ifacename): - cmd = 'addr flush dev %s' % ifacename - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - self._cache_delete([ifacename, 'addrs']) - - def del_addr_all(self, ifacename, skip_addrs=[]): - if not skip_addrs: - skip_addrs = [] - runningaddrsdict = self.get_running_addrs(ifname=ifacename) - try: - # XXX: ignore errors. Fix this to delete secondary addresses - # first - [self.addr_del(ifacename, a) for a in - set(runningaddrsdict.keys()).difference(skip_addrs)] - except: - # ignore errors - pass - - def addr_get(self, ifacename, details=True, refresh=False): - addrs = self._cache_get('addr', [ifacename, 'addrs'], refresh=refresh) - if not addrs: - return None - if details: - return addrs - return addrs.keys() - - def get_running_addrs(self, ifaceobj=None, ifname=None, details=True, addr_virtual_ifaceobj=None): - """ - We now support addr with link scope. Since the kernel may add it's - own link address to some interfaces we need to filter them out and - make sure we only deal with the addresses set by ifupdown2. - - To do so we look at the previous configuration made by ifupdown2 - (with the help of the statemanager) together with the addresses - specified by the user in /etc/network/interfaces, these addresses - are then compared to the running state of the intf (ip addr show) - made via a netlink addr dump. - For each configured addresses of scope link, we check if it was - previously configured by ifupdown2 to create a final set of the - addresses watched by ifupdown2 - """ - if not ifaceobj and not ifname: - return None - - config_addrs = set() - - if ifaceobj: - interface_name = ifaceobj.name - else: - interface_name = ifname - - if addr_virtual_ifaceobj: - for attr_name in ["address-virtual", "vrrp"]: - for virtual in addr_virtual_ifaceobj.get_attr_value(attr_name) or []: - for ip in virtual.split(): - try: - IPNetwork(ip) - config_addrs.add(ip) - except: - pass - - saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(addr_virtual_ifaceobj.name) - for saved_ifaceobj in saved_ifaceobjs or []: - for virtual in saved_ifaceobj.get_attr_value(attr_name) or []: - for ip in virtual.split(): - try: - IPNetwork(ip) - config_addrs.add(ip) - except: - pass - else: - if ifaceobj: - for addr in ifaceobj.get_attr_value('address') or []: - config_addrs.add(addr) - - saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(interface_name) - for saved_ifaceobj in saved_ifaceobjs or []: - for addr in saved_ifaceobj.get_attr_value('address') or []: - config_addrs.add(addr) - - running_addrs = OrderedDict() - cached_addrs = self.addr_get(interface_name) - if cached_addrs: - for addr, addr_details in cached_addrs.items(): - try: - scope = int(addr_details['scope']) - except Exception: - try: - d = {} - addr_obj = IPNetwork(addr) - if isinstance(addr_obj, IPv6Network): - d['family'] = 'inet6' - else: - d['family'] = 'inet' - running_addrs[addr] = d - except: - running_addrs[addr] = {} - continue - if (scope & Route.RT_SCOPE_LINK and addr in config_addrs) or not scope & Route.RT_SCOPE_LINK: - running_addrs[addr] = addr_details - else: - return None - - if details: - return running_addrs - return running_addrs.keys() - - @staticmethod - def compare_user_config_vs_running_state(running_addrs, user_addrs): - ip4 = [] - ip6 = [] - - for ip in user_addrs or []: - obj = IPNetwork(ip) - - if type(obj) == IPv6Network: - ip6.append(str(obj)) - else: - ip4.append(str(obj)) - - running_ipobj = [] - for ip in running_addrs or []: - running_ipobj.append(str(IPNetwork(ip))) - - return running_ipobj == (ip4 + ip6) - - def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None): - # purges address - if purge_existing: - # if perfmode is not set and also if iface has no sibling - # objects, purge addresses that are not present in the new - # config - runningaddrs = self.get_running_addrs( - ifname=ifacename, - details=False, - addr_virtual_ifaceobj=ifaceobj - ) - addrs = utils.get_normalized_ip_addr(ifacename, addrs) - - if self.compare_user_config_vs_running_state(runningaddrs, addrs): - return - try: - # if primary address is not same, there is no need to keep any. - # reset all addresses - if (addrs and runningaddrs and - (addrs[0] != runningaddrs[0])): - self.del_addr_all(ifacename) - else: - self.del_addr_all(ifacename, addrs) - except Exception, e: - self.logger.warning('%s: %s' % (ifacename, str(e))) - for a in addrs: - try: - self.addr_add(ifacename, a, metric=metric) - except Exception, e: - self.logger.error(str(e)) - - def _link_set_ifflag(self, ifacename, value): - # Dont look at the cache, the cache may have stale value - # because link status can be changed by external - # entity (One such entity is ifupdown main program) - cmd = 'link set dev %s %s' % (ifacename, value.lower()) - if LinkUtils.ipbatch: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - - def link_up(self, ifacename): - self._link_set_ifflag(ifacename, 'UP') - - def link_down(self, ifacename): - self._link_set_ifflag(ifacename, 'DOWN') - - def link_set(self, ifacename, key, value=None, - force=False, t=None, state=None): - if not force: - if (key not in ['master', 'nomaster'] and - self._cache_check('link', [ifacename, key], value)): - return - cmd = 'link set dev %s' % ifacename - if t: - cmd += ' type %s' % t - cmd += ' %s' % key - if value: - cmd += ' %s' % value - if state: - cmd += ' %s' % state - if LinkUtils.ipbatch: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - if key not in ['master', 'nomaster']: - self._cache_update([ifacename, key], value) - - def link_set_hwaddress(self, ifacename, hwaddress, force=False, keep_down=False): - if not force: - link_hwaddress = self.link_get_hwaddress(ifacename) - - if self.mac_str_to_int(link_hwaddress) == self.mac_str_to_int(hwaddress): - return False - - self.link_down(ifacename) - cmd = 'link set dev %s address %s' % (ifacename, hwaddress) - if LinkUtils.ipbatch: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - - if not keep_down: - self.link_up(ifacename) - self._cache_update([ifacename, 'hwaddress'], hwaddress) - return True - - def link_set_mtu(self, ifacename, mtu): - if ifupdownflags.flags.DRYRUN: - return True - if not mtu or not ifacename: return - self.write_file('/sys/class/net/%s/mtu' % ifacename, mtu) - self._cache_update([ifacename, 'mtu'], mtu) - - def link_set_alias(self, ifacename, alias): - self.write_file('/sys/class/net/%s/ifalias' % ifacename, - '\n' if not alias else alias) - - def link_get_alias(self, ifacename): - return self.read_file_oneline('/sys/class/net/%s/ifalias' - % ifacename) - - def link_isloopback(self, ifacename): - flags = self._cache_get('link', [ifacename, 'flags']) - if not flags: - return - if 'LOOPBACK' in flags: - return True - return False - - def link_get_status(self, ifacename): - return self._cache_get('link', [ifacename, 'ifflag'], refresh=True) - - @staticmethod - def route_add_gateway(ifacename, gateway, vrf=None, metric=None, onlink=True): - if not gateway: - return - if not vrf: - cmd = '%s route add default via %s proto kernel' % (utils.ip_cmd, - gateway) - else: - cmd = ('%s route add table %s default via %s proto kernel' % - (utils.ip_cmd, vrf, gateway)) - # Add metric - if metric: - cmd += ' metric %s' % metric - cmd += ' dev %s' % ifacename - - if onlink: - cmd += " onlink" - - utils.exec_command(cmd) - - @staticmethod - def route_del_gateway(ifacename, gateway, vrf=None, metric=None): - # delete default gw - if not gateway: - return - if not vrf: - cmd = ('%s route del default via %s proto kernel' % - (utils.ip_cmd, gateway)) - else: - cmd = ('%s route del table %s default via %s proto kernel' % - (utils.ip_cmd, vrf, gateway)) - if metric: - cmd += ' metric %s' % metric - cmd += ' dev %s' % ifacename - utils.exec_command(cmd) - - @staticmethod - def _get_vrf_id(ifacename): - try: - return linkCache.vrfs[ifacename]['table'] - except KeyError: - dump = netlink.link_dump(ifacename) - - [linkCache.update_attrdict([ifname], linkattrs) - for ifname, linkattrs in dump.items()] - - if dump and dump.get(ifacename, {}).get('kind') == 'vrf': - vrf_table = dump.get(ifacename, {}).get('linkinfo', {}).get('table') - linkCache.vrfs[ifacename] = {'table': vrf_table} - return vrf_table - - return None - - def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips): - vrf_table = None - - if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE: - try: - for upper_iface in ifaceobj.upperifaces: - vrf_table = self._get_vrf_id(upper_iface) - if vrf_table: - break - except: - pass - - ip_route_del = [] - for ip in ips: - ip_network_obj = IPNetwork(ip) - - if type(ip_network_obj) == IPv6Network: - route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen) - - if vrf_table: - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - LinkUtils.add_to_batch('route del %s table %s dev %s' % (route_prefix, vrf_table, macvlan_ifacename)) - else: - utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'table', vrf_table, 'dev', macvlan_ifacename]) - else: - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - LinkUtils.add_to_batch('route del %s dev %s' % (route_prefix, macvlan_ifacename)) - else: - utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'dev', macvlan_ifacename]) - ip_route_del.append((route_prefix, vrf_table)) - - for ip, vrf_table in ip_route_del: - if vrf_table: - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - LinkUtils.add_to_batch('route add %s table %s dev %s proto kernel metric 9999' % (ip, vrf_table, macvlan_ifacename)) - else: - utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'table', vrf_table, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999']) - else: - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - LinkUtils.add_to_batch('route add %s dev %s proto kernel metric 9999' % (ip, macvlan_ifacename)) - else: - utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999']) - - def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid): - if self.link_exists(vlan_device_name): - return - utils.exec_command('%s link add link %s name %s type vlan id %d' % - (utils.ip_cmd, - vlan_raw_device, vlan_device_name, vlanid)) - self._cache_update([vlan_device_name], {}) - - def link_create_vlan_from_name(self, vlan_device_name): - v = vlan_device_name.split('.') - if len(v) != 2: - self.logger.warn('invalid vlan device name %s' % vlan_device_name) - return - self.link_create_vlan(vlan_device_name, v[0], v[1]) - - def link_create_macvlan(self, name, linkdev, mode='private'): - if self.link_exists(name): - return - cmd = ('link add link %s' % linkdev + - ' name %s' % name + - ' type macvlan mode %s' % mode) - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - self._cache_update([name], {}) - - def get_vxlan_peers(self, dev, svcnodeip): - cmd = '%s fdb show brport %s' % (utils.bridge_cmd, - dev) - cur_peers = [] - try: - ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False) - utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT) - output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout) - ps.wait() - utils.disable_subprocess_signal_forwarding(signal.SIGINT) - try: - ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+') - for l in output.split('\n'): - m = ppat.search(l) - if m and m.group(1) != svcnodeip: - cur_peers.append(m.group(1)) - except: - self.logger.warn('error parsing ip link output') - except subprocess.CalledProcessError as e: - if e.returncode != 1: - self.logger.error(str(e)) - finally: - utils.disable_subprocess_signal_forwarding(signal.SIGINT) - - return cur_peers - - def tunnel_create(self, tunnelname, mode, attrs={}): - """ generic link_create function """ - if self.link_exists(tunnelname): - return - - cmd = '' - if '6' in mode: - cmd = ' -6' - - if mode in ['gretap']: - cmd += ' link add %s type %s' % (tunnelname, mode) - else: - cmd += ' tunnel add %s mode %s' % (tunnelname, mode) - - if attrs: - for k, v in attrs.iteritems(): - cmd += ' %s' % k - if v: - cmd += ' %s' % v - if self.ipbatch and not self.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('ip %s' % cmd) - self._cache_update([tunnelname], {}) - - def tunnel_change(self, tunnelname, attrs={}): - """ tunnel change function """ - if not self.link_exists(tunnelname): - return - cmd = 'tunnel change' - cmd += ' %s' %(tunnelname) - if attrs: - for k, v in attrs.iteritems(): - cmd += ' %s' %k - if v: - cmd += ' %s' %v - if self.ipbatch and not self.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('ip %s' % cmd) - - def link_create_vxlan(self, name, vxlanid, - localtunnelip=None, - svcnodeip=None, - remoteips=None, - learning='on', - ageing=None, - anycastip=None, - ttl=None): - if svcnodeip and remoteips: - raise Exception("svcnodeip and remoteip is mutually exclusive") - args = '' - if svcnodeip: - args += ' remote %s' % svcnodeip - if ageing: - args += ' ageing %s' % ageing - if learning == 'off': - args += ' nolearning' - if ttl is not None: - args += ' ttl %s' % ttl - - if self.link_exists(name): - cmd = 'link set dev %s type vxlan dstport %d' % (name, LinkUtils.VXLAN_UDP_PORT) - vxlanattrs = self.get_vxlandev_attrs(name) - # on ifreload do not overwrite anycast_ip to individual ip if clagd - # has modified - if vxlanattrs: - running_localtunnelip = vxlanattrs.get('local') - if anycastip and running_localtunnelip and anycastip == running_localtunnelip: - localtunnelip = running_localtunnelip - running_svcnode = vxlanattrs.get('svcnode') - if running_svcnode and not svcnodeip: - args += ' noremote' - else: - cmd = 'link add dev %s type vxlan id %s dstport %d' % (name, vxlanid, LinkUtils.VXLAN_UDP_PORT) - - if localtunnelip: - args += ' local %s' % localtunnelip - cmd += args - - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - - # XXX: update linkinfo correctly - #self._cache_update([name], {}) - - @staticmethod - def link_exists(ifacename): - if ifupdownflags.flags.DRYRUN: - return True - return os.path.exists('/sys/class/net/%s' % ifacename) - - @staticmethod - def link_exists_nodryrun(ifname): - return os.path.exists('/sys/class/net/%s' % ifname) - - def link_get_ifindex(self, ifacename): - if ifupdownflags.flags.DRYRUN: - return True - return self.read_file_oneline('/sys/class/net/%s/ifindex' % ifacename) - - def is_vlan_device_by_name(self, ifacename): - if re.search(r'\.', ifacename): - return True - return False - - @staticmethod - def link_add_macvlan(ifname, macvlan_ifacename, mode): - utils.exec_commandl(['ip', 'link', 'add', 'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', mode]) - - @staticmethod - def link_add_xfrm(ifname, xfrm_name, xfrm_id): - utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id]) - - @staticmethod - def route_add(route): - utils.exec_command('%s route add %s' % (utils.ip_cmd, - route)) - - @staticmethod - def route6_add(route): - utils.exec_command('%s -6 route add %s' % (utils.ip_cmd, - route)) - - def get_vlandev_attrs(self, ifacename): - return (self._cache_get('link', [ifacename, 'link']), - self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']), - self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol'])) - - def get_vlan_protocol(self, ifacename): - return self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol']) - - def get_vxlandev_attrs(self, ifacename): - return self._cache_get('link', [ifacename, 'linkinfo']) - - def get_vxlandev_learning(self, ifacename): - return self._cache_get('link', [ifacename, 'linkinfo', Link.IFLA_VXLAN_LEARNING]) - - def set_vxlandev_learning(self, ifacename, learn): - if learn == 'on': - utils.exec_command('%s link set dev %s type vxlan learning' % - (utils.ip_cmd, ifacename)) - self._cache_update([ifacename, 'linkinfo', 'learning'], 'on') - else: - utils.exec_command('%s link set dev %s type vxlan nolearning' % - (utils.ip_cmd, ifacename)) - self._cache_update([ifacename, 'linkinfo', 'learning'], 'off') - - def link_get_linkinfo_attrs(self, ifacename): - return self._cache_get('link', [ifacename, 'linkinfo']) - - def link_get_mtu(self, ifacename, refresh=False): - return self._cache_get('link', [ifacename, 'mtu'], refresh=refresh) - - def link_get_mtu_sysfs(self, ifacename): - return self.read_file_oneline('/sys/class/net/%s/mtu' - % ifacename) - - def link_get_kind(self, ifacename): - return self._cache_get('link', [ifacename, 'kind']) - - def link_get_slave_kind(self, ifacename): - return self._cache_get('link', [ifacename, 'slave_kind']) - - def link_get_hwaddress(self, ifacename): - address = self._cache_get('link', [ifacename, 'hwaddress']) - # newly created logical interface addresses dont end up in the cache - # read hwaddress from sysfs file for these interfaces - if not address: - address = self.read_file_oneline('/sys/class/net/%s/address' - % ifacename) - return address - - def link_create(self, ifacename, t, attrs={}): - """ generic link_create function """ - if self.link_exists(ifacename): - return - cmd = 'link add' - cmd += ' name %s type %s' % (ifacename, t) - if attrs: - for k, v in attrs.iteritems(): - cmd += ' %s' % k - if v: - cmd += ' %s' % v - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - self._cache_update([ifacename], {}) - - def link_delete(self, ifacename): - if not self.link_exists(ifacename): - return - cmd = 'link del %s' % ifacename - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch(cmd) - else: - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - self._cache_invalidate() - - def link_get_master(self, ifacename): - sysfs_master_path = '/sys/class/net/%s/master' % ifacename - if os.path.exists(sysfs_master_path): - link_path = os.readlink(sysfs_master_path) - if link_path: - return os.path.basename(link_path) - else: - return None - else: - return self._cache_get('link', [ifacename, 'master']) - - def get_brport_peer_link(self, bridgename): - try: - return self._cache_get('link', [bridgename, 'info_slave_data', Link.IFLA_BRPORT_PEER_LINK]) - except: - return None - - @staticmethod - def bridge_port_vids_add(bridgeportname, vids): - [utils.exec_command('%s vlan add vid %s dev %s' % - (utils.bridge_cmd, - v, bridgeportname)) for v in vids] - - @staticmethod - def bridge_port_vids_del(bridgeportname, vids): - if not vids: - return - [utils.exec_command('%s vlan del vid %s dev %s' % - (utils.bridge_cmd, - v, bridgeportname)) for v in vids] - - @staticmethod - def bridge_port_vids_flush(bridgeportname, vid): - utils.exec_command('%s vlan del vid %s dev %s' % - (utils.bridge_cmd, - vid, bridgeportname)) - - @staticmethod - def bridge_port_vids_get(bridgeportname): - bridgeout = utils.exec_command('%s vlan show dev %s' % - (utils.bridge_cmd, - bridgeportname)) - if not bridgeout: - return [] - brvlanlines = bridgeout.readlines()[2:] - vids = [l.strip() for l in brvlanlines] - return [v for v in vids if v] - - @staticmethod - def bridge_port_vids_get_all(): - brvlaninfo = {} - bridgeout = utils.exec_command('%s -c vlan show' - % utils.bridge_cmd) - if not bridgeout: - return brvlaninfo - brvlanlines = bridgeout.splitlines() - brportname = None - for l in brvlanlines[1:]: - if l and not l.startswith(' ') and not l.startswith('\t'): - attrs = l.split() - brportname = attrs[0].strip() - brvlaninfo[brportname] = {'pvid': None, 'vlan': []} - l = ' '.join(attrs[1:]) - if not brportname or not l: - continue - l = l.strip() - if 'PVID' in l: - brvlaninfo[brportname]['pvid'] = l.split()[0] - elif 'Egress Untagged' not in l: - brvlaninfo[brportname]['vlan'].append(l) - return brvlaninfo - - def bridge_port_vids_get_all_json(self): - if not self.supported_command['%s -c -json vlan show' - % utils.bridge_cmd]: - return {} - brvlaninfo = {} - try: - bridgeout = utils.exec_command('%s -c -json vlan show' - % utils.bridge_cmd) - except: - self.supported_command['%s -c -json vlan show' - % utils.bridge_cmd] = False - self.logger.info('%s -c -json vlan show: skipping unsupported command' - % utils.bridge_cmd) - try: - return self.get_bridge_vlan_nojson() - except Exception as e: - self.logger.info('bridge: get_bridge_vlan_nojson: %s' % str(e)) - return {} - - if not bridgeout: return brvlaninfo - try: - vlan_json = json.loads(bridgeout, encoding="utf-8") - except Exception, e: - self.logger.info('json loads failed with (%s)' % str(e)) - return {} - - try: - if isinstance(vlan_json, list): - # newer iproute2 version changed the bridge vlan show output - # ifupdown2 relies on the previous format, we have the convert - # data into old format - bridge_port_vids = dict() - - for intf in vlan_json: - bridge_port_vids[intf["ifname"]] = intf["vlans"] - - return bridge_port_vids - else: - # older iproute2 version have different ways to dump vlans - # ifupdown2 prefers the following syntax: - # { - # "vx-1002": [{ - # "vlan": 1002, - # "flags": ["PVID", "Egress Untagged"] - # } - # ], - # "vx-1004": [{ - # "vlan": 1004, - # "flags": ["PVID", "Egress Untagged"] - # }] - # } - return vlan_json - except Exception as e: - self.logger.debug("bridge vlan show: Unknown json output: %s" % str(e)) - return vlan_json - - @staticmethod - def get_bridge_vlan_nojson(): - vlan_json = {} - bridgeout = utils.exec_commandl([utils.bridge_cmd, '-c', 'vlan', 'show']) - if bridgeout: - output = [line.split('\n') for line in bridgeout.split('\n\n')] - output[0] = output[0][1:] - for line in output: - current_swp = None - if not line: - continue - for entry in line: - if not entry: - continue - prefix, vlan = entry.split('\t') - if prefix: - current_swp = prefix - vlan_json[prefix] = [] - v = {} - vlan = vlan[1:] - try: - v['vlan'] = int(vlan) - except: - try: - if '-' in vlan: - start, end = vlan.split('-') - if ' ' in end: - end = end[0:end.index(' ')] - v['vlan'] = int(start) - v['vlanEnd'] = int(end) - else: - v['vlan'] = int(vlan[0:vlan.index(' ')]) - flags = [] - if 'PVID' in vlan: - flags.append('PVID') - if 'Egress Untagged' in vlan: - flags.append('Egress Untagged') - v['flags'] = flags - except: - continue - vlan_json[current_swp].append(v) - return vlan_json - - def bridge_vlan_cache_get(self, ifacename, refresh=False): - if not self.bridge_vlan_cache_fill_done or refresh: - self.bridge_vlan_cache = self.bridge_port_vids_get_all_json() - self.bridge_vlan_cache_fill_done = True - return self.bridge_vlan_cache.get(ifacename, {}) - - def bridge_vlan_get_pvid(self, ifacename, refresh=False): - pvid = 0 - - for vinfo in self.bridge_vlan_cache_get(ifacename, refresh): - v = vinfo.get('vlan') - pvid = v if 'PVID' in vinfo.get('flags', []) else 0 - if pvid: - return pvid - return pvid - - def bridge_vlan_get_vids(self, ifacename, refresh=False): - vids = [] - - for vinfo in self.bridge_vlan_cache_get(ifacename, refresh): - v = vinfo.get('vlan') - ispvid = True if 'PVID' in vinfo.get('flags', []) else False - if ispvid: - pvid = v if 'PVID' in vinfo.get('flags', []) else 0 - if pvid == 1: - continue - vEnd = vinfo.get('vlanEnd') - if vEnd: - vids.extend(range(v, vEnd + 1)) - else: - vids.append(v) - return vids - - def bridge_vlan_get_vids_n_pvid(self, ifacename, refresh=False): - vids = [] - pvid = 0 - - for vinfo in self.bridge_vlan_cache_get(ifacename, refresh): - v = vinfo.get('vlan') - ispvid = True if 'PVID' in vinfo.get('flags', []) else False - if ispvid: - pvid = v if 'PVID' in vinfo.get('flags', []) else 0 - vEnd = vinfo.get('vlanEnd') - if vEnd: - vids.extend(range(v, vEnd + 1)) - else: - vids.append(v) - return vids, pvid - - def bridge_port_pvid_add(self, bridgeportname, pvid): - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch('vlan add vid %s untagged pvid dev %s' % - (pvid, bridgeportname)) - else: - utils.exec_command('%s vlan add vid %s untagged pvid dev %s' % - (utils.bridge_cmd, - pvid, bridgeportname)) - - def bridge_port_pvid_del(self, bridgeportname, pvid): - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - self.add_to_batch('vlan del vid %s untagged pvid dev %s' % - (pvid, bridgeportname)) - else: - utils.exec_command('%s vlan del vid %s untagged pvid dev %s' % - (utils.bridge_cmd, - pvid, bridgeportname)) - - def bridge_port_pvids_get(self, bridgeportname): - return self.read_file_oneline('/sys/class/net/%s/brport/pvid' - % bridgeportname) - - def bridge_vids_add(self, bridgeportname, vids, bridge=True): - target = 'self' if bridge else '' - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - [self.add_to_batch('vlan add vid %s dev %s %s' % - (v, bridgeportname, target)) for v in vids] - else: - [utils.exec_command('%s vlan add vid %s dev %s %s' % - (utils.bridge_cmd, - v, bridgeportname, target)) for v in vids] - - def bridge_vids_del(self, bridgeportname, vids, bridge=True): - target = 'self' if bridge else '' - if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: - [self.add_to_batch('vlan del vid %s dev %s %s' % - (v, bridgeportname, target)) for v in vids] - else: - [utils.exec_command('%s vlan del vid %s dev %s %s' % - (utils.bridge_cmd, - v, bridgeportname, target)) for v in vids] - - @staticmethod - def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None): - target = 'self' if bridge else '' - vlan_str = '' - if vlan: - vlan_str = 'vlan %s ' % vlan - - dst_str = '' - if remote: - dst_str = 'dst %s ' % remote - - utils.exec_command('%s fdb replace %s dev %s %s %s %s' % - (utils.bridge_cmd, - address, dev, vlan_str, target, dst_str)) - - @staticmethod - def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None): - target = 'self' if bridge else '' - vlan_str = '' - if vlan: - vlan_str = 'vlan %s ' % vlan - - dst_str = '' - if remote: - dst_str = 'dst %s ' % remote - - utils.exec_command('%s fdb append %s dev %s %s %s %s' % - (utils.bridge_cmd, - address, dev, vlan_str, target, dst_str)) - - @staticmethod - def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None): - target = 'self' if bridge else '' - vlan_str = '' - if vlan: - vlan_str = 'vlan %s ' % vlan - - dst_str = '' - if remote: - dst_str = 'dst %s ' % remote - utils.exec_command('%s fdb del %s dev %s %s %s %s' % - (utils.bridge_cmd, - address, dev, vlan_str, target, dst_str)) - - def bridge_is_vlan_aware(self, bridgename): - filename = '/sys/class/net/%s/bridge/vlan_filtering' % bridgename - if os.path.exists(filename) and self.read_file_oneline(filename) == '1': - return True - return False - - @staticmethod - def bridge_port_get_bridge_name(bridgeport): - filename = '/sys/class/net/%s/brport/bridge' % bridgeport - try: - return os.path.basename(os.readlink(filename)) - except: - return None - - @staticmethod - def bridge_port_exists(bridge, bridgeportname): - try: - return os.path.exists('/sys/class/net/%s/brif/%s' - % (bridge, bridgeportname)) - except Exception: - return False - - def bridge_fdb_show_dev(self, dev): - try: - fdbs = {} - output = utils.exec_command('%s fdb show dev %s' - % (utils.bridge_cmd, dev)) - if output: - for fdb_entry in output.splitlines(): - try: - entries = fdb_entry.split() - fdbs.setdefault(entries[2], []).append(entries[0]) - except: - self.logger.debug('%s: invalid fdb line \'%s\'' - % (dev, fdb_entry)) - return fdbs - except Exception: - return None - - @staticmethod - def is_bridge(bridge): - return os.path.exists('/sys/class/net/%s/bridge' % bridge) - - def is_link_up(self, ifacename): - ret = False - try: - flags = self.read_file_oneline('/sys/class/net/%s/flags' % ifacename) - iflags = int(flags, 16) - if iflags & 0x0001: - ret = True - except: - ret = False - return ret - - def ip_route_get_dev(self, prefix, vrf_master=None): - try: - if vrf_master: - cmd = '%s route get %s vrf %s' % (utils.ip_cmd, prefix, vrf_master) - else: - cmd = '%s route get %s' % (utils.ip_cmd, prefix) - - output = utils.exec_command(cmd) - if output: - rline = output.splitlines()[0] - if rline: - rattrs = rline.split() - return rattrs[rattrs.index('dev') + 1] - except Exception, e: - self.logger.debug('ip_route_get_dev: failed .. %s' % str(e)) - return None - - @staticmethod - def link_get_lowers(ifacename): - try: - lowers = glob.glob("/sys/class/net/%s/lower_*" % ifacename) - if not lowers: - return [] - return [os.path.basename(l)[6:] for l in lowers] - except: - return [] - - @staticmethod - def link_get_uppers(ifacename): - try: - uppers = glob.glob("/sys/class/net/%s/upper_*" % ifacename) - if not uppers: - return None - return [os.path.basename(u)[6:] for u in uppers] - except Exception: - return None - - def link_get_vrfs(self): - if not LinkUtils._CACHE_FILL_DONE: - self._fill_cache() - return linkCache.vrfs - - @staticmethod - def cache_get_info_slave(attrlist): - try: - return linkCache.get_attr(attrlist) - except: - return None - - def get_brport_learning(self, ifacename): - learn = self.read_file_oneline('/sys/class/net/%s/brport/learning' - % ifacename) - if learn and learn == '1': - return 'on' - else: - return 'off' - - def get_brport_learning_bool(self, ifacename): - return utils.get_boolean_from_string(self.read_file_oneline('/sys/class/net/%s/brport/learning' % ifacename)) - - def set_brport_learning(self, ifacename, learn): - if learn == 'off': - return self.write_file('/sys/class/net/%s/brport/learning' - % ifacename, '0') - else: - return self.write_file('/sys/class/net/%s/brport/learning' - % ifacename, '1') - - ################################################################################# - ################################### BOND UTILS ################################## - ################################################################################# - - def _link_cache_get(self, attrlist, refresh=False): - return self._cache_get('link', attrlist, refresh) - - def cache_delete(self, attrlist, value=None): - return self._cache_delete(attrlist, value) - - def link_cache_get(self, attrlist, refresh=False): - return self._link_cache_get(attrlist, refresh) - - def link_cache_check(self, attrlist, value, refresh=False): - return self._link_cache_check(attrlist, value, refresh) - - def _link_cache_check(self, attrlist, value, refresh=False): - try: - return self._link_cache_get(attrlist, refresh) == value - except Exception, e: - self.logger.debug('_cache_check(%s) : [%s]' - % (str(attrlist), str(e))) - pass - return False - - bondcmd_attrmap = { - Link.IFLA_BOND_MODE: 'mode', - Link.IFLA_BOND_MIIMON: 'miimon', - Link.IFLA_BOND_USE_CARRIER: 'use_carrier', - Link.IFLA_BOND_AD_LACP_RATE: 'lacp_rate', - Link.IFLA_BOND_XMIT_HASH_POLICY: 'xmit_hash_policy', - Link.IFLA_BOND_MIN_LINKS: 'min_links', - Link.IFLA_BOND_NUM_PEER_NOTIF: 'num_grat_arp', - Link.IFLA_BOND_AD_ACTOR_SYSTEM: 'ad_actor_system', - Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: 'ad_actor_sys_prio', - Link.IFLA_BOND_AD_LACP_BYPASS: 'lacp_bypass', - Link.IFLA_BOND_UPDELAY: 'updelay', - Link.IFLA_BOND_DOWNDELAY: 'downdelay', - } - - def bond_set_attrs_nl(self, bondname, ifla_info_data): - bond_attr_name = 'None' # for log purpose (in case an exception raised) - for nl_attr, value in ifla_info_data.items(): - try: - bond_attr_name = self.bondcmd_attrmap[nl_attr] - file_path = '/sys/class/net/%s/bonding/%s' % (bondname, bond_attr_name) - if os.path.exists(file_path): - self.write_file(file_path, str(value)) - except Exception as e: - exception_str = '%s: %s %s: %s' % (bondname, bond_attr_name, value, str(e)) - if ifupdownflags.flags.FORCE: - self.logger.warning(exception_str) - else: - self.logger.debug(exception_str) - - def bond_set_attrs(self, bondname, attrdict, prehook): - for attrname, attrval in attrdict.items(): - if (self._link_cache_check([bondname, 'linkinfo', - attrname], attrval)): - continue - if (attrname == 'mode' - or attrname == 'xmit_hash_policy' - or attrname == 'lacp_rate' or attrname == 'min_links'): - if prehook: - prehook(bondname) - try: - if ((attrname not in ['lacp_rate', - 'lacp_bypass']) or - self._link_cache_check([bondname, 'linkinfo', 'mode'], '802.3ad', - True)): - self.write_file('/sys/class/net/%s/bonding/%s' - % (bondname, attrname), attrval) - except Exception, e: - if ifupdownflags.flags.FORCE: - self.logger.warn(str(e)) - pass - else: - raise - - def bond_set_use_carrier(self, bondname, use_carrier): - if not use_carrier or (use_carrier != '0' and use_carrier != '1'): - return - if (self._link_cache_check([bondname, 'linkinfo', 'use_carrier'], - use_carrier)): - return - self.write_file('/sys/class/net/%s' % bondname + - '/bonding/use_carrier', use_carrier) - self._cache_update([bondname, 'linkinfo', - 'use_carrier'], use_carrier) - - def bond_get_use_carrier(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'use_carrier']) - - def bond_get_use_carrier_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER]) - - def bond_set_xmit_hash_policy(self, bondname, hash_policy, prehook=None): - valid_values = ['layer2', 'layer3+4', 'layer2+3'] - if not hash_policy: - return - if hash_policy not in valid_values: - raise Exception('invalid hash policy value %s' % hash_policy) - if (self._link_cache_check([bondname, 'linkinfo', 'xmit_hash_policy'], - hash_policy)): - return - if prehook: - prehook(bondname) - self.write_file('/sys/class/net/%s' % bondname + - '/bonding/xmit_hash_policy', hash_policy) - self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'], - hash_policy) - - def bond_get_xmit_hash_policy(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'xmit_hash_policy']) - - def bond_get_xmit_hash_policy_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY]) - - def bond_set_miimon(self, bondname, miimon): - if (self._link_cache_check([bondname, 'linkinfo', 'miimon'], - miimon)): - return - self.write_file('/sys/class/net/%s' % bondname + - '/bonding/miimon', miimon) - self._cache_update([bondname, 'linkinfo', 'miimon'], miimon) - - def bond_get_miimon(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'miimon']) - - def bond_get_miimon_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON]) - - def bond_set_mode(self, bondname, mode, prehook=None): - valid_modes = ['balance-rr', 'active-backup', 'balance-xor', - 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb'] - if not mode: - return - if mode not in valid_modes: - raise Exception('invalid mode %s' % mode) - if (self._link_cache_check([bondname, 'linkinfo', 'mode'], - mode)): - return - if prehook: - prehook(bondname) - self.write_file('/sys/class/net/%s' % bondname + '/bonding/mode', mode) - self._cache_update([bondname, 'linkinfo', 'mode'], mode) - - def bond_get_mode(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'mode']) - - def bond_get_mode_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE]) - - def bond_set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None): - if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'): - return - if (self._link_cache_check([bondname, 'linkinfo', 'lacp_rate'], - lacp_rate)): - return - if prehook: - prehook(bondname) - try: - self.write_file('/sys/class/net/%s' % bondname + - '/bonding/lacp_rate', lacp_rate) - except: - raise - finally: - if posthook: - prehook(bondname) - self._cache_update([bondname, 'linkinfo', - 'lacp_rate'], lacp_rate) - - def bond_get_lacp_rate(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'lacp_rate']) - - def bond_get_lacp_rate_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE]) - - def bond_set_lacp_bypass_allow(self, bondname, allow, prehook=None, posthook=None): - if self._link_cache_check([bondname, 'linkinfo', 'lacp_bypass'], allow): - return - if prehook: - prehook(bondname) - try: - self.write_file('/sys/class/net/%s' % bondname + - '/bonding/lacp_bypass', allow) - except: - raise - finally: - if posthook: - posthook(bondname) - self._cache_update([bondname, 'linkinfo', - 'lacp_bypass'], allow) - - def bond_get_lacp_bypass_allow(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'lacp_bypass']) - - def bond_get_lacp_bypass_allow_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS]) - - def bond_set_min_links(self, bondname, min_links, prehook=None): - if (self._link_cache_check([bondname, 'linkinfo', 'min_links'], - min_links)): - return - if prehook: - prehook(bondname) - self.write_file('/sys/class/net/%s/bonding/min_links' % bondname, - min_links) - self._cache_update([bondname, 'linkinfo', 'min_links'], min_links) - - def bond_get_min_links(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'min_links']) - - def get_min_links_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS]) - - def bond_get_ad_actor_system(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_system']) - - def bond_get_ad_actor_system_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM]) - - def bond_get_ad_actor_sys_prio(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_sys_prio']) - - def bond_get_ad_actor_sys_prio_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO]) - - def bond_get_num_unsol_na(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'num_unsol_na']) - - def bond_get_num_unsol_na_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]) - - def bond_get_num_grat_arp(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'num_grat_arp']) - - def bond_get_num_grat_arp_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]) - - def bond_get_updelay(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'updelay']) - - def bond_get_updelay_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY]) - - def bond_get_downdelay(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', 'downdelay']) - - def bond_get_downdelay_nl(self, bondname): - return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY]) - - def bond_enslave_slave(self, bondname, slave, prehook=None, posthook=None): - slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves']) - if slaves and slave in slaves: - return - if prehook: - prehook(slave) - self.write_file('/sys/class/net/%s' % bondname + - '/bonding/slaves', '+' + slave) - if posthook: - posthook(slave) - self._cache_update([bondname, 'linkinfo', 'slaves'], slave) - - def bond_remove_slave(self, bondname, slave): - slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves']) - if not slaves or slave not in slaves: - return - sysfs_bond_path = ('/sys/class/net/%s' % bondname + - '/bonding/slaves') - if not os.path.exists(sysfs_bond_path): - return - self.write_file(sysfs_bond_path, '-' + slave) - self._cache_delete([bondname, 'linkinfo', 'slaves'], slave) - - def bond_remove_slaves_all(self, bondname): - if not self._link_cache_get([bondname, 'linkinfo', 'slaves']): - return - slaves = None - sysfs_bond_path = ('/sys/class/net/%s' % bondname + - '/bonding/slaves') - try: - with open(sysfs_bond_path, 'r') as f: - slaves = f.readline().strip().split() - except IOError, e: - raise Exception('error reading slaves of bond %s (%s)' % (bondname, str(e))) - for slave in slaves: - self.link_down(slave) - try: - self.bond_remove_slave(bondname, slave) - except Exception, e: - if not ifupdownflags.flags.FORCE: - raise Exception('error removing slave %s from bond %s (%s)' % (slave, bondname, str(e))) - else: - pass - self._cache_delete([bondname, 'linkinfo', 'slaves']) - - @staticmethod - def bond_load_bonding_module(): - return utils.exec_command('%s -q bonding' % utils.modprobe_cmd) - - def create_bond(self, bondname): - if self.bond_exists(bondname): - return - # load_bonding_module() has already been run - self.write_file('/sys/class/net/bonding_masters', '+' + bondname) - self._cache_update([bondname], {}) - - def delete_bond(self, bondname): - if not os.path.exists('/sys/class/net/%s' % bondname): - return - self.write_file('/sys/class/net/bonding_masters', '-' + bondname) - self._cache_delete([bondname]) - - def bond_get_slaves(self, bondname): - slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves']) - if slaves: - return list(slaves) - slavefile = '/sys/class/net/%s/bonding/slaves' % bondname - if os.path.exists(slavefile): - buf = self.read_file_oneline(slavefile) - if buf: - slaves = buf.split() - if not slaves: - return [] - self._cache_update([bondname, 'linkinfo', 'slaves'], slaves) - return list(slaves) - - def bond_slave_exists(self, bond, slave): - slaves = self.bond_get_slaves(bond) - if not slaves: - return False - return slave in slaves - - @staticmethod - def bond_exists(bondname): - return os.path.exists('/sys/class/net/%s/bonding' % bondname) - - ################################################################################# - ################################## BRIDGE UTILS ################################# - ################################################################################# - - def create_bridge(self, bridgename): - if not LinkUtils.bridge_utils_is_installed: - return - if self.bridge_exists(bridgename): - return - utils.exec_command('%s addbr %s' % (utils.brctl_cmd, bridgename)) - self._cache_update([bridgename], {}) - - def delete_bridge(self, bridgename): - if not LinkUtils.bridge_utils_is_installed: - return - if not self.bridge_exists(bridgename): - return - utils.exec_command('%s delbr %s' % (utils.brctl_cmd, bridgename)) - self._cache_invalidate() - - def add_bridge_port(self, bridgename, bridgeportname): - """ Add port to bridge """ - if not LinkUtils.bridge_utils_is_installed: - return - ports = self._link_cache_get([bridgename, 'linkinfo', 'ports']) - if ports and ports.get(bridgeportname): - return - utils.exec_command('%s addif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname)) - self._cache_update([bridgename, 'linkinfo', 'ports', bridgeportname], {}) - - def delete_bridge_port(self, bridgename, bridgeportname): - """ Delete port from bridge """ - if not LinkUtils.bridge_utils_is_installed: - return - ports = self._link_cache_get([bridgename, 'linkinfo', 'ports']) - if not ports or not ports.get(bridgeportname): - return - utils.exec_command('%s delif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname)) - self._cache_delete([bridgename, 'linkinfo', 'ports', 'bridgeportname']) - - def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict): - portattrs = self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname]) - if portattrs == None: - portattrs = {} - for k, v in attrdict.iteritems(): - if ifupdownflags.flags.CACHE: - curval = portattrs.get(k) - if curval and curval == v: - continue - if k == 'unicast-flood': - self.write_file('/sys/class/net/%s/brport/unicast_flood' % bridgeportname, v) - elif k == 'multicast-flood': - self.write_file('/sys/class/net/%s/brport/multicast_flood' % bridgeportname, v) - elif k == 'learning': - self.write_file('/sys/class/net/%s/brport/learning' % bridgeportname, v) - elif k == 'arp-nd-suppress': - self.write_file('/sys/class/net/%s/brport/neigh_suppress' % bridgeportname, v) - else: - if not LinkUtils.bridge_utils_is_installed: - continue - utils.exec_command('%s set%s %s %s %s' % (utils.brctl_cmd, k, bridgename, bridgeportname, v)) - - def set_bridgeport_attr(self, bridgename, bridgeportname, - attrname, attrval): - if not LinkUtils.bridge_utils_is_installed: - return - if self._link_cache_check([bridgename, 'linkinfo', 'ports', bridgeportname, attrname], attrval): - return - utils.exec_command('%s set%s %s %s %s' % - (utils.brctl_cmd, - attrname, - bridgename, - bridgeportname, - attrval)) - - def set_bridge_attrs(self, bridgename, attrdict): - for k, v in attrdict.iteritems(): - if not v: - continue - if self._link_cache_check([bridgename, 'linkinfo', k], v): - continue - try: - if k == 'igmp-version': - self.write_file('/sys/class/net/%s/bridge/' - 'multicast_igmp_version' % bridgename, v) - elif k == 'mld-version': - self.write_file('/sys/class/net/%s/bridge/' - 'multicast_mld_version' % bridgename, v) - elif k == 'vlan-protocol': - self.write_file('/sys/class/net/%s/bridge/' - 'vlan_protocol' % bridgename, - VlanProtocols.ETHERTYPES_TO_ID.get(v.upper(), - None)) - elif k == 'vlan-stats': - self.write_file('/sys/class/net/%s/bridge/' - 'vlan_stats_enabled' % bridgename, v) - elif k == 'mcstats': - self.write_file('/sys/class/net/%s/bridge/' - 'multicast_stats_enabled' % bridgename, v) - else: - if not LinkUtils.bridge_utils_is_installed: - continue - cmd = ('%s set%s %s %s' % - (utils.brctl_cmd, k, bridgename, v)) - utils.exec_command(cmd) - except Exception, e: - self.logger.warn('%s: %s' % (bridgename, str(e))) - pass - - def set_bridge_attr(self, bridgename, attrname, attrval): - if self._link_cache_check([bridgename, 'linkinfo', attrname], attrval): - return - if attrname == 'igmp-version': - self.write_file('/sys/class/net/%s/bridge/multicast_igmp_version' - % bridgename, attrval) - elif attrname == 'mld-version': - self.write_file('/sys/class/net/%s/bridge/multicast_mld_version' - % bridgename, attrval) - elif attrname == 'vlan-protocol': - self.write_file('/sys/class/net/%s/bridge/vlan_protocol' - % bridgename, VlanProtocols.ETHERTYPES_TO_ID[attrval.upper()]) - elif attrname == 'vlan-stats': - self.write_file('/sys/class/net/%s/bridge/vlan_stats_enabled' - % bridgename, attrval) - elif attrname == 'mcstats': - self.write_file('/sys/class/net/%s/bridge/multicast_stats_enabled' - % bridgename, attrval) - else: - if not LinkUtils.bridge_utils_is_installed: - return - cmd = '%s set%s %s %s' % (utils.brctl_cmd, - attrname, bridgename, attrval) - utils.exec_command(cmd) - - def get_bridge_attrs(self, bridgename): - attrs = self._link_cache_get([bridgename, 'linkinfo']) - no_ints_attrs = {} - for key, value in attrs.items(): - if type(key) == str: - no_ints_attrs[key] = value - return no_ints_attrs - - def get_bridgeport_attrs(self, bridgename, bridgeportname): - return self._link_cache_get([bridgename, 'linkinfo', 'ports', - bridgeportname]) - - def get_bridgeport_attr(self, bridgename, bridgeportname, attrname): - return self._link_cache_get([bridgename, 'linkinfo', 'ports', - bridgeportname, attrname]) - - @staticmethod - def bridge_set_stp(bridge, stp_state): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s stp %s %s' % (utils.brctl_cmd, bridge, stp_state)) - - def bridge_get_stp(self, bridge): - sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' % bridge - if not os.path.exists(sysfs_stpstate): - return 'error' - stpstate = self.read_file_oneline(sysfs_stpstate) - if not stpstate: - return 'error' - try: - if int(stpstate) > 0: - return 'yes' - elif int(stpstate) == 0: - return 'no' - except: - return 'unknown' - - @staticmethod - def _conv_value_to_user(s): - try: - ret = int(s) / 100 - return '%d' % ret - except: - return None - - def read_value_from_sysfs(self, filename, preprocess_func): - value = self.read_file_oneline(filename) - if not value: - return None - return preprocess_func(value) - - @staticmethod - def bridge_set_ageing(bridge, ageing): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setageing %s %s' % (utils.brctl_cmd, bridge, ageing)) - - def bridge_get_ageing(self, bridge): - return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time' - % bridge, self._conv_value_to_user) - - @staticmethod - def set_bridgeprio(bridge, prio): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setbridgeprio %s %s' % (utils.brctl_cmd, bridge, prio)) - - def get_bridgeprio(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/priority' % bridge) - - @staticmethod - def bridge_set_fd(bridge, fd): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setfd %s %s' % (utils.brctl_cmd, bridge, fd)) - - def bridge_get_fd(self, bridge): - return self.read_value_from_sysfs( - '/sys/class/net/%s/bridge/forward_delay' - % bridge, self._conv_value_to_user) - - def bridge_set_gcint(self, bridge, gcint): - raise Exception('set_gcint not implemented') - - @staticmethod - def bridge_set_hello(bridge, hello): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s sethello %s %s' % (utils.brctl_cmd, bridge, hello)) - - def bridge_get_hello(self, bridge): - return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time' - % bridge, self._conv_value_to_user) - - @staticmethod - def bridge_set_maxage(bridge, maxage): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmaxage %s %s' % (utils.brctl_cmd, bridge, maxage)) - - def bridge_get_maxage(self, bridge): - return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age' - % bridge, self._conv_value_to_user) - - @staticmethod - def bridge_set_pathcost(bridge, port, pathcost): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setpathcost %s %s %s' % (utils.brctl_cmd, bridge, port, pathcost)) - - def bridge_get_pathcost(self, bridge, port): - return self.read_file_oneline('/sys/class/net/%s/brport/path_cost' - % port) - - @staticmethod - def bridge_set_portprio(bridge, port, prio): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setportprio %s %s %s' % (utils.brctl_cmd, bridge, port, prio)) - - def bridge_get_portprio(self, bridge, port): - return self.read_file_oneline('/sys/class/net/%s/brport/priority' - % port) - - @staticmethod - def bridge_set_hashmax(bridge, hashmax): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s sethashmax %s %s' % (utils.brctl_cmd, bridge, hashmax)) - - def bridge_get_hashmax(self, bridge): - return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max' - % bridge) - - @staticmethod - def bridge_set_hashel(bridge, hashel): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s sethashel %s %s' % (utils.brctl_cmd, bridge, hashel)) - - def bridge_get_hashel(self, bridge): - return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity' - % bridge) - - @staticmethod - def bridge_set_mclmc(bridge, mclmc): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmclmc %s %s' % (utils.brctl_cmd, bridge, mclmc)) - - def bridge_get_mclmc(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_last_member_count' - % bridge) - - @staticmethod - def bridge_set_mcrouter(bridge, mcrouter): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmcrouter %s %s' % (utils.brctl_cmd, bridge, mcrouter)) - - def bridge_get_mcrouter(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_router' % bridge) - - @staticmethod - def bridge_set_mcsnoop(bridge, mcsnoop): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmcsnoop %s %s' % (utils.brctl_cmd, bridge, mcsnoop)) - - def bridge_get_mcsnoop(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_snooping' % bridge) - - @staticmethod - def bridge_set_mcsqc(bridge, mcsqc): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmcsqc %s %s' % (utils.brctl_cmd, bridge, mcsqc)) - - def bridge_get_mcsqc(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_startup_query_count' - % bridge) - - @staticmethod - def bridge_set_mcqifaddr(bridge, mcqifaddr): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmcqifaddr %s %s' % (utils.brctl_cmd, bridge, mcqifaddr)) - - def bridge_get_mcqifaddr(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr' - % bridge) - - @staticmethod - def bridge_set_mcquerier(bridge, mcquerier): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmcquerier %s %s' % (utils.brctl_cmd, bridge, mcquerier)) - - def bridge_get_mcquerier(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_querier' % bridge) - - def bridge_set_mcqv4src(self, bridge, vlan, mcquerier): - try: - vlan = int(vlan) - except: - self.logger.info('%s: set mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e))) - return - if vlan == 0 or vlan > 4095: - self.logger.warn('mcqv4src vlan \'%d\' invalid range' % vlan) - return - - ip = mcquerier.split('.') - if len(ip) != 4: - self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier) - return - for k in ip: - if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255: - self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier) - return - - if not LinkUtils.bridge_utils_is_installed: - return - - utils.exec_command('%s setmcqv4src %s %d %s' % - (utils.brctl_cmd, bridge, vlan, mcquerier)) - - def bridge_del_mcqv4src(self, bridge, vlan): - if not LinkUtils.bridge_utils_is_installed: - return - try: - vlan = int(vlan) - except: - self.logger.info('%s: del mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e))) - return - utils.exec_command('%s delmcqv4src %s %d' % (utils.brctl_cmd, bridge, vlan)) - - def bridge_get_mcqv4src(self, bridge, vlan=None): - if not LinkUtils.bridge_utils_is_installed: - return {} - if not self.supported_command['showmcqv4src']: - return {} - mcqv4src = {} - try: - mcqout = utils.exec_command('%s showmcqv4src %s' % - (utils.brctl_cmd, bridge)) - except Exception as e: - s = str(e).lower() - if 'never heard' in s: - msg = ('%s showmcqv4src: skipping unsupported command' - % utils.brctl_cmd) - self.logger.info(msg) - self.supported_command['showmcqv4src'] = False - return {} - raise - if not mcqout: - return {} - mcqlines = mcqout.splitlines() - for l in mcqlines[1:]: - l = l.strip() - k, d, v = l.split('\t') - if not k or not v: - continue - mcqv4src[k] = v - if vlan: - return mcqv4src.get(vlan) - return mcqv4src - - def bridge_get_mcqv4src_sysfs(self, bridge, vlan=None): - if not LinkUtils.bridge_utils_is_installed: - return {} - if not self.supported_command['showmcqv4src']: - return {} - if ifupdownflags.flags.PERFMODE: - return {} - mcqv4src = {} - try: - filename = '/sys/class/net/%s/bridge/multicast_v4_queriers' % bridge - if os.path.exists(filename): - for line in self.read_file(filename) or []: - vlan_id, ip = line.split('=') - mcqv4src[vlan_id] = ip.strip() - except Exception as e: - s = str(e).lower() - msg = ('%s showmcqv4src: skipping unsupported command' - % utils.brctl_cmd) - self.logger.info(msg) - self.supported_command['showmcqv4src'] = False - return {} - if vlan: - return mcqv4src.get(vlan) - return mcqv4src - - @staticmethod - def bridge_set_mclmi(bridge, mclmi): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmclmi %s %s' % (utils.brctl_cmd, bridge, mclmi)) - - def bridge_get_mclmi(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_last_member_interval' - % bridge) - - @staticmethod - def bridge_set_mcmi(bridge, mcmi): - if not LinkUtils.bridge_utils_is_installed: - return - utils.exec_command('%s setmcmi %s %s' % (utils.brctl_cmd, bridge, mcmi)) - - def bridge_get_mcmi(self, bridge): - return self.read_file_oneline( - '/sys/class/net/%s/bridge/multicast_membership_interval' - % bridge) - - @staticmethod - def bridge_exists(bridge): - return os.path.exists('/sys/class/net/%s/bridge' % bridge) - - @staticmethod - def is_bridge_port(ifacename): - return os.path.exists('/sys/class/net/%s/brport' % ifacename) - - @staticmethod - def bridge_port_exists(bridge, bridgeportname): - try: - return os.path.exists('/sys/class/net/%s/brif/%s' % (bridge, bridgeportname)) - except: - return False - - @staticmethod - def get_bridge_ports(bridgename): - try: - return os.listdir('/sys/class/net/%s/brif/' % bridgename) - except: - return [] - - def reset_addr_cache(self, ifname): - try: - linkCache.links[ifname]['addrs'] = {} - self.logger.debug('%s: reset address cache' % ifname) - except: - pass - - def get_ipv6_addrgen_mode(self, ifname): - try: - return self._cache_get('link', [ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] - except: - # default to 0 (eui64) - return 0 - - def ipv6_addrgen(self, ifname, addrgen, link_created): - try: - # IFLA_INET6_ADDR_GEN_MODE values: - # 0 = eui64 - # 1 = none - if self._link_cache_get([ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] == addrgen: - self.logger.debug('%s: ipv6 addrgen already %s' % (ifname, 'off' if addrgen else 'on')) - return - - disabled_ipv6 = self.read_file_oneline('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % ifname) - if not disabled_ipv6 or int(disabled_ipv6) == 1: - self.logger.info('%s: cannot set addrgen: ipv6 is disabled on this device' % ifname) - return - - if int(self._link_cache_get([ifname, 'mtu'])) < 1280: - self.logger.info('%s: ipv6 addrgen is disabled on device with MTU ' - 'lower than 1280: cannot set addrgen %s' % (ifname, 'off' if addrgen else 'on')) - return - except (KeyError, TypeError): - self.logger.debug('%s: ipv6 addrgen probably not supported or disabled on this device' % ifname) - return - except Exception: - pass - - if not link_created: - # When setting addrgenmode it is necessary to flap the macvlan - # device. After flapping the device we also need to re-add all - # the user configuration. The best way to add the user config - # is to flush our internal address cache - self.reset_addr_cache(ifname) - - cmd = 'link set dev %s addrgenmode %s' % (ifname, Link.ifla_inet6_addr_gen_mode_dict.get(addrgen)) - - is_link_up = self.is_link_up(ifname) - - if is_link_up: - self.link_down(ifname) - - #if LinkUtils.ipbatch: - # self.add_to_batch(cmd) - #else: - # because this command might fail on older kernel its better to not batch it - utils.exec_command('%s %s' % (utils.ip_cmd, cmd)) - - if is_link_up: - self.link_up(ifname) diff --git a/ifupdown2/ifupdownaddons/cache.py b/ifupdown2/ifupdownaddons/cache.py index b74f3276..483b59f9 100644 --- a/ifupdown2/ifupdownaddons/cache.py +++ b/ifupdown2/ifupdownaddons/cache.py @@ -4,8 +4,6 @@ # Author: Roopa Prabhu, roopa@cumulusnetworks.com # -import pprint - class MSTPAttrsCache(): bridges = {} @@ -24,91 +22,3 @@ def set(cls, bridgename, attrs): @classmethod def invalidate(cls): MSTPAttrsCache.bridges = {} - - -class linkCache(): - """ This class contains methods and instance variables to cache - link info """ - - """ { : { 'ifindex': , - 'mtu': , - 'state' : ', - 'flags' : , - 'kind' : , - 'linkinfo' : { : , - : , - : { - } """ - 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)) diff --git a/ifupdown2/ifupdownaddons/dhclient.py b/ifupdown2/ifupdownaddons/dhclient.py index 4d907f59..7e67bf78 100644 --- a/ifupdown2/ifupdownaddons/dhclient.py +++ b/ifupdown2/ifupdownaddons/dhclient.py @@ -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) diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index ca54743a..6ac92753 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -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 @@ def _get_bridge_and_port_attrs_from_cache(self, bridgename): 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 @@ def _get_bridge_and_port_attrs_from_cache(self, bridgename): 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 @@ def _get_bridge_and_port_attrs_from_cache(self, bridgename): 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 @@ def set_bridge_port_attr(self, bridgename, portname, attrname, value, json_attr= 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 @@ def set_bridge_attr(self, bridgename, attrname, attrvalue, check=True): 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 @@ def set_bridge_treeprio(self, bridgename, attrvalue, check=True): 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): diff --git a/ifupdown2/lib/__init__.py b/ifupdown2/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ifupdown2/lib/addon.py b/ifupdown2/lib/addon.py new file mode 100644 index 00000000..12c50201 --- /dev/null +++ b/ifupdown2/lib/addon.py @@ -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() diff --git a/ifupdown2/lib/base_objects.py b/ifupdown2/lib/base_objects.py new file mode 100644 index 00000000..786edc52 --- /dev/null +++ b/ifupdown2/lib/base_objects.py @@ -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) diff --git a/ifupdown2/lib/dry_run.py b/ifupdown2/lib/dry_run.py new file mode 100644 index 00000000..b0c6f9a8 --- /dev/null +++ b/ifupdown2/lib/dry_run.py @@ -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 diff --git a/ifupdown2/lib/exceptions.py b/ifupdown2/lib/exceptions.py new file mode 100644 index 00000000..25e928b7 --- /dev/null +++ b/ifupdown2/lib/exceptions.py @@ -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 diff --git a/ifupdown2/lib/io.py b/ifupdown2/lib/io.py new file mode 100644 index 00000000..057b3250 --- /dev/null +++ b/ifupdown2/lib/io.py @@ -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"))) diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py new file mode 100644 index 00000000..704d1200 --- /dev/null +++ b/ifupdown2/lib/iproute2.py @@ -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 diff --git a/ifupdown2/lib/log.py b/ifupdown2/lib/log.py new file mode 100644 index 00000000..5f194043 --- /dev/null +++ b/ifupdown2/lib/log.py @@ -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) diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py new file mode 100644 index 00000000..6b188bd6 --- /dev/null +++ b/ifupdown2/lib/nlcache.py @@ -0,0 +1,3216 @@ +#!/usr/bin/env python +# +# 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 +# +# Netlink cache -- +# + +import os +import socket +import struct +import signal +import inspect +import logging +import threading +import traceback + +from ipaddr import IPNetwork +from logging import DEBUG, WARNING +from collections import OrderedDict + +try: + from ifupdown2.lib.sysfs import Sysfs + from ifupdown2.lib.base_objects import BaseObject + + from ifupdown2.nlmanager.nlpacket import \ + Address, \ + Netconf, \ + Link, \ + Route, \ + AF_MPLS, \ + NetlinkPacket, \ + NLM_F_REQUEST, \ + RTM_NEWLINK, \ + RTM_SETLINK, \ + RTM_DELLINK, \ + RTM_NEWADDR, \ + RTM_DELADDR, \ + RTMGRP_ALL, \ + NLMSG_DONE, \ + NLM_F_REQUEST, \ + NLM_F_CREATE, \ + NLM_F_ACK, \ + RT_SCOPES, \ + INFINITY_LIFE_TIME + + import ifupdown2.nlmanager.nlpacket as nlpacket + import ifupdown2.nlmanager.nllistener as nllistener + import ifupdown2.nlmanager.nlmanager as nlmanager + import ifupdown2.ifupdown.statemanager as statemanager +except: + from lib.sysfs import Sysfs + from lib.base_objects import BaseObject + + from nlmanager.nlpacket import \ + Address, \ + Netconf, \ + Link, \ + Route, \ + AF_MPLS, \ + NetlinkPacket, \ + NLM_F_REQUEST, \ + RTM_NEWLINK, \ + RTM_SETLINK, \ + RTM_DELLINK, \ + RTM_NEWADDR, \ + RTM_DELADDR, \ + RTMGRP_ALL, \ + NLMSG_DONE, \ + NLM_F_REQUEST, \ + NLM_F_CREATE, \ + NLM_F_ACK, \ + RT_SCOPES, \ + INFINITY_LIFE_TIME + + import nlmanager.nlpacket as nlpacket + import nlmanager.nllistener as nllistener + import nlmanager.nlmanager as nlmanager + import ifupdown.statemanager as statemanager + + +log = logging.getLogger() + + +class NetlinkListenerWithCacheErrorNotInitialized(Exception): + """ + If NetlinkListenerWithCache fails on __init__() or / start() + we need to raise this custom exception. + """ + pass + + +class NetlinkError(Exception): + def __init__(self, exception, prefix=None, ifname=None): + netlink_exception_message = ['netlink'] + + if ifname: + netlink_exception_message.append(ifname) + + if prefix: + netlink_exception_message.append(prefix) + + netlink_exception_message.append(str(exception)) + super(NetlinkError, self).__init__(": ".join(netlink_exception_message)) + + +class NetlinkCacheError(Exception): + pass + + +class NetlinkCacheIfnameNotFoundError(NetlinkCacheError): + pass + + +class NetlinkCacheIfindexNotFoundError(NetlinkCacheError): + pass + + +class _NetlinkCache: + """ Netlink Cache Class """ + + # we need to store these attributes in a static list to be able to iterate + # through it when comparing Address objects in add_address() + # we ignore IFA_CACHEINFO and IFA_FLAGS + _ifa_attributes = ( + Address.IFA_ADDRESS, + Address.IFA_LOCAL, + Address.IFA_LABEL, + Address.IFA_BROADCAST, + Address.IFA_ANYCAST, + # Address.IFA_CACHEINFO, + Address.IFA_MULTICAST, + # Address.IFA_FLAGS + ) + + def __init__(self): + # sysfs API + self.__sysfs = Sysfs + self.__sysfs.cache = self + + self._link_cache = {} + self._addr_cache = {} + self._bridge_vlan_cache = {} + + # helper dictionaries + # ifindex: ifname + # ifname: ifindex + self._ifname_by_ifindex = {} + self._ifindex_by_ifname = {} + + self._ifname_by_ifindex_sysfs = {} + self._ifindex_by_ifname_sysfs = {} + + # master/slave(s) dictionary + # master_ifname: [slave_ifname, slave_ifname] + self._masters_and_slaves = {} + + # slave/master dictionary + # slave_ifname: master_ifname + self._slaves_master = {} + + # netconf cache data-structure schema: + # { + # family: { + # ifindex: obj + # } + # } + self._netconf_cache = { + socket.AF_INET: {}, + socket.AF_INET6: {}, + AF_MPLS: {} + } + # custom lock mechanism for netconf cache + self._netconf_cache_lock = threading.Lock() + + # RLock is needed because we don't want to have separate handling in + # get_ifname, get_ifindex and all the API function + self._cache_lock = threading.RLock() + + # After sending a RTM_DELLINK request (ip link del DEV) we don't + # automatically receive an RTM_DELLINK notification but instead we + # have 3 to 5 RTM_NEWLINK notifications (first the device goes + # admin-down then, goes through other steps that send notifications... + # Because of this behavior the cache is out of sync and may cause + # issues. To work-around this behavior we can ignore RTM_NEWLINK for a + # given ifname until we receive the RTM_DELLINK. That way our cache is + # not stale. When deleting a link, ifupdown2 uses: + # - NetlinkListenerWithCache:link_del(ifname) + # Before sending the RTM_DELLINK netlink packet we: + # - register the ifname in the _ignore_rtm_newlinkq + # - force purge the cache because we are not notified right away + # - for every RTM_NEWLINK notification we check _ignore_rtm_newlinkq + # to see if we need to ignore that packet + # - for every RTM_DELLINK notification we check if we have a + # corresponding entry in _ignore_rtm_newlinkq and remove it + self._ignore_rtm_newlinkq = list() + self._ignore_rtm_newlinkq_lock = threading.Lock() + + # After sending a no master request (IFLA_MASTER=0) the kernels send + # 2 or 3 notifications (with IFLA_MASTER) before sending the final + # notification where IFLA_MASTER is removed. For performance purposes + # we don't wait for those notifications, we simply update the cache + # to reflect the change (if we got an ACK on the nomaster request). + # Those extra notification re-add the former slave to it's master + # (in our internal data-structures at least). ifupdown2 relies on + # the cache to get accurate information, this puts the cache in an + # unreliable state. We can detected this bad state and avoid it. Afer + # a nomaster request we "register" the device as "nomaster", meaning + # that we will manually remove the IFLA_MASTER attribute from any + # subsequent packet, until the final packet arrives - then unregister + # the device from the nomasterq. + # We need an extra data-structure and lock mechanism for this: + self._rtm_newlink_nomasterq = list() + self._rtm_newlink_nomasterq_lock = threading.Lock() + + # In the scenario of NetlinkListenerWithCache, the listener thread + # decode netlink packets and perform caching operation based on their + # respective msgtype add_link for RTM_NEWLINK, remove_link for DELLINK + # In some cases the main thread is creating a new device with: + # NetlinkListenerWithCache.link_add() + # the request is sent and the cache won't have any knowledge of this + # new link until we receive a NEWLINK notification on the listener + # socket meanwhile the main thread keeps going. The main thread may + # query the cache for the newly created device but the cache might not + # know about it yet thus creating unexpected situation in the main + # thread operations. We need to provide a mechanism to block the main + # thread until the desired notification is processed. The main thread + # can call: + # register_wait_event(ifname, netlink_msgtype) + # to register an event for device name 'ifname' and netlink msgtype. + # The main thread should then call wait_event to sleep until the + # notification is received the NetlinkListenerWithCache provides the + # following API: + # tx_nlpacket_get_response_with_error_and_wait_for_cache(ifname, nl_packet) + # to handle both packet transmission, error handling and cache event + self._wait_event = None + self._wait_event_alarm = threading.Event() + + def __handle_type_error(self, func_name, data, exception, return_value): + """ + TypeError shouldn't happen but if it does, we are prepared to log and recover + """ + log.debug('nlcache: %s: %s: TypeError: %s' % (func_name, data, str(exception))) + return return_value + + def __unslave_nolock(self, slave, master=None): + """ + WARNING: LOCK SHOULD BE ACQUIRED BEFORE CALLING THIS FUNCTION + + When unslaving a device we need to manually clear and update our internal + data structures to avoid keeping stale information before receiving a proper + netlink notification. + + Dictionaries: + - master_and_slaves + - slaves_master + - bridge_vlan_cache + + :param master: + :param slave: + :return: + """ + try: + del self._link_cache[slave].attributes[Link.IFLA_MASTER] + except: + pass + + try: + if not master: + master = self._slaves_master[slave] + + self._masters_and_slaves[master].remove(slave) + except (KeyError, ValueError): + for master, slaves_set in self._masters_and_slaves.iteritems(): + if slave in slaves_set: + slaves_set.remove(slave) + break + + try: + del self._slaves_master[slave] + except KeyError: + pass + + try: + del self._bridge_vlan_cache[slave] + except KeyError: + pass + + def append_to_ignore_rtm_newlinkq(self, ifname): + """ + Register device 'ifname' to the ignore_rtm_newlinkq list pending + RTM_DELLINK (see comments above _ignore_rtm_newlinkq declaration) + """ + with self._ignore_rtm_newlinkq_lock: + self._ignore_rtm_newlinkq.append(ifname) + + def remove_from_ignore_rtm_newlinkq(self, ifname): + """ Unregister ifname from ignore_newlinkq list """ + try: + with self._ignore_rtm_newlinkq_lock: + self._ignore_rtm_newlinkq.remove(ifname) + except ValueError: + pass + + def append_to_rtm_newlink_nomasterq(self, ifname): + """ Register device 'ifname' to the _ignore_rtm_newlink_nomasterq """ + with self._rtm_newlink_nomasterq_lock: + self._rtm_newlink_nomasterq.append(ifname) + + def remove_from_rtm_newlink_nomasterq(self, ifname): + """ Unregister ifname from _ignore_rtm_newlink_nomasterq list """ + try: + with self._rtm_newlink_nomasterq_lock: + self._rtm_newlink_nomasterq.remove(ifname) + except ValueError: + pass + + def register_wait_event(self, ifname, msgtype): + """ + Register a cache "wait event" for device named 'ifname' and packet + type msgtype + + We only one wait_event to be registered. Currently we don't support + multi-threaded application so we need to had a strict check. In the + future we could have a wait_event queue for multiple thread could + register wait event. + :param ifname: target device + :param msgtype: netlink message type (RTM_NEWLINK, RTM_DELLINK etc.) + :return: boolean: did we successfully register a wait_event? + """ + with self._cache_lock: + if self._wait_event: + return False + self._wait_event = (ifname, msgtype) + return True + + def wait_event(self): + """ + Sleep until cache event happened in netlinkq thread or timeout expired + :return: None + + We set an arbitrary timeout at 1sec in case the kernel doesn't send + out a notification for the event we want to wait for. + """ + if not self._wait_event_alarm.wait(1): + log.debug('nlcache: wait event alarm timeout expired for device "%s" and netlink packet type: %s' + % (self._wait_event[0], NetlinkPacket.type_to_string.get(self._wait_event[1], str(self._wait_event[1])))) + with self._cache_lock: + self._wait_event = None + self._wait_event_alarm.clear() + + def unregister_wait_event(self): + """ + Clear current wait event (cache can only handle one at once) + :return: + """ + with self._cache_lock: + self._wait_event = None + self._wait_event_alarm.clear() + + def override_link_flag(self, ifname, flags): + # TODO: dont override all the flags just turn on/off IFF_UP + try: + with self._cache_lock: + self._link_cache[ifname].flags = flags + except: + pass + + def override_link_mtu(self, ifname, mtu): + """ + Manually override link mtu and ignore any failures + :param ifname: + :param mtu: + :return: + """ + try: + with self._cache_lock: + self._link_cache[ifname].attributes[Link.IFLA_MTU].value = mtu + except: + pass + + def override_cache_unslave_link(self, slave, master): + """ + Manually update the cache unslaving SLAVE from MASTER + + When calling link_set_nomaster, we don't want to wait for the RTM_GETLINK + notification - if the operation return with NL_SUCCESS we can manually + update our cache and move on. + + :param master: + :param slave: + :return: + """ + with self._cache_lock: + self.__unslave_nolock(slave, master) + + def DEBUG_IFNAME(self, ifname, with_addresses=False): + """ + A very useful function to use while debugging, it dumps the netlink + packet with debug and color output. + """ + import logging + root = logging.getLogger() + + level = root.level + + try: + root.setLevel(DEBUG) + for handler in root.handlers: + handler.setLevel(DEBUG) + + nllistener.log.setLevel(DEBUG) + nlpacket.log.setLevel(DEBUG) + nlmanager.log.setLevel(DEBUG) + with self._cache_lock: + obj = self._link_cache[ifname] + save_debug = obj.debug + obj.debug = True + obj.dump() + obj.debug = save_debug + + #if with_addresses: + # addrs = self._addr_cache.get(ifname, []) + # log.error('ADDRESSES=%s' % addrs) + # for addr in addrs: + # save_debug = addr.debug + # addr.debug = True + # addr.dump() + # addr.debug = save_debug + # log.error('-----------') + # log.error('-----------') + # log.error('-----------') + except: + traceback.print_exc() + # TODO: save log_level at entry and re-apply it after the dump + nllistener.log.setLevel(WARNING) + nlpacket.log.setLevel(WARNING) + nlmanager.log.setLevel(WARNING) + + root.setLevel(level) + for handler in root.handlers: + handler.setLevel(level) + + def DEBUG_MSG(self, msg): + import logging + root = logging.getLogger() + level = root.level + + try: + root.setLevel(DEBUG) + for handler in root.handlers: + handler.setLevel(DEBUG) + + nllistener.log.setLevel(DEBUG) + nlpacket.log.setLevel(DEBUG) + nlmanager.log.setLevel(DEBUG) + + save_debug = msg.debug + msg.debug = True + msg.dump() + msg.debug = save_debug + except: + traceback.print_exc() + # TODO: save log_level at entry and re-apply it after the dump + nllistener.log.setLevel(WARNING) + nlpacket.log.setLevel(WARNING) + nlmanager.log.setLevel(WARNING) + + root.setLevel(level) + for handler in root.handlers: + handler.setLevel(level) + + def _populate_sysfs_ifname_ifindex_dicts(self): + ifname_by_ifindex_dict = {} + ifindex_by_ifname_dict = {} + try: + for dir_name in os.listdir('/sys/class/net/'): + try: + with open('/sys/class/net/%s/ifindex' % dir_name) as f: + ifindex = int(f.readline()) + ifname_by_ifindex_dict[ifindex] = dir_name + ifindex_by_ifname_dict[dir_name] = ifindex + except (IOError, ValueError): + pass + except OSError: + pass + with self._cache_lock: + self._ifname_by_ifindex_sysfs = ifname_by_ifindex_dict + self._ifindex_by_ifname_sysfs = ifindex_by_ifname_dict + + def get_ifindex(self, ifname): + """ + Return device index or raise NetlinkCacheIfnameNotFoundError + :param ifname: + :return: int + :raise: NetlinkCacheIfnameNotFoundError(NetlinkCacheError) + """ + try: + with self._cache_lock: + return self._ifindex_by_ifname[ifname] + except KeyError: + # We assume that if the user requested a valid device ifindex but + # for some reason we don't find any trace of it in our cache, we + # then use sysfs to make sure that this device exists and fill our + # internal help dictionaries. + with self._cache_lock: + ifindex = self._ifindex_by_ifname_sysfs.get(ifname) + + if ifindex: + return ifindex + self._populate_sysfs_ifname_ifindex_dicts() + try: + return self._ifindex_by_ifname_sysfs[ifname] + except KeyError: + # if we still haven't found any trace of the requested device + # we raise a custom exception + raise NetlinkCacheIfnameNotFoundError('ifname %s not present in cache' % ifname) + + def get_ifname(self, ifindex): + """ + Return device name or raise NetlinkCacheIfindexNotFoundError + :param ifindex: + :return: str + :raise: NetlinkCacheIfindexNotFoundError (NetlinkCacheError) + """ + try: + with self._cache_lock: + return self._ifname_by_ifindex[ifindex] + except KeyError: + # We assume that if the user requested a valid device ifname but + # for some reason we don't find any trace of it in our cache, we + # then use sysfs to make sure that this device exists and fill our + # internal help dictionaries. + with self._cache_lock: + ifname = self._ifname_by_ifindex_sysfs.get(ifindex) + + if ifname: + return ifname + self._populate_sysfs_ifname_ifindex_dicts() + try: + return self._ifname_by_ifindex_sysfs[ifindex] + except KeyError: + # if we still haven't found any trace of the requested device + # we raise a custom exception + raise NetlinkCacheIfindexNotFoundError('ifindex %s not present in cache' % ifindex) + + def link_exists(self, ifname): + """ + Check if we have a cache entry for device 'ifname' + :param ifname: device name + :return: boolean + """ + with self._cache_lock: + return ifname in self._link_cache + + def link_is_up(self, ifname): + """ + Check if device 'ifname' has IFF_UP flag + :param ifname: + :return: boolean + """ + try: + with self._cache_lock: + return self._link_cache[ifname].flags & Link.IFF_UP + except (KeyError, TypeError): + # ifname is not present in the cache + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False) + + def link_is_loopback(self, ifname): + """ + Check if device has IFF_LOOPBACK flag + :param ifname: + :return: boolean + """ + try: + with self._cache_lock: + return self._link_cache[ifname].flags & Link.IFF_LOOPBACK + # IFF_LOOPBACK should be enough, otherwise we can also check for + # link.device_type & Link.ARPHRD_LOOPBACK + except (KeyError, AttributeError): + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False) + + def link_exists_and_up(self, ifname): + """ + Check if device exists and has IFF_UP flag set + :param ifname: + :return: tuple (boolean, boolean) -> (link_exists, link_is_up) + """ + try: + with self._cache_lock: + return True, self._link_cache[ifname].flags & Link.IFF_UP + except KeyError: + # ifname is not present in the cache + return False, False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=(False, False)) + + def link_is_bridge(self, ifname): + return self.get_link_kind(ifname) == 'bridge' + + def get_link_kind(self, ifname): + """ + Return link IFLA_INFO_KIND + :param ifname: + :return: string + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_KIND] + except (KeyError, AttributeError): + return None + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None) + + def get_link_mtu(self, ifname): + """ + Return link IFLA_MTU + :param ifname: + :return: int + """ + return self.get_link_attribute(ifname, Link.IFLA_MTU, default=0) + + def get_link_mtu_str(self, ifname): + """ + Return link IFLA_MTU as string + :param ifname: + :return: str + """ + return str(self.get_link_mtu(ifname)) + + def get_link_address(self, ifname): + """ + Return link IFLA_ADDRESS + :param ifname: + :return: str + """ + packet = None + default_value = "" + try: + with self._cache_lock: + packet = self._link_cache[ifname] + return packet.attributes[Link.IFLA_ADDRESS].value.lower() + except (KeyError, AttributeError): + # KeyError will be raised if: + # - ifname is missing from the cache (but link_exists should be called prior this call) + # - IFLA_ADDRESS is missing + # AttributeError can also be raised if attributes[IFLA_ADDRESS] returns None + # If the packet is tagged as a REQUEST packet (priv_flags) we should query sysfs + # otherwise default_value is returned. + if packet and packet.priv_flags & NLM_F_REQUEST: + return self.__sysfs.get_link_address(ifname) + else: + return default_value + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), default_value) + + def get_link_address_raw(self, ifname): + """ + Return link IFLA_ADDRESS as integer + :param ifname: + :return: int + """ + return self.get_link_attribute_raw(ifname, Link.IFLA_ADDRESS, default=0) + + def get_link_alias(self, ifname): + """ + Return link IFLA_IFALIAS + :param ifname: + :return: str + """ + return self.get_link_attribute(ifname, Link.IFLA_IFALIAS) + + def get_link_attribute(self, ifname, attr, default=None): + """ + Return link attribute 'attr'.value + :param ifname: + :param attr: + :param default: + :return: + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[attr].value + except (KeyError, AttributeError): + return default + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default) + + def get_link_attribute_raw(self, ifname, attr, default=None): + """ + Return link attribute 'attr'.raw + :param ifname: + :param attr: + :param default: + :return: + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[attr].raw + except (KeyError, AttributeError): + return default + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default) + + def get_link_slave_kind(self, ifname): + """ + Return device slave kind + :param ifname: + :return: str + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND] + except (KeyError, AttributeError): + return None + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None) + + def get_link_info_data_attribute(self, ifname, info_data_attribute, default=None): + """ + Return device linkinfo:info_data attribute or default value + :param ifname: + :param info_data_attribute: + :param default: + :return: + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][info_data_attribute] + except (KeyError, AttributeError): + return default + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default) + + def get_link_info_data(self, ifname): + """ + Return device linkinfo:info_data attribute or default value + :param ifname: + :param info_data_attribute: + :param default: + :return: + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA] + except (KeyError, AttributeError): + return {} + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value={}) + + def get_link_info_slave_data_attribute(self, ifname, info_slave_data_attribute, default=None): + """ + Return device linkinfo:info_slave_data attribute or default value + :param ifname: + :param info_data_attribute: + :param default: + :return: + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA][info_slave_data_attribute] + except (KeyError, AttributeError): + return default + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default) + + ################ + # MASTER & SLAVE + ################ + def get_master(self, ifname): + """ + Return device master's ifname + :param ifname: + :return: str + """ + try: + with self._cache_lock: + return self._slaves_master[ifname] + except (KeyError, AttributeError): + return None + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None) + + def get_slaves(self, master): + """ + Return all devices ifname enslaved to master device + :param master: + :return: list of string + """ + try: + with self._cache_lock: + return list(self._masters_and_slaves[master]) + except KeyError: + return [] + + def is_link_enslaved_to(self, slave, master): + """ + Return bool if SLAVE is enslaved to MASTER + :param slave: + :param master: + :return: + """ + try: + with self._cache_lock: + return self._slaves_master[slave] == master + except KeyError: + return False + + def get_lower_device_ifname(self, ifname): + """ + Return the lower-device (IFLA_LINK) name or raise KeyError + :param ifname: + :return: string + """ + try: + with self._cache_lock: + return self.get_ifname(self._link_cache[ifname].attributes[Link.IFLA_LINK].value) + except (NetlinkCacheIfnameNotFoundError, AttributeError, KeyError): + return None + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None) + + ########################################################################## + # VRF #################################################################### + ########################################################################## + + def get_vrf_table_map(self): + vrf_table_map = {} + try: + with self._cache_lock: + for ifname, obj in self._link_cache.iteritems(): + linkinfo = obj.attributes.get(Link.IFLA_LINKINFO) + + if linkinfo and linkinfo.value.get(Link.IFLA_INFO_KIND) == "vrf": + vrf_table_map[linkinfo.value[Link.IFLA_INFO_DATA][Link.IFLA_VRF_TABLE]] = ifname + except Exception as e: + log.debug("get_vrf_table_map: %s" % str(e)) + return vrf_table_map + + def get_vrf_table(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_VRF_TABLE] + except (KeyError, AttributeError): + return 0 + + ########################################################################## + # BOND ################################################################### + ########################################################################## + + def bond_exists(self, ifname): + """ + Check if bond 'ifname' exists + :param ifname: bond name + :return: boolean + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[nlpacket.Link.IFLA_LINKINFO].value[nlpacket.Link.IFLA_INFO_KIND] == 'bond' + except (KeyError, AttributeError): + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False) + + ########################################################################## + # BRIDGE PORT ############################################################ + ########################################################################## + + def get_bridge_port_multicast_router(self, ifname): + """ + Get bridge port multicast_router value - defaults to 1 + + :param ifname: + :return: + """ + default_value = 1 + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA][Link.IFLA_BRPORT_MULTICAST_ROUTER] + except (KeyError, AttributeError): + # KeyError will be raised if: + # - ifname is missing from the cache (but link_exists should be called prior this call) + # - IFLA_BRPORT_MULTICAST_ROUTER is missing + # AttributeError can also be raised if IFLA_LINKINFO is missing (None.value) + # default_value is returned. + return default_value + except TypeError as e: + return self.__handle_type_error( + inspect.currentframe().f_code.co_name, + ifname, + str(e), + return_value=default_value + ) + + ########################################################################## + # BRIDGE ################################################################# + ########################################################################## + + def get_bridge_multicast_snooping(self, ifname): + """ + Get bridge multicast_snooping value - defaults to 1 + + :param ifname: + :return: + """ + default_value = 1 + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BR_MCAST_SNOOPING] + except (KeyError, AttributeError): + # KeyError will be raised if: + # - ifname is missing from the cache (but link_exists should be called prior this call) + # - IFLA_BR_MCAST_SNOOPING is missing + # AttributeError can also be raised if IFLA_LINKINFO is missing (None.value) + # default_value is returned. + return default_value + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default_value) + + def get_bridge_stp(self, ifname): + """ + WARNING: ifname should be a bridge + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BR_STP_STATE] + except (KeyError, AttributeError): + return 0 + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0) + + def get_brport_cost(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_COST] + except (KeyError, AttributeError): + return None + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None) + + def get_brport_priority(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_PRIORITY] + except (KeyError, AttributeError): + return None + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=None) + + def get_brport_unicast_flood(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_UNICAST_FLOOD] + except (KeyError, AttributeError): + return 0 + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0) + + def get_brport_multicast_flood(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_MCAST_FLOOD] + except (KeyError, AttributeError): + return 0 + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0) + + def get_brport_broadcast_flood(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_BCAST_FLOOD] + except (KeyError, AttributeError): + return 0 + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0) + + def get_brport_neigh_suppress(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BRPORT_NEIGH_SUPPRESS] + except (KeyError, AttributeError): + return 0 + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0) + + def get_brport_learning(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA][Link.IFLA_BRPORT_LEARNING] + except (KeyError, AttributeError): + return 0 + + def get_pvid_and_vids(self, ifname): + """ + vlan-identifiers are stored in: + + self._bridge_vlan_cache = { + ifname: [(vlan, flag), (vlan, flag), ...] + } + + Those vlans are stored in compressed format (RTEXT_FILTER_BRVLAN_COMPRESSED) + We only uncompress the vlan when the user request it. + + :param ifname: + :return tuple: pvid, vids = int, [int, ] + """ + pvid = None + vlans = [] + try: + range_begin_vlan_id = None + range_flag = None + + with self._cache_lock: + bridge_vlans_tuples = self._bridge_vlan_cache.get(ifname) + + if bridge_vlans_tuples: + for (vlan_id, vlan_flag) in sorted(bridge_vlans_tuples): + + if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID: + pvid = vlan_id + + if vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN: + range_begin_vlan_id = vlan_id + range_flag = vlan_flag + + elif vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_END: + range_flag |= vlan_flag + + if not range_begin_vlan_id: + log.warning("BRIDGE_VLAN_INFO_RANGE_END is %d but we never " + "saw a BRIDGE_VLAN_INFO_RANGE_BEGIN" % vlan_id) + range_begin_vlan_id = vlan_id + + for x in xrange(range_begin_vlan_id, vlan_id + 1): + vlans.append(x) + + range_begin_vlan_id = None + range_flag = None + + else: + vlans.append(vlan_id) + except: + log.exception("get_bridge_vids") + return pvid, vlans + + def get_pvid(self, ifname): + """ + Get Port VLAN ID for device 'ifname' + + :param ifname: + :return: + """ + pvid = None + try: + with self._cache_lock: + bridge_vlans_tuples = self._bridge_vlan_cache.get(ifname) + + if bridge_vlans_tuples: + + for (vlan_id, vlan_flag) in sorted(bridge_vlans_tuples): + + if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID: + return vlan_id + except: + log.exception("get_pvid") + return pvid + + def bridge_exists(self, ifname): + """ + Check if cached device is a bridge + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_KIND] == "bridge" + except (KeyError, AttributeError): + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False) + + def bridge_is_vlan_aware(self, ifname): + """ + Return IFLA_BR_VLAN_FILTERING value + :param ifname: + :return: boolean + """ + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA][Link.IFLA_BR_VLAN_FILTERING] + except (KeyError, AttributeError): + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False) + + def link_is_bridge_port(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND] == "bridge" + except (KeyError, AttributeError): + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False) + + def bridge_port_exists(self, bridge_name, brport_name): + try: + with self._cache_lock: + # we are assuming that bridge_name is a valid bridge? + return self._slaves_master[brport_name] == bridge_name + except (KeyError, AttributeError): + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, bridge_name, str(e), return_value=False) + + def get_bridge_name_from_port(self, bridge_port_name): + bridge_name = self.get_master(bridge_port_name) + # now that we have the master's name we just need to double check + # if the master is really a bridge + return bridge_name if self.link_is_bridge(bridge_name) else None + + #def is_link_slave_kind(self, ifname, _type): + # try: + # with self._cache_lock: + # return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND] == _type + # except (KeyError, AttributeError): + # return False + + ######################################## + + def get_link_ipv6_addrgen_mode(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_ADDR_GEN_MODE] + except (KeyError, AttributeError): + # default to 0 (eui64) + return 0 + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0) + + ##################################################### + ##################################################### + ##################################################### + ##################################################### + ##################################################### + + def add_link(self, link): + """ + Cache RTM_NEWLINK packet + :param link: + :return: + """ + ifindex = link.ifindex + ifname = link.get_attribute_value(Link.IFLA_IFNAME) + + # check if this device is registered in the ignore list + with self._ignore_rtm_newlinkq_lock: + if ifname in self._ignore_rtm_newlinkq: + return + # check if this device is registered in the nomaster list: + # if so we need to remove IFLA_MASTER attribute (if it fails + # it means we've received the final notification and we should + # unregister the device from our list. + with self._rtm_newlink_nomasterq_lock: + if ifname in self._rtm_newlink_nomasterq: + try: + del link.attributes[Link.IFLA_MASTER] + except: + self._rtm_newlink_nomasterq.remove(ifname) + + # we need to check if the device was previously enslaved + # so we can update the _masters_and_slaves and _slaves_master + # dictionaries if the master has changed or was un-enslaved. + old_ifla_master = None + + with self._cache_lock: + + # do we have a wait event registered for RTM_NEWLINK this ifname + if self._wait_event and self._wait_event == (ifname, RTM_NEWLINK): + self._wait_event_alarm.set() + + try: + ifla_master_attr = self._link_cache[ifname].attributes.get(Link.IFLA_MASTER) + + if ifla_master_attr: + old_ifla_master = ifla_master_attr.get_pretty_value() + except KeyError: + # link is not present in the cache + pass + except AttributeError: + # if this code is ever reached, this is very concerning and + # should never happen as _link_cache should always contains + # nlpacket.NetlinkPacket... maybe have some extra handling + # here just in case? + pass + + self._link_cache[ifname] = link + + ###################################################### + # update helper dictionaries and handle link renamed # + ###################################################### + if ifindex: + # ifindex can be None for packet added on ACK, it means + # that we are caching the request packet and not the + # notification coming from the kernel. We can leave + # those data-strctures empty and rely on our try/excepts + # in get_ifname/get_ifindex/get_master to do the work. + + self._ifindex_by_ifname[ifname] = ifindex + + rename_detected = False + old_ifname_entry_for_ifindex = self._ifname_by_ifindex.get(ifindex) + + if old_ifname_entry_for_ifindex and old_ifname_entry_for_ifindex != ifname: + # The ifindex was reused for a new interface before we got the + # RTM_DELLINK notification or the device using that ifindex was + # renamed. We need to update the cache accordingly. + rename_detected = True + + self._ifname_by_ifindex[ifindex] = ifname + + if rename_detected: + # in this case we detected a rename... It should'nt happen has we should get a RTM_DELLINK before that. + # if we still detect a rename the opti is to get rid of the stale value directly + try: + del self._ifindex_by_ifname[old_ifname_entry_for_ifindex] + except KeyError: + log.debug('update_helper_dicts: del _ifindex_by_ifname[%s]: KeyError ifname: %s' + % (old_ifname_entry_for_ifindex, old_ifname_entry_for_ifindex)) + try: + del self._link_cache[old_ifname_entry_for_ifindex] + except KeyError: + log.debug('update_helper_dicts: del _link_cache[%s]: KeyError ifname: %s' + % (old_ifname_entry_for_ifindex, old_ifname_entry_for_ifindex)) + ###################################################### + ###################################################### + + link_ifla_master_attr = link.attributes.get(Link.IFLA_MASTER) + if link_ifla_master_attr: + link_ifla_master = link_ifla_master_attr.get_pretty_value() + else: + link_ifla_master = None + + # if the link has a master we need to store it in an helper dictionary, where + # the key is the master ifla_ifname and the value is a list of slaves, example: + # _masters_slaves_dict = { + # 'bond0': ['swp21', 'swp42'] + # } + # this will be useful in the case we need to iterate on all slaves of a specific link + + if old_ifla_master: + if old_ifla_master != link_ifla_master: + # the link was previously enslaved but master is now unset on this device + # we need to reflect that on the _masters_and_slaves and _slaves_master dictionaries + try: + self.__unslave_nolock(slave=ifname) + except NetlinkCacheIfindexNotFoundError: + pass + else: + # the master status didn't change we can assume that our internal + # masters_slaves dictionary is up to date and return here + return + + if not link_ifla_master: + return + + master_ifname = self._ifname_by_ifindex.get(link_ifla_master) + + if not master_ifname: + # in this case we have a link object with IFLA_MASTER set to a ifindex + # but this ifindex is not in our _ifname_by_ifindex dictionary thus it's + # not in the _link_cache yet. This situation may happen when getting the + # very first link dump. The kernel dumps device in the "ifindex" order. + # + # So let's say you have a box with 4 ports (lo, eth0, swp1, swp2), then + # manually create a bond (bond0) and enslave swp1 and swp2, bond0 will + # have ifindex 5 but when getting the link dump swp1 will be handled first + # so at this point the cache has no idea if ifindex 5 is valid or not. + # But since we've made it this far we can assume that this is probably a + # valid device and will use sysfs to confirm. + master_device_path = '/sys/class/net/%s/master' % ifname + + if os.path.exists(master_device_path): + # this check is necessary because realpath doesn't return None on error + # it returns it's input argument... + # >>> os.path.realpath('/sys/class/net/device_not_found') + # '/sys/class/net/device_not_found' + # >>> + master_ifname = os.path.basename(os.path.realpath(master_device_path)) + + if master_ifname in self._masters_and_slaves: + self._masters_and_slaves[master_ifname].add(ifname) + else: + self._masters_and_slaves[master_ifname] = set([ifname]) + + self._slaves_master[ifname] = master_ifname + + def update_link_info_data(self, ifname, ifla_info_data): + """ + Update specific IFLA_INFO_DATA attributes of an existing cached device + ignore all errors + """ + try: + with self._cache_lock: + self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_DATA].update(ifla_info_data) + except: + pass + + def add_bridge_vlan(self, msg): + """ + Process AF_BRIDGE family packets (AF_BRIDGE family should be check + before calling this function). + + Extract VLAN_INFO (vlan id and flag) and store it in cache. + + :param link: + :return: + """ + vlans_list = [] + + with self._cache_lock: + ifla_af_spec = msg.get_attribute_value(Link.IFLA_AF_SPEC) + ifname = msg.get_attribute_value(Link.IFLA_IFNAME) + + if not ifla_af_spec: + return + + try: + # We need to check if this object is still in cache, after a bridge + # is removed we still receive AF_BRIDGE notifications for it's slave + # those notifications should be ignored. + ifla_master = msg.get_attribute_value(Link.IFLA_MASTER) + + if not ifla_master or not ifla_master in self._ifname_by_ifindex: + return + except: + pass + + # Example IFLA_AF_SPEC + # 20: 0x1c001a00 .... Length 0x001c (28), Type 0x001a (26) IFLA_AF_SPEC + # 21: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO + # 22: 0x00000a00 .... + # 23: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO + # 24: 0x00001000 .... + # 25: 0x08000200 .... Nested Attribute - Length 0x0008 (8), Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO + # 26: 0x00001400 .... + for x_type, x_value in ifla_af_spec.iteritems(): + if x_type == Link.IFLA_BRIDGE_VLAN_INFO: + for vlan_flag, vlan_id in x_value: + # We store these in the tuple as (vlan, flag) instead + # (flag, vlan) so that we can sort the list of tuples + vlans_list.append((vlan_id, vlan_flag)) + + self._bridge_vlan_cache.update({ifname: vlans_list}) + + def force_add_slave(self, master, slave): + """ + When calling link_set_master, we don't want to wait for the RTM_GETLINK + notification - if the operation return with NL_SUCCESS we can manually + update our cache and move on + :param master: + :param slave: + :return: + """ + try: + with self._cache_lock: + master_slaves = self._masters_and_slaves.get(master) + + if not master_slaves: + self._masters_and_slaves[master] = {slave} + else: + master_slaves.add(slave) + + # if the slave is already enslaved to another device we should + # make sure to remove it from the _masters_and_slaves data + # structure as well. + old_master = self._slaves_master.get(slave) + + if old_master: + try: + self._masters_and_slaves.get(old_master, []).remove(slave) + except: + pass + + self._slaves_master[slave] = master + except: + # since this is an optimization function we can ignore all error + pass + + def force_add_slave_list(self, master, slave_list): + """ + When calling link_set_master, we don't want to wait for the RTM_GETLINK + notification - if the operation return with NL_SUCCESS we can manually + update our cache and move on + :param master: + :param slave: + :return: + """ + try: + with self._cache_lock: + master_slaves = self._masters_and_slaves.get(master) + + if not master_slaves: + self._masters_and_slaves[master] = set(slave_list) + else: + master_slaves.update(slave_list) + + for slave in slave_list: + self._slaves_master[slave] = master + except: + # since this is an optimization function we can ignore all error + pass + + def force_remove_link(self, ifname): + """ + When calling link_del (RTM_DELLINK) we need to manually remove the + associated cache entry - the RTM_DELLINK notification isn't received + instantaneously - we don't want to keep stale value in our cache + :param ifname: + :return: + """ + try: + ifindex = self.get_ifindex(ifname) + except (KeyError, NetlinkCacheIfnameNotFoundError): + ifindex = None + self.remove_link(None, link_ifname=ifname, link_ifindex=ifindex) + + def remove_link(self, link, link_ifname=None, link_ifindex=None): + """ Process RTM_DELLINK packet and purge the cache accordingly """ + if link: + ifindex = link.ifindex + ifname = link.get_attribute_value(Link.IFLA_IFNAME) + try: + # RTM_DELLINK received - we can now remove ifname from the + # ignore_rtm_newlinkq list. We don't bother checkin if the + # if name is present in the list (because it's likely in) + with self._ignore_rtm_newlinkq_lock: + self._ignore_rtm_newlinkq.remove(ifname) + except ValueError: + pass + else: + ifname = link_ifname + ifindex = link_ifindex + + link_ifla_master = None + # when an enslaved device is removed we receive the RTM_DELLINK + # notification without the IFLA_MASTER attribute, we need to + # get the previous cached value in order to correctly update the + # _masters_and_slaves dictionary + + with self._cache_lock: + try: + try: + ifla_master_attr = self._link_cache[ifname].attributes.get(Link.IFLA_MASTER) + if ifla_master_attr: + link_ifla_master = ifla_master_attr.get_pretty_value() + except KeyError: + # link is not present in the cache + pass + except AttributeError: + # if this code is ever reached this is very concerning and + # should never happen as _link_cache should always contains + # nlpacket.NetlinkPacket maybe have some extra handling here + # just in case? + pass + finally: + del self._link_cache[ifname] + except KeyError: + # KeyError means that the link doesn't exists in the cache + log.debug('del _link_cache: KeyError ifname: %s' % ifname) + + try: + # like in __unslave_nolock() we need to make sure that all deleted link + # have their bridge-vlans and _slaves_master entries cleared. + for slave in list(self._masters_and_slaves[ifname]): + self.__unslave_nolock(slave, master=ifname) + except: + pass + + try: + del self._bridge_vlan_cache[ifname] + except: + pass + + try: + del self._ifname_by_ifindex[ifindex] + except KeyError: + log.debug('del _ifname_by_ifindex: KeyError ifindex: %s' % ifindex) + + try: + del self._ifindex_by_ifname[ifname] + except KeyError: + log.debug('del _ifindex_by_ifname: KeyError ifname: %s' % ifname) + + try: + del self._addr_cache[ifname] + except KeyError: + log.debug('del _addr_cache: KeyError ifname: %s' % ifname) + + try: + del self._masters_and_slaves[ifname] + except KeyError: + log.debug('del _masters_and_slaves: KeyError ifname: %s' % ifname) + + # if the device was enslaved to another device we need to remove + # it's entry from our _masters_and_slaves dictionary + if link_ifla_master > 0: + try: + self.__unslave_nolock(slave=ifname) + except NetlinkCacheIfindexNotFoundError as e: + log.debug('cache: remove_link: %s: %s' % (ifname, str(e))) + except KeyError: + log.debug('_masters_and_slaves[if%s].remove(%s): KeyError' % (link_ifla_master, ifname)) + + def _address_get_ifname_and_ifindex(self, addr): + ifindex = addr.ifindex + label = addr.get_attribute_value(Address.IFA_LABEL) + + if not label: + try: + label = self.get_ifname(ifindex) + except NetlinkCacheIfindexNotFoundError: + pass + + return label, ifindex + + def __check_and_replace_address(self, address_list, new_addr): + """ + Check if new_addr is in address_list, if found we replace the occurrence + with the new and update object "new_addr" + + address_list should be a valid list (check before calling to improve perf) + :param address_list: + :param new_addr: + :return: + """ + ip_with_prefix = new_addr.get_attribute_value(Address.IFA_ADDRESS).with_prefixlen + + for index, addr in enumerate(address_list): + if addr.get_attribute_value(Address.IFA_ADDRESS).with_prefixlen == ip_with_prefix: + address_list[index] = new_addr + return True + + return False + + def add_address(self, addr): + ifname, ifindex = self._address_get_ifname_and_ifindex(addr) + + if not ifname: + log.debug('nlcache: add_address: cannot cache addr for ifindex %s' % ifindex) + return + + ip_version = addr.get_attribute_value(Address.IFA_ADDRESS).version + + with self._cache_lock: + + if ifname in self._addr_cache: + address_list = self._addr_cache[ifname][ip_version] + # First check if the address is already cached, if so + # we need to update it's entry with the new obj + if not address_list or not self.__check_and_replace_address(address_list, addr): + address_list.append(addr) + else: + self._addr_cache[ifname] = { + 4: [], + 6: [], + ip_version: [addr] + } + + def force_address_flush_family(self, ifname, family): + try: + with self._cache_lock: + self._addr_cache[ifname][family] = [] + except: + pass + + def address_flush_link(self, ifname): + """ + Flush address cache for link 'ifname' + :param ifname: + :return: + """ + try: + with self._cache_lock: + self._addr_cache[ifname] = {4: [], 6: []} + except: + pass + + def force_remove_addr(self, ifname, addr): + """ + When calling addr_del (RTM_DELADDR) we need to manually remove the + associated cache entry - the RTM_DELADDR notification isn't received + instantaneously - we don't want to keep stale value in our cache + :param ifname: + :param addr: + """ + try: + with self._cache_lock: + # iterate through the interface addresses + # to find which one to remove from the cache + obj_to_remove = None + + for cache_addr in self._addr_cache[ifname][addr.version]: + try: + if cache_addr.attributes[Address.IFA_ADDRESS].value.with_prefixlen == addr.with_prefixlen: + obj_to_remove = cache_addr + except: + try: + if cache_addr.attributes[Address.IFA_LOCAL].value.with_prefixlen == addr.with_prefixlen: + obj_to_remove = cache_addr + except: + return + if obj_to_remove: + self._addr_cache[ifname][addr.version].remove(obj_to_remove) + except: + pass + + def remove_address(self, addr_to_remove): + ifname, _ = self._address_get_ifname_and_ifindex(addr_to_remove) + + with self._cache_lock: + # iterate through the interface addresses + # to find which one to remove from the cache + try: + ip_version = addr_to_remove.get_attribute_value(Address.IFA_ADDRESS).version + except: + try: + ip_version = addr_to_remove.get_attribute_value(Address.IFA_LOCAL).version + except: + # print debug error + return + + addrs_for_interface = self._addr_cache.get(ifname, {}).get(ip_version) + + if not addrs_for_interface: + return + + list_addr_to_remove = [] + + for addr in addrs_for_interface: + # compare each object attribute to see if they match + addr_match = False + + for ifa_attr in self._ifa_attributes: + if addr.get_attribute_value(ifa_attr) != addr_to_remove.get_attribute_value(ifa_attr): + addr_match = False + break + addr_match = True + # if the address attribute matches we need to remove this one + + if addr_match: + list_addr_to_remove.append(addr) + + for addr in list_addr_to_remove: + try: + addrs_for_interface.remove(addr) + except ValueError as e: + log.debug('nlcache: remove_address: exception: %s' % e) + + def get_addresses_list(self, ifname): + addresses = [] + try: + with self._cache_lock: + intf_addresses = self._addr_cache[ifname] + for addr in intf_addresses.get(4, []): + addresses.append(addr.attributes[Address.IFA_ADDRESS].value) + for addr in intf_addresses.get(6, []): + addresses.append(addr.attributes[Address.IFA_ADDRESS].value) + return addresses + except (KeyError, AttributeError): + return addresses + + def get_addresses_objects_list(self, ifname): + addresses = [] + try: + with self._cache_lock: + intf_addresses = self._addr_cache[ifname] + for addr in intf_addresses.get(4, []): + addresses.append(addr) + for addr in intf_addresses.get(6, []): + addresses.append(addr) + return addresses + except (KeyError, AttributeError): + return addresses + + def link_has_ip(self, ifname): + try: + with self._cache_lock: + intf_addresses = self._addr_cache[ifname] + return bool(intf_addresses.get(4, None) or intf_addresses.get(6, None)) + except: + return False + + ############################################################################ + ############################################################################ + ############################################################################ + + def add_netconf(self, msg): + """ + cache RTM_NEWNETCONF objects + { + family: { + ifindex: RTM_NEWNETCONF + } + } + we currently only support AF_INET, AF_INET6 and AF_MPLS family. + """ + try: + with self._netconf_cache_lock: + self._netconf_cache[msg.family][msg.get_attribute_value(msg.NETCONFA_IFINDEX)] = msg + except: + pass + + def remove_netconf(self, msg): + """ + Process RTM_DELNETCONF, remove associated entry in our _netconf_cache + """ + try: + with self._netconf_cache_lock: + del self._netconf_cache[msg.family][msg.get_attribute_value(msg.NETCONFA_IFINDEX)] + except: + pass + + def get_netconf_forwarding(self, family, ifname): + """ + Return netconf device forwarding value + """ + try: + with self._netconf_cache_lock: + return self._netconf_cache[family][self.get_ifindex(ifname)].get_attribute_value(Netconf.NETCONFA_FORWARDING) + except: + # if KeyError and family == AF_INET6: ipv6 is probably disabled on this device + return None + + def get_netconf_mpls_input(self, ifname): + """ + Return netconf device MPLS input value + """ + try: + with self._netconf_cache_lock: + return self._netconf_cache[AF_MPLS][self.get_ifindex(ifname)].get_attribute_value(Netconf.NETCONFA_INPUT) + except: + return None + + ############################################################################ + ############################################################################ + ############################################################################ + + def get_ifupdown2_addresses_list(self, ifaceobj_list, ifname, with_address_virtual=False): + """ + With the new live cache, we store every intf's addresses even if they + werent configured by ifupdown2. We need to filter those out to avoid + problems + + To do so we look at the previous configuration made by ifupdown2 + (with the help of the statemanager) together with the addresses + specified by the user in /etc/network/interfaces, these addresses + are then compared to the running state of the intf + """ + if not ifaceobj_list: + ifaceobj_list = [] + + config_addrs = set( + self.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format( + ifaceobj_list, + with_address_virtual=with_address_virtual, + details=False + ) + ) + + for previous_state_addr in self.get_user_config_ip_addrs_with_attrs_in_ipnetwork_format( + statemanager.statemanager_api.get_ifaceobjs(ifname), + with_address_virtual=with_address_virtual, + details=False + ): + config_addrs.add(previous_state_addr) + + ifupdown2_addresses = [] + + for addr in self.get_addresses_objects_list(ifname): + ip_addr = addr.attributes[Address.IFA_ADDRESS].value + if ip_addr in config_addrs: + ifupdown2_addresses.append(ip_addr) + elif not addr.scope & Route.RT_SCOPE_LINK: + ifupdown2_addresses.append(ip_addr) + + return ifupdown2_addresses + + def get_user_config_ip_addrs_with_attrs_in_ipnetwork_format(self, ifaceobj_list, with_address_virtual=False, details=True): + """ + if details=True: + This function will return a OrderedDict of user addresses (from /e/n/i) + An OrderedDict is necessary because the addresses order is important (primary etc) + + if details=False: + Function will return an ordered list of ip4 followed by ip6 as configured in /e/n/i. + + all of the IP object were created by IPNetwork. + """ + if not ifaceobj_list: + return {} if details else [] + + ip4 = [] + ip6 = [] + + for ifaceobj in ifaceobj_list: + addresses = ifaceobj.get_attr_value('address') + + if addresses: + for addr_index, addr in enumerate(addresses): + if '/' in addr: + ip_network_obj = IPNetwork(addr) + else: + # if netmask is specified under the stanza we need to use to + # create the IPNetwork objects, otherwise let IPNetwork figure + # out the correct netmask for ip4 & ip6 + netmask = ifaceobj.get_attr_value_n('netmask', addr_index) + + if netmask: + ip_network_obj = IPNetwork('%s/%s' % (addr, netmask)) + else: + ip_network_obj = IPNetwork(addr) + + if not details: + # if no details=False we don't need to go further and our lists + # will only store the IPNetwork object and nothing else + if ip_network_obj.version == 6: + ip6.append(ip_network_obj) + else: + ip4.append(ip_network_obj) + continue + + addr_attributes = {} + + for attr in ['broadcast', 'pointopoint', 'scope', 'preferred-lifetime']: + attr_value = ifaceobj.get_attr_value_n(attr, addr_index) + + if attr_value: + addr_attributes[attr] = attr_value + + if ip_network_obj.version == 6: + ip6.append((ip_network_obj, addr_attributes)) + else: + ip4.append((ip_network_obj, addr_attributes)) + + if not with_address_virtual: + continue + # + # address-virtual and vrrp ips also needs to be accounted for + # + addresses_virtual = ifaceobj.get_attr_value('address-virtual') + vrrp = ifaceobj.get_attr_value('vrrp') + + for attr_config in (addresses_virtual, vrrp): + for addr_virtual_entry in attr_config or []: + for addr in addr_virtual_entry.split(): + try: + ip_network_obj = IPNetwork(addr) + + if ip_network_obj.version == 6: + if not details: + ip6.append(ip_network_obj) + else: + ip6.append((ip_network_obj, {})) + else: + if not details: + ip4.append(ip_network_obj) + else: + ip4.append((ip_network_obj, {})) + except: + continue + + # always return ip4 first, followed by ip6 + if not details: + return ip4 + ip6 + else: + user_config_addresses = OrderedDict() + + for addr, addr_details in ip4: + user_config_addresses[addr] = addr_details + + for addr, addr_details in ip6: + user_config_addresses[addr] = addr_details + + return user_config_addresses + ############################################################################ + ############################################################################ + ############################################################################ + + def addr_is_cached(self, ifname, addr): + """ + return True if addr is in cache + + We might need to check if metric/peer and other attribute are also correctly cached. + We might also need to add a "force" attribute to skip the cache check + :param ifname: + :param ifindex: + :return: + """ + try: + with self._cache_lock: + for cache_addr in self._addr_cache[ifname][addr.version]: + try: + ifa_address = cache_addr.attributes[Address.IFA_ADDRESS].value + if ifa_address.ip == addr.ip and ifa_address.prefixlen == addr.prefixlen: + return True + except: + try: + ifa_local = cache_addr.attributes[Address.IFA_LOCAL].value + return ifa_local.ip == addr.ip and ifa_local.prefixlen == addr.prefixlen + except: + pass + except (KeyError, AttributeError): + pass + return False + + # old + + def get_link_obj(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname] + except KeyError: + return None + + def get_link_info_slave_data(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_DATA] + except (KeyError, AttributeError): + return {} + + def is_link_kind(self, ifname, _type): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_KIND] == _type + except (KeyError, AttributeError): + return False + + def is_link_slave_kind(self, ifname, _type): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_LINKINFO].value[Link.IFLA_INFO_SLAVE_KIND] == _type + except (KeyError, AttributeError): + return False + + +class NetlinkListenerWithCache(nllistener.NetlinkManagerWithListener, BaseObject): + + __instance = None + VXLAN_UDP_PORT = 4789 + + @staticmethod + def init(log_level): + """ + Create the singleton via this init function + Following calls should use get_instance() + :param log_level: + :return: + """ + if not NetlinkListenerWithCache.__instance: + try: + NetlinkListenerWithCache.__instance = NetlinkListenerWithCache(log_level=log_level) + except Exception as e: + log.error('NetlinkListenerWithCache: init: %s' % e) + traceback.print_exc() + + @staticmethod + def get_instance(): + """ + Use this function to retrieve the active reference to the + NetlinkListenerWithCache, make sure you called .init() first + :return: + """ + if not NetlinkListenerWithCache.__instance: + raise NetlinkListenerWithCacheErrorNotInitialized("NetlinkListenerWithCache not initialized") + return NetlinkListenerWithCache.__instance + + def __init__(self, log_level): + """ + + :param log_level: + """ + if NetlinkListenerWithCache.__instance: + raise RuntimeError("NetlinkListenerWithCache: invalid access. Please use NetlinkListenerWithCache.getInstance()") + else: + NetlinkListenerWithCache.__instance = self + + nllistener.NetlinkManagerWithListener.__init__( + self, + groups=( + nlpacket.RTMGRP_LINK + | nlpacket.RTMGRP_IPV4_IFADDR + | nlpacket.RTMGRP_IPV6_IFADDR + | nlpacket.RTNLGRP_IPV4_NETCONF + | nlpacket.RTNLGRP_IPV6_NETCONF + | nlpacket.RTNLGRP_MPLS_NETCONF + ), + start_listener=False, + error_notification=True + ) + + BaseObject.__init__(self) + + signal.signal(signal.SIGTERM, self.signal_term_handler) + signal.signal(signal.SIGINT, self.signal_int_handler) + + self.cache = _NetlinkCache() + + # set specific log level to lower-level API + nllistener.log.setLevel(log_level) + nlpacket.log.setLevel(log_level) + nlmanager.log.setLevel(log_level) + + self.IPNetwork_version_to_family = {4: socket.AF_INET, 6: socket.AF_INET6} + + nlpacket.mac_int_to_str = lambda mac_int: ':'.join(('%012x' % mac_int)[i:i + 2] for i in range(0, 12, 2)) + # Override the nlmanager's mac_int_to_str function + # Return an integer in MAC string format: xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx + + self.workq_handler = { + self.WORKQ_SERVICE_NETLINK_QUEUE: self.service_netlinkq, + } + + # NetlinkListenerWithCache first dumps links and addresses then start + # a worker thread before returning. The worker thread processes the + # workq mainly to service (process) the netlinkq which contains our + # netlink packet (notification coming from the Kernel). + # When the main thread is making netlin requests (i.e. bridge add etc + # ...) the main thread will sleep (thread.event.wait) until we notify + # it when receiving an ack associated with the request. The request + # may fail and the kernel won't return an ACK but instead return a + # NLMSG_ERROR packet. We need to store those packet separatly because: + # - we could have several NLMSG_ERROR for different requests from + # different threads (in a multi-threaded ifupdown2 case) + # - we want to decode the packet and tell the user/log or even raise + # an exception with the appropriate message. + # User must check the return value of it's netlink requests and + # catch any exceptions, for that purpose please use API: + # - tx_nlpacket_get_response_with_error + self.errorq = list() + self.errorq_lock = threading.Lock() + self.errorq_enabled = True + + # when ifupdown2 starts, we need to fill the netlink cache + # GET_LINK/ADDR request are asynchronous, we need to block + # and wait for the cache to be filled. We are using this one + # time netlinkq_notify_event to wait for the cache completion event + self.netlinkq_notify_event = None + + # another threading event to make sure that the netlinkq worker thread is ready + self.is_ready = threading.Event() + + self.worker = None + + def __str__(self): + return "NetlinkListenerWithCache" + + def start(self): + """ + Start NetlinkListener - + cache all links, bridges, addresses and netconfs + :return: + """ + self.restart_listener() + + # set ifupdown2 specific supported and ignore messages + self.listener.supported_messages = ( + nlpacket.RTM_NEWLINK, + nlpacket.RTM_DELLINK, + nlpacket.RTM_NEWADDR, + nlpacket.RTM_DELADDR, + nlpacket.RTM_NEWNETCONF, + nlpacket.RTM_DELNETCONF + ) + self.listener.ignore_messages = ( + nlpacket.RTM_GETLINK, + nlpacket.RTM_GETADDR, + nlpacket.RTM_GETNEIGH, + nlpacket.RTM_GETROUTE, + nlpacket.RTM_GETQDISC, + nlpacket.RTM_NEWNEIGH, + nlpacket.RTM_DELNEIGH, + nlpacket.RTM_NEWROUTE, + nlpacket.RTM_DELROUTE, + nlpacket.RTM_DELNETCONF, + nlpacket.RTM_NEWQDISC, + nlpacket.RTM_DELQDISC, + nlpacket.RTM_GETQDISC, + nlpacket.NLMSG_ERROR, # should be in supported_messages ? + nlpacket.NLMSG_DONE # should be in supported_messages ? + ) + + # get all links and wait for the cache to be filled + self.get_all_links_wait_netlinkq() + + # get all addresses and wait for cache to be filled + self.get_all_addresses_wait_netlinkq() + + # get a netconf dump and wait for the cached to be filled + self.get_all_netconf_wait_netlinkq() + + # TODO: on ifquery we shoudn't start any thread (including listener in NetlinkListener) + # only for standalone code. + #import sys + #for arg in sys.argv: + # if 'ifquery' in arg: + # self.worker = None + # return + + # start the netlinkq worker thread + self.worker = threading.Thread(target=self.main, name='NetlinkListenerWithCache') + self.worker.start() + self.is_ready.wait() + + def cleanup(self): + if not self.__instance: + return + + # passing 0, 0 to the handler so it doesn't log.info + self.signal_term_handler(0, 0) + + if self.worker: + self.worker.join() + + def main(self): + self.is_ready.set() + + # This loop has two jobs: + # - process items on our workq + # - process netlink messages on our netlinkq, messages are placed there via our NetlinkListener + try: + while True: + # Sleep until our alarm goes off...NetlinkListener will set the alarm once it + # has placed a NetlinkPacket object on our netlinkq. If someone places an item on + # our workq they should also set our alarm...if they don't it is not the end of + # the world as we will wake up in 1s anyway to check to see if our shutdown_event + # has been set. + self.alarm.wait(0.1) + # when ifupdown2 is not running we could change the timeout to 1 sec or more (daemon mode) + # the daemon can also put a hook (pyinotify) on /etc/network/interfaces + # if we detect changes to that file it probably means that ifupdown2 will be called very soon + # then we can scale up our ops (or maybe unpause some of them) + # lets study the use cases + self.alarm.clear() + if self.shutdown_event.is_set(): + break + + while not self.workq.empty(): + (event, options) = self.workq.get() + + if event == self.WORKQ_SERVICE_NETLINK_QUEUE: + self.service_netlinkq(self.netlinkq_notify_event) + elif event == self.WORKQ_SERVICE_ERROR: + self.logger.error('NetlinkListenerWithCache: WORKQ_SERVICE_ERROR') + else: + raise Exception("Unsupported workq event %s" % event) + except: + raise + finally: + # il faut surement mettre un try/except autour de la boucle au dessus + # car s'il y a une exception on ne quitte pas le listener thread + self.listener.shutdown_event.set() + self.listener.join() + + def reset_errorq(self): + with self.errorq_lock: + self.logger.debug("nlcache: reset errorq") + self.errorq = [] + + def rx_rtm_newaddr(self, rxed_addr_packet): + super(NetlinkListenerWithCache, self).rx_rtm_newaddr(rxed_addr_packet) + self.cache.add_address(rxed_addr_packet) + + def rx_rtm_dellink(self, link): + # cache only supports AF_UNSPEC for now + if link.family != socket.AF_UNSPEC: + return + super(NetlinkListenerWithCache, self).rx_rtm_dellink(link) + self.cache.remove_link(link) + + def rx_rtm_deladdr(self, addr): + super(NetlinkListenerWithCache, self).rx_rtm_deladdr(addr) + self.cache.remove_address(addr) + + def rx_rtm_newlink(self, rxed_link_packet): + # cache only supports AF_UNSPEC for now + # we can modify the cache to support more family: + # cache { + # intf_name: { + # AF_UNSPEC: NetlinkObj, + # AF_BRIDGE: NetlinkObj + # }, + # } + if rxed_link_packet.family != socket.AF_UNSPEC: + # special handling for AF_BRIDGE packets + if rxed_link_packet.family == socket.AF_BRIDGE: + self.cache.add_bridge_vlan(rxed_link_packet) + return + + super(NetlinkListenerWithCache, self).rx_rtm_newlink(rxed_link_packet) + self.cache.add_link(rxed_link_packet) + + def rx_rtm_newnetconf(self, msg): + super(NetlinkListenerWithCache, self).rx_rtm_newnetconf(msg) + self.cache.add_netconf(msg) + + def rx_rtm_delnetconf(self, msg): + super(NetlinkListenerWithCache, self).rx_rtm_delnetconf(msg) + self.cache.remove_netconf(msg) + + def tx_nlpacket_get_response_with_error(self, nl_packet): + """ + After getting an ACK we need to check if this ACK was in fact an + error (NLMSG_ERROR). This function go through the .errorq list to + find the error packet associated with our request. + If found, we process it and raise an exception with the appropriate + information/message. + + :param nl_packet: + :return: + """ + self.tx_nlpacket_get_response(nl_packet) + + error_packet = None + index = 0 + + with self.errorq_lock: + for error in self.errorq: + if error.seq == nl_packet.seq and error.pid == nl_packet.pid: + error_packet = error + break + index += 1 + + if error_packet: + del self.errorq[index] + + if not error_packet: + return True + + error_code = abs(error_packet.negative_errno) + + if error_packet.msgtype == NLMSG_DONE or not error_code: + # code NLE_SUCCESS...this is an ACK + return True + + if self.debug: + error_packet.dump() + + try: + # os.strerror might raise ValueError + strerror = os.strerror(error_code) + + if strerror: + error_str = "operation failed with '%s' (%s)" % (strerror, error_code) + else: + error_str = "operation failed with code %s" % error_code + + except ValueError: + error_str = "operation failed with code %s" % error_code + + raise Exception(error_str) + + def tx_nlpacket_get_response_with_error_and_cache_on_ack(self, packet): + """ + TX packet and manually cache the object + """ + self.tx_nlpacket_get_response_with_error(packet) + # 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 |= 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) + + def tx_nlpacket_get_response_with_error_and_wait_for_cache(self, ifname, nl_packet): + """ + The netlink request are asynchronus, but sometimes the main thread/user + would like to wait until the result of the request is cached. To do so + a cache event for ifname and nl_packet.msgtype is registered. Then the + netlink packet is TXed, errors are checked then we sleep until the + cache event is set (or we reach the timeout). This allows us to reliably + make sure is up to date with newly created/removed devices or addresses. + :param nl_packet: + :return: + """ + wait_event_registered = self.cache.register_wait_event(ifname, nl_packet.msgtype) + + try: + result = self.tx_nlpacket_get_response_with_error(nl_packet) + except: + # an error was caught, we need to unregister the event and raise again + self.cache.unregister_wait_event() + raise + + if wait_event_registered: + self.cache.wait_event() + + return result + + def get_all_links_wait_netlinkq(self): + self.logger.info("requesting link dump") + # create netlinkq notify event so we can wait until the links are cached + self.netlinkq_notify_event = threading.Event() + self.get_all_links() + # we also need a dump of all existing bridge vlans + self.get_all_br_links(compress_vlans=True) + # block until the netlinkq was serviced and cached + self.service_netlinkq(self.netlinkq_notify_event) + self.netlinkq_notify_event.wait() + self.netlinkq_notify_event.clear() + + def get_all_addresses_wait_netlinkq(self): + self.logger.info("requesting address dump") + self.netlinkq_notify_event = threading.Event() + self.get_all_addresses() + # block until the netlinkq was serviced and cached + self.service_netlinkq(self.netlinkq_notify_event) + self.netlinkq_notify_event.wait() + self.netlinkq_notify_event.clear() + self.netlinkq_notify_event = False + + def get_all_netconf_wait_netlinkq(self): + self.logger.info("requesting netconf dump") + self.netlinkq_notify_event = threading.Event() + self.netconf_dump() + # block until the netlinkq was serviced and cached + self.service_netlinkq(self.netlinkq_notify_event) + self.netlinkq_notify_event.wait() + self.netlinkq_notify_event.clear() + self.netlinkq_notify_event = False + + def vlan_modify(self, msgtype, ifindex, vlanid_start, vlanid_end=None, bridge_self=False, bridge_master=False, pvid=False, untagged=False): + """ + iproute2 bridge/vlan.c vlan_modify() + """ + assert msgtype in (RTM_SETLINK, RTM_DELLINK), "Invalid msgtype %s, must be RTM_SETLINK or RTM_DELLINK" % msgtype + assert vlanid_start >= 1 and vlanid_start <= 4096, "Invalid VLAN start %s" % vlanid_start + + if vlanid_end is None: + vlanid_end = vlanid_start + + assert vlanid_end >= 1 and vlanid_end <= 4096, "Invalid VLAN end %s" % vlanid_end + assert vlanid_start <= vlanid_end, "Invalid VLAN range %s-%s, start must be <= end" % (vlanid_start, vlanid_end) + + debug = msgtype in self.debug + bridge_flags = 0 + vlan_info_flags = 0 + + link = Link(msgtype, debug, use_color=self.use_color) + link.flags = NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_BRIDGE, ifindex, 0, 0) + + if bridge_self: + bridge_flags |= Link.BRIDGE_FLAGS_SELF + + if bridge_master: + bridge_flags |= Link.BRIDGE_FLAGS_MASTER + + if pvid: + vlan_info_flags |= Link.BRIDGE_VLAN_INFO_PVID + + if untagged: + vlan_info_flags |= Link.BRIDGE_VLAN_INFO_UNTAGGED + + ifla_af_spec = OrderedDict() + + if bridge_flags: + ifla_af_spec[Link.IFLA_BRIDGE_FLAGS] = bridge_flags + + # just one VLAN + if vlanid_start == vlanid_end: + ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [(vlan_info_flags, vlanid_start), ] + + # a range of VLANs + else: + ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [ + (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_BEGIN, vlanid_start), + (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_END, vlanid_end) + ] + + link.add_attribute(Link.IFLA_AF_SPEC, ifla_af_spec) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error(link) + + ############################################################################################################# + # Netlink API ############################################################################################### + ############################################################################################################# + + def link_add(self, ifname, kind): + self.link_add_with_attributes(ifname, kind, {}) + + def link_add_with_attributes(self, ifname, kind, ifla): + """ + Build and TX a RTM_NEWLINK message to add the desired interface + """ + if ifla: + self.logger.info("%s: netlink: ip link add dev %s type %s (with attributes)" % (ifname, ifname, kind)) + self.logger.debug("attributes: %s" % ifla) + else: + self.logger.info("%s: netlink: ip link add dev %s type %s" % (ifname, ifname, kind)) + try: + debug = RTM_NEWLINK in self.debug + + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + + for nl_attr, value in ifla.items(): + link.add_attribute(nl_attr, value) + + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: kind + }) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) + except Exception as e: + raise Exception("%s: cannot create link %s type %s" % (ifname, ifname, kind)) + + def link_add_with_attributes_dry_run(self, ifname, kind, ifla): + self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type %s" % (ifname, kind)) + self.logger.debug("attributes: %s" % ifla) + + ### + + def __link_set_flag(self, ifname, flags): + """ + Bring interface 'ifname' up (raises on error) + :param ifname: + :return: + """ + try: + link = Link(RTM_NEWLINK, RTM_NEWLINK in self.debug, use_color=self.use_color) + link.flags = NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack("=BxxxiLL", socket.AF_UNSPEC, 0, flags, Link.IFF_UP) + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.build_message(self.sequence.next(), self.pid) + result = self.tx_nlpacket_get_response_with_error(link) + # if we reach this code it means the operation went through + # without exception we can update the cache value this is + # needed for the following case (and probably others): + # + # ifdown bond0 ; ip link set dev bond_slave down + # ifup bond0 + # at the beginning the slaves are admin down + # ifupdownmain:run_up link up the slave + # the bond addon check if the slave is up or down + # and admin down the slave before enslavement + # but the cache didn't process the UP notification yet + # so the cache has a stale value and we try to enslave + # a port, that is admin up, to a bond resulting + # in an unexpected failure + self.cache.override_link_flag(ifname, flags) + return result + except Exception as e: + raise NetlinkError(e, "ip link set dev %s %s" % (ifname, "up" if flags == Link.IFF_UP else "down"), ifname=ifname) + + def link_up(self, ifname): + if not self.cache.link_is_up(ifname): + self.logger.info("%s: netlink: ip link set dev %s up" % (ifname, ifname)) + self.__link_set_flag(ifname, flags=Link.IFF_UP) + + def link_up_force(self, ifname): + self.logger.info("%s: netlink: ip link set dev %s up" % (ifname, ifname)) + self.__link_set_flag(ifname, flags=Link.IFF_UP) + + def link_down(self, ifname): + if self.cache.link_is_up(ifname): + self.logger.info("%s: netlink: ip link set dev %s down" % (ifname, ifname)) + self.__link_set_flag(ifname, flags=0) + + def link_down_force(self, ifname): + self.logger.info("%s: netlink: ip link set dev %s down" % (ifname, ifname)) + self.__link_set_flag(ifname, flags=0) + + def link_up_dry_run(self, ifname): + self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s up" % ifname) + + def link_down_dry_run(self, ifname): + self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s down" % ifname) + + def link_down_force_dry_run(self, ifname): + self.link_down_dry_run(ifname) + + ### + + def __link_set_protodown(self, ifname, state): + debug = RTM_NEWLINK in self.debug + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack("=BxxxiLL", socket.AF_UNSPEC, 0, 0, 0) + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_PROTO_DOWN, state) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error(link) + + def link_set_protodown_on(self, ifname): + """ + Bring ifname up by setting IFLA_PROTO_DOWN on + """ + self.logger.info("%s: netlink: set link %s protodown on" % (ifname, ifname)) + try: + self.__link_set_protodown(ifname, 1) + except Exception as e: + raise NetlinkError(e, "cannot set link %s protodown on" % ifname, ifname=ifname) + + def link_set_protodown_off(self, ifname): + """ + Take ifname down by setting IFLA_PROTO_DOWN off + """ + self.logger.info("%s: netlink: set link %s protodown off" % (ifname, ifname)) + try: + self.__link_set_protodown(ifname, 0) + except Exception as e: + raise NetlinkError(e, "cannot set link %s protodown off" % ifname, ifname=ifname) + + def link_set_protodown_on_dry_run(self, ifname): + self.log_info_ifname_dry_run(ifname, "netlink: set link %s protodown on" % ifname) + + def link_set_protodown_off_dry_run(self, ifname): + self.log_info_ifname_dry_run(ifname, "netlink: set link %s protodown off" % ifname) + + ### + + def link_del(self, ifname): + """ + Send RTM_DELLINK request + :param ifname: + :return: + """ + self.logger.info("%s: netlink: ip link del %s" % (ifname, ifname)) + try: + ifindex = self.cache.get_ifindex(ifname) + debug = RTM_DELLINK in self.debug + + link = Link(RTM_DELLINK, debug, use_color=self.use_color) + link.flags = NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack("Bxxxiii", socket.AF_UNSPEC, ifindex, 0, 0) + link.build_message(self.sequence.next(), self.pid) + + try: + # We need to register this ifname so the cache can ignore and discard + # any further RTM_NEWLINK packet until we receive the associated + # RTM_DELLINK notification + self.cache.append_to_ignore_rtm_newlinkq(ifname) + + result = self.tx_nlpacket_get_response_with_error(link) + + # Manually purge the cache entry for ifname to make sure we don't have + # any stale value in our cache + self.cache.force_remove_link(ifname) + return result + except: + # Something went wrong while sending the RTM_DELLINK request + # we need to clear ifname from the ignore_rtm_newlinkq list + self.cache.remove_from_ignore_rtm_newlinkq(ifname) + raise + except Exception as e: + raise NetlinkError(e, "cannot delete link %s" % ifname, ifname=ifname) + + def link_del_dry_run(self, ifname): + self.log_info_ifname_dry_run(ifname, "netlink: ip link del %s" % ifname) + + ### + + def __link_set_master(self, ifname, master_ifindex, master_ifname=None): + debug = RTM_NEWLINK in self.debug + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack("=BxxxiLL", socket.AF_UNSPEC, 0, 0, 0) + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_MASTER, master_ifindex) + link.build_message(self.sequence.next(), self.pid) + result = self.tx_nlpacket_get_response_with_error(link) + # opti: + # if we reach this code it means the slave/unslave opreation went through + # we can manually update our cache to reflect the change without having + # to wait for the netlink notification + if master_ifindex: + self.cache.force_add_slave(master_ifname, ifname) + else: + self.cache.override_cache_unslave_link(slave=ifname, master=master_ifname) + return result + + def link_set_master(self, ifname, master_ifname): + self.logger.info("%s: netlink: ip link set dev %s master %s" % (ifname, ifname, master_ifname)) + try: + self.__link_set_master(ifname, self.cache.get_ifindex(master_ifname), master_ifname=master_ifname) + except Exception as e: + raise NetlinkError(e, "cannot enslave link %s to %s" % (ifname, master_ifname), ifname=ifname) + + def link_set_nomaster(self, ifname): + self.logger.info("%s: netlink: ip link set dev %s nomaster" % (ifname, ifname)) + try: + self.cache.append_to_rtm_newlink_nomasterq(ifname) + self.__link_set_master(ifname, 0) + except Exception as e: + self.cache.remove_from_rtm_newlink_nomasterq(ifname) + raise NetlinkError(e, "cannot un-enslave link %s" % ifname, ifname=ifname) + + def link_set_master_dry_run(self, ifname, master_dev): + self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s master %s" % (ifname, master_dev)) + + def link_set_nomaster_dry_run(self, ifname): + self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s nomaster" % ifname) + + ### + + def link_set_address_dry_run(self, ifname, hw_address): + self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s address %s" % (ifname, hw_address)) + + def link_set_address(self, ifname, hw_address): + is_link_up = self.cache.link_is_up(ifname) + # check if the link is already up or not if the link is + # up we need to down it then make sure we up it again + try: + if is_link_up: + self.link_down_force(ifname) + + self.logger.info("%s: netlink: ip link set dev %s address %s" % (ifname, ifname, hw_address)) + debug = RTM_NEWLINK in self.debug + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_ADDRESS, hw_address) + + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error(link) + except Exception as e: + raise NetlinkError(e, "cannot set dev %s address %s" % (ifname, hw_address), ifname=ifname) + finally: + if is_link_up: + self.link_up_force(ifname) + + ### + + __macvlan_mode = { + "private": Link.MACVLAN_MODE_PRIVATE, + "vepa": Link.MACVLAN_MODE_VEPA, + "bridge": Link.MACVLAN_MODE_BRIDGE, + "passthru": Link.MACVLAN_MODE_PASSTHRU, + "source": Link.MACVLAN_MODE_SOURCE + } + + def link_add_macvlan(self, ifname, macvlan_ifname, macvlan_mode=None): + self.logger.info( + "%s: netlink: ip link add link %s name %s type macvlan mode %s" + % (ifname, ifname, macvlan_ifname, macvlan_mode if macvlan_mode else "private") + ) + try: + ifindex = self.cache.get_ifindex(ifname) + debug = RTM_NEWLINK in self.debug + + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack("Bxxxiii", socket.AF_UNSPEC, 0, 0, 0) + + link.add_attribute(Link.IFLA_IFNAME, ifname) + + if ifindex: + link.add_attribute(Link.IFLA_LINK, ifindex) + + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: "macvlan", + Link.IFLA_INFO_DATA: { + Link.IFLA_MACVLAN_MODE: self.__macvlan_mode.get( + macvlan_mode, + Link.MACVLAN_MODE_PRIVATE + ) + } + }) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) + + except Exception as e: + raise Exception( + "netlink: %s: cannot create macvlan %s: %s" + % (ifname, macvlan_ifname, str(e)) + ) + + def link_add_macvlan_dry_run(self, ifname, macvlan_ifame, macvlan_mode=None): + self.log_info_ifname_dry_run( + ifname, + "netlink: ip link add link %s name %s type macvlan mode %s" + % (ifname, macvlan_ifame, macvlan_mode if macvlan_mode else "private") + ) + return True + + ### + + def link_add_vrf(self, ifname, vrf_table): + self.logger.info("%s: netlink: ip link add dev %s type vrf table %s" % (ifname, ifname, vrf_table)) + + debug = RTM_NEWLINK in self.debug + + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: "vrf", + Link.IFLA_INFO_DATA: { + Link.IFLA_VRF_TABLE: int(vrf_table) + } + }) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) + + def link_add_vrf_dry_run(self, ifname, vrf_table): + self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type vrf table %s" % (ifname, vrf_table)) + return True + + ### + + def link_add_bridge(self, ifname, mtu=None): + self.logger.info("%s: netlink: ip link add dev %s type bridge" % (ifname, ifname)) + + debug = RTM_NEWLINK in self.debug + + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + link.add_attribute(Link.IFLA_IFNAME, ifname) + + if mtu: + self.logger.info("%s: netlink: set bridge mtu %s" % (ifname, mtu)) + link.add_attribute(Link.IFLA_MTU, mtu) + + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: "bridge", + }) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) + + def link_add_bridge_dry_run(self, ifname): + self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bridge" % ifname) + return True + + def link_set_bridge_info_data(self, ifname, ifla_info_data, link_just_created): + self.logger.info( + "%s: netlink: ip link set dev %s type bridge (with attributes)" + % (ifname, ifname) + ) + self.logger.debug("attributes: %s" % ifla_info_data) + + try: + debug = RTM_NEWLINK in self.debug + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: "bridge", + Link.IFLA_INFO_DATA: ifla_info_data + }) + link.build_message(self.sequence.next(), self.pid) + result = self.tx_nlpacket_get_response_with_error(link) + + if link_just_created: + self.cache.update_link_info_data(ifname, ifla_info_data) + + return result + except Exception as e: + raise Exception("%s: netlink: cannot create bridge or set attributes: %s" % (ifname, str(e))) + + def link_set_bridge_info_data_dry_run(self, ifname, ifla_info_data): + self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bridge (with attributes)" % ifname) + self.logger.debug("attributes: %s" % ifla_info_data) + + ### + + def link_add_bridge_vlan(self, ifname, vlan_id): + """ + Add VLAN(s) to a bridge interface + """ + self.logger.info("%s: netlink: bridge vlan add vid %s dev %s" % (ifname, vlan_id, ifname)) + try: + ifindex = self.cache.get_ifindex(ifname) + self.vlan_modify(RTM_SETLINK, ifindex, vlan_id, bridge_self=True) + # TODO: we should probably fill our internal cache when when the ACK is received. + except Exception as e: + raise NetlinkError(e, "cannot add bridge vlan %s" % vlan_id, ifname=ifname) + + def link_del_bridge_vlan(self, ifname, vlan_id): + """ + Delete VLAN(s) from a bridge interface + """ + self.logger.info("%s: netlink: bridge vlan del vid %s dev %s" % (ifname, vlan_id, ifname)) + try: + ifindex = self.cache.get_ifindex(ifname) + self.vlan_modify(RTM_DELLINK, ifindex, vlan_id, bridge_self=True) + except Exception as e: + raise NetlinkError(e, "cannot remove bridge vlan %s" % vlan_id, ifname=ifname) + + def link_add_bridge_vlan_dry_run(self, ifname, vlan_id): + self.log_info_ifname_dry_run(ifname, "netlink: bridge vlan add vid %s dev %s" % (vlan_id, ifname)) + + def link_del_bridge_vlan_dry_run(self, ifname, vlan_id): + self.log_info_ifname_dry_run(ifname, "netlink: bridge vlan del vid %s dev %s" % (vlan_id, ifname)) + + ### + + def link_add_vlan(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None): + """ + ifindex is the index of the parent interface that this sub-interface + is being added to + + If you name an interface swp2.17 but assign it to vlan 12, the kernel + will return a very misleading NLE_MSG_OVERFLOW error. It only does + this check if the ifname uses dot notation. + + Do this check here so we can provide a more intuitive error + """ + try: + if vlan_protocol: + self.logger.info("%s: netlink: ip link add link %s name %s type vlan id %s protocol %s" + % (ifname, vlan_raw_device, ifname, vlan_id, vlan_protocol)) + + else: + self.logger.info("%s: netlink: ip link add link %s name %s type vlan id %s" + % (ifname, vlan_raw_device, ifname, vlan_id)) + + if "." in ifname: + ifname_vlanid = int(ifname.split(".")[-1]) + + if ifname_vlanid != vlan_id: + raise Exception( + "Interface %s must belong to VLAN %d (VLAN %d was requested)" + % (ifname, ifname_vlanid, vlan_id) + ) + + ifindex = self.cache.get_ifindex(vlan_raw_device) + + ifla_info_data = {Link.IFLA_VLAN_ID: vlan_id} + + if vlan_protocol: + ifla_info_data[Link.IFLA_VLAN_PROTOCOL] = vlan_protocol + + debug = RTM_NEWLINK in self.debug + + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_LINK, ifindex) + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: "vlan", + Link.IFLA_INFO_DATA: ifla_info_data + }) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) + except Exception as e: + raise NetlinkError(e, "cannot create vlan %s %s" % (ifname, vlan_id), ifname=ifname) + + def link_add_vlan_dry_run(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None): + """ + ifindex is the index of the parent interface that this sub-interface + is being added to + + If you name an interface swp2.17 but assign it to vlan 12, the kernel + will return a very misleading NLE_MSG_OVERFLOW error. It only does + this check if the ifname uses dot notation. + + Do this check here so we can provide a more intuitive error + """ + if vlan_protocol: + self.log_info_ifname_dry_run( + ifname, + "netlink: ip link add link %s name %s type vlan id %s protocol %s" + % (vlan_raw_device, ifname, vlan_id, vlan_protocol) + ) + + else: + self.log_info_ifname_dry_run( + ifname, + "netlink: ip link add link %s name %s type vlan id %s" + % (vlan_raw_device, ifname, vlan_id) + ) + + ### + + def link_add_vxlan_with_info_data(self, ifname, info_data): + """ + cmd = ["ip link add %s type vxlan id %s" % (ifname, id)] + + if port: + cmd.append("dstport %s" % port) + info_data[nlpacket.Link.IFLA_VXLAN_PORT] = int(port) + + if local: + cmd.append("local %s" % local) + info_data[nlpacket.Link.IFLA_VXLAN_LOCAL] = local + + if ageing: + cmd.append("ageing %s" % ageing) + info_data[nlpacket.Link.IFLA_VXLAN_AGEING] = int(ageing) + + if group: + if group.is_multicast: + cmd.append("group %s" % group) + else: + cmd.append("remote %s" % group) + info_data[nlpacket.Link.IFLA_VXLAN_GROUP] = group + else: + cmd.append("noremote") + + if not learning: + cmd.append("nolearning") + info_data[nlpacket.Link.IFLA_VXLAN_LEARNING] = int(learning) + + if physdev: + cmd.append("dev %s" % physdev) + info_data[nlpacket.Link.IFLA_VXLAN_LINK] = self.cache.get_ifindex(physdev) + + if ttl: + cmd.append("ttl %s" % ttl) + info_data[nlpacket.Link.IFLA_VXLAN_TTL] = ttl + + self.logger.info('%s: netlink: %s' % (ifname, " ".join(cmd))) + + :param ifname: + :param info_data: + :return: + """ + self.logger.info( + "%s: netlink: ip link add dev %s type vxlan id %s (with attributes)" + % (ifname, ifname, info_data.get(Link.IFLA_VXLAN_ID)) + ) + self.logger.debug("attributes: %s" % info_data) + + debug = RTM_NEWLINK in self.debug + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: "vxlan", + Link.IFLA_INFO_DATA: info_data + }) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) + + def link_add_vxlan_with_info_data_dry_run(self, ifname, info_data): + self.log_info_ifname_dry_run( + ifname, + "netlink: ip link add dev %s type vxlan id %s (with attributes)" + % (ifname, info_data.get(Link.IFLA_VXLAN_ID)) + ) + self.logger.debug("attributes: %s" % info_data) + return True + + ### + + def link_add_bond_with_info_data(self, ifname, ifla_info_data): + self.logger.info( + "%s: netlink: ip link add dev %s type bond (with attributes)" + % (ifname, ifname) + ) + self.logger.debug("attributes: %s" % ifla_info_data) + + try: + debug = RTM_NEWLINK in self.debug + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0) + link.add_attribute(Link.IFLA_IFNAME, ifname) + link.add_attribute(Link.IFLA_LINKINFO, { + Link.IFLA_INFO_KIND: "bond", + Link.IFLA_INFO_DATA: ifla_info_data + }) + link.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) + except Exception as e: + raise Exception("%s: netlink: cannot create bond with attributes: %s" % (ifname, str(e))) + + def link_add_bond_with_info_data_dry_run(self, ifname, ifla_info_data): + self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bond (with attributes)" % ifname) + self.logger.debug("attributes: %s" % ifla_info_data) + + ### + + def link_set_brport_with_info_slave_data(self, ifname, kind, ifla_info_data, ifla_info_slave_data): + """ + Build and TX a RTM_NEWLINK message to add the desired interface + """ + self.logger.info("%s: netlink: ip link set dev %s: bridge port attributes" % (ifname, ifname)) + self.logger.debug("attributes: %s" % ifla_info_slave_data) + + try: + debug = RTM_NEWLINK in self.debug + + link = Link(RTM_NEWLINK, debug, use_color=self.use_color) + link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + link.body = struct.pack("Bxxxiii", socket.AF_UNSPEC, 0, 0, 0) + + if ifname: + link.add_attribute(Link.IFLA_IFNAME, ifname) + + linkinfo = dict() + + if kind: + linkinfo[Link.IFLA_INFO_KIND] = kind + linkinfo[Link.IFLA_INFO_DATA] = ifla_info_data + + linkinfo[Link.IFLA_INFO_SLAVE_KIND] = "bridge" + linkinfo[Link.IFLA_INFO_SLAVE_DATA] = ifla_info_slave_data + + link.add_attribute(Link.IFLA_LINKINFO, linkinfo) + link.build_message(self.sequence.next(), self.pid) + + # the brport already exists and is cached - after this operation we most + # likely don't need to do anything about the brport so we don't need to + # wait for the new notification to be cached. + return self.tx_nlpacket_get_response_with_error(link) + except Exception as e: + raise Exception("netlink: %s: cannot set %s (bridge slave) with options: %s" % (kind, ifname, str(e))) + + def link_set_brport_with_info_slave_data_dry_run(self, ifname, _, __, ifla_info_slave_data): + self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s: bridge port attributes" % ifname) + self.logger.debug("attributes: %s" % ifla_info_slave_data) + + ############################################################################ + # ADDRESS + ############################################################################ + + def addr_add_dry_run(self, ifname, addr, broadcast=None, peer=None, scope=None, preferred_lifetime=None, metric=None): + log_msg = ["netlink: ip addr add %s dev %s" % (addr, ifname)] + + if scope: + log_msg.append("scope %s" % scope) + + if broadcast: + log_msg.append("broadcast %s" % broadcast) + + if preferred_lifetime: + log_msg.append("preferred_lft %s" % preferred_lifetime) + + if peer: + log_msg.append("peer %s" % peer) + + if metric: + log_msg.append("metric %s" % metric) + + self.log_info_ifname_dry_run(ifname, " ".join(log_msg)) + + def addr_add(self, ifname, addr, broadcast=None, peer=None, scope=None, preferred_lifetime=None, metric=None): + log_msg = ["%s: netlink: ip addr add %s dev %s" % (ifname, addr, ifname)] + log_msg_displayed = False + try: + # We might need to check if metric/peer and other attribute are also + # correctly cached. + # We might also need to add a "force" attribute to skip the cache check + if self.cache.addr_is_cached(ifname, addr): + return + + if scope: + log_msg.append("scope %s" % scope) + scope_value = RT_SCOPES.get(scope, 0) + else: + scope_value = 0 + + debug = RTM_NEWADDR in self.debug + + packet = Address(RTM_NEWADDR, debug, use_color=self.use_color) + packet.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK + packet.family = self.IPNetwork_version_to_family.get(addr.version) + + packet.add_attribute(Address.IFA_ADDRESS, addr) + packet.add_attribute(Address.IFA_LOCAL, addr) + + if broadcast: + log_msg.append("broadcast %s" % broadcast) + packet.add_attribute(Address.IFA_BROADCAST, broadcast) + + if preferred_lifetime: + # struct ifa_cacheinfo { + # __u32 ifa_prefered; + # __u32 ifa_valid; + # __u32 cstamp; /* created timestamp, hundredths of seconds */ + # __u32 tstamp; /* updated timestamp, hundredths of seconds */ + # }; + log_msg.append("preferred_lft %s" % preferred_lifetime) + + if preferred_lifetime.lower() == "forever": + preferred_lifetime = INFINITY_LIFE_TIME + + packet.add_attribute(Address.IFA_CACHEINFO, (int(preferred_lifetime), INFINITY_LIFE_TIME, 0, 0)) + + if metric: + log_msg.append("metric %s" % metric) + packet.add_attribute(Address.IFA_RT_PRIORITY, int(metric)) + + if peer: + log_msg.append("peer %s" % peer) + packet.add_attribute(Address.IFA_ADDRESS, peer) + packet_prefixlen = peer.prefixlen + else: + packet_prefixlen = addr.prefixlen + + self.logger.info(" ".join(log_msg)) + log_msg_displayed = True + + packet.body = struct.pack("=4Bi", packet.family, packet_prefixlen, 0, scope_value, self.cache.get_ifindex(ifname)) + packet.build_message(self.sequence.next(), self.pid) + return self.tx_nlpacket_get_response_with_error(packet) + except Exception as e: + if not log_msg_displayed: + # just in case we get an exception before we reach the log.info + # we should display it before we raise the exception + log.info(" ".join(log_msg)) + raise NetlinkError(e, "cannot add address %s dev %s" % (addr, ifname), ifname=ifname) + + ### + + def addr_del_dry_run(self, ifname, addr): + self.log_info_ifname_dry_run(ifname, "netlink: ip addr del %s dev %s" % (addr, ifname)) + + def addr_del(self, ifname, addr): + if not self.cache.addr_is_cached(ifname, addr): + return + self.logger.info("%s: netlink: ip addr del %s dev %s" % (ifname, addr, ifname)) + try: + debug = RTM_DELADDR in self.debug + + packet = Address(RTM_DELADDR, debug, use_color=self.use_color) + packet.flags = NLM_F_REQUEST | NLM_F_ACK + packet.family = self.IPNetwork_version_to_family.get(addr.version) + packet.body = struct.pack("=4Bi", packet.family, addr.prefixlen, 0, 0, self.cache.get_ifindex(ifname)) + + packet.add_attribute(Address.IFA_LOCAL, addr) + + packet.build_message(self.sequence.next(), self.pid) + result = self.tx_nlpacket_get_response_with_error(packet) + + # RTM_DELADDR successful, we need to update our cache + # to make sure we don't have any stale ip addr cached + self.cache.force_remove_addr(ifname, addr) + + return result + except Exception as e: + raise NetlinkError(e, "cannot delete address %s dev %s" % (addr, ifname), ifname=ifname) + + def addr_flush(self, ifname): + """ + From iproute2/ip/ipaddress.c + /* + * Note that the kernel may delete multiple addresses for one + * delete request (e.g. if ipv4 address promotion is disabled). + * Since a flush operation is really a series of delete requests + * its possible that we may request an address delete that has + * already been done by the kernel. Therefore, ignore EADDRNOTAVAIL + * errors returned from a flush request + */ + """ + for addr in self.cache.get_addresses_list(ifname): + try: + self.addr_del(ifname, addr) + except: + pass + + ######################## + # TEMPORARY DEBUG CODE # + ######################## + + def DEBUG_ON(self): + self.debug_link(True) + self.debug_address(True) + nllistener.log.setLevel(DEBUG) + nlpacket.log.setLevel(DEBUG) + nlmanager.log.setLevel(DEBUG) + + def DEBUG_OFF(self): + self.debug_address(False) + self.debug_link(False) + nllistener.log.setLevel(WARNING) + nlpacket.log.setLevel(WARNING) + nlmanager.log.setLevel(WARNING) diff --git a/ifupdown2/lib/status.py b/ifupdown2/lib/status.py new file mode 100644 index 00000000..3ce9f05c --- /dev/null +++ b/ifupdown2/lib/status.py @@ -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 diff --git a/ifupdown2/lib/sysfs.py b/ifupdown2/lib/sysfs.py new file mode 100644 index 00000000..ab4b2a79 --- /dev/null +++ b/ifupdown2/lib/sysfs.py @@ -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() diff --git a/ifupdown2/man/ifdown.8.rst b/ifupdown2/man/ifdown.8.rst index 817c5984..6fc202c6 100644 --- a/ifupdown2/man/ifdown.8.rst +++ b/ifupdown2/man/ifdown.8.rst @@ -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 diff --git a/ifupdown2/man/ifup.8.rst b/ifupdown2/man/ifup.8.rst index 817c5984..6fc202c6 100644 --- a/ifupdown2/man/ifup.8.rst +++ b/ifupdown2/man/ifup.8.rst @@ -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 diff --git a/ifupdown2/nlmanager/nllistener.py b/ifupdown2/nlmanager/nllistener.py index e9c877e3..393e511e 100644 --- a/ifupdown2/nlmanager/nllistener.py +++ b/ifupdown2/nlmanager/nllistener.py @@ -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 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 @@ def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvb 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 @@ def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvb 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 @@ def supported_messages_del(self, msgtype): 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) - - # 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 = {} + 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) + 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 @@ def run(self): # 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 @@ def run(self): 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 @@ def run(self): # 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 @@ def run(self): 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 @@ def run(self): 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 @@ def run(self): 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 @@ def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, er 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 @@ def __str__(self): 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 @@ def signal_int_handler(self, signal, frame): 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 @@ def rx_rtm_dellink(self, msg): 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 @@ def rx_rtm_delroute(self, msg): 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 @@ def filter_by_attribute(self, add, filter_type, msgtype, attribute, attribute_va 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 @@ def service_netlinkq(self): 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 @@ def service_netlinkq(self): 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 @@ def service_netlinkq(self): 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])) diff --git a/ifupdown2/nlmanager/nlmanager.py b/ifupdown2/nlmanager/nlmanager.py index deb7ecf9..806b8e67 100644 --- a/ifupdown2/nlmanager/nlmanager.py +++ b/ifupdown2/nlmanager/nlmanager.py @@ -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 @@ def debug_route(self, enabled): 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 @@ def tx_nlpacket_get_response(self, nlpacket): 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 @@ def request_dump(self, rtm_type, family, debug): 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 @@ def link_add_vlan(self, ifindex, ifname, vlanid, vlan_protocol=None): 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 @@ def netconf_dump(self): 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) diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py index cd0d332d..fcb89fb1 100644 --- a/ifupdown2/nlmanager/nlpacket.py +++ b/ifupdown2/nlmanager/nlpacket.py @@ -1,6 +1,6 @@ # Copyright (c) 2009-2013, Exa Networks Limited # Copyright (c) 2009-2013, Thomas Mangin -# Copyright (c) 2015-2017 Cumulus Networks, Inc. +# Copyright (c) 2015-2020 Cumulus Networks, Inc. # # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -30,7 +30,7 @@ import logging import struct -from ipaddr import IPv4Address, IPv6Address, IPAddress +from ipaddr import IPNetwork, IPv4Address, IPv6Address, IPAddress from binascii import hexlify from pprint import pformat from socket import AF_UNSPEC, AF_INET, AF_INET6, AF_BRIDGE, htons @@ -40,6 +40,10 @@ log = logging.getLogger(__name__) SYSLOG_EXTRA_DEBUG = 5 +ETH_P_IP = 0x0800 +ETH_P_IPV6 = 0x86DD + +INFINITY_LIFE_TIME = 0xFFFFFFFF # Interface name buffer size #define IFNAMSIZ 16 (kernel source) IF_NAME_SIZE = 15 # 15 because python doesn't have \0 @@ -72,8 +76,13 @@ RTM_GETQDISC = 0x26 RTM_NEWNETCONF = 80 +RTM_DELNETCONF = 81 RTM_GETNETCONF = 82 +RTM_NEWMDB = 84 +RTM_DELMDB = 85 +RTM_GETMDB = 86 + # Netlink message flags NLM_F_REQUEST = 0x01 # It is query message. NLM_F_MULTI = 0x02 # Multipart message, terminated by NLMSG_DONE @@ -112,12 +121,41 @@ RTMGRP_DECnet_IFADDR = 0x1000 RTMGRP_DECnet_ROUTE = 0x4000 RTMGRP_IPV6_PREFIX = 0x20000 +RTNLGRP_MDB = 0x1A + + +def nl_mgrp(group): + """ + The api is a reimplementation of "nl_mgrp" function from + iproute2/include/utils.h + """ + if group > 31: + raise Exception("%d Invalid Group" % group) + else: + group = (1 << (group - 1)) if group else 0 + return group + + +RTNLGRP_IPV4_NETCONF = nl_mgrp(24) +RTNLGRP_IPV6_NETCONF = nl_mgrp(25) +RTNLGRP_MPLS_NETCONF = nl_mgrp(29) + RTMGRP_ALL = (RTMGRP_LINK | RTMGRP_NOTIFY | RTMGRP_NEIGH | RTMGRP_TC | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_MROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_RULE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_MROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFINFO | - RTMGRP_DECnet_IFADDR | RTMGRP_DECnet_ROUTE | - RTMGRP_IPV6_PREFIX) + RTMGRP_DECnet_IFADDR | RTMGRP_DECnet_ROUTE | nl_mgrp(RTNLGRP_MDB) | + RTMGRP_IPV6_PREFIX | RTNLGRP_IPV4_NETCONF | RTNLGRP_IPV6_NETCONF | RTNLGRP_MPLS_NETCONF) + +# /etc/iproute2/rt_scopes +RT_SCOPES = { + "global": 0, + "universe": 0, + "nowhere": 255, + "host": 254, + "link": 253, + "site": 200 +} AF_MPLS = 28 @@ -216,210 +254,1073 @@ def padded_length(length): return int((length + 3) / 4) * 4 -class Attribute(object): +class NetlinkPacket_IFLA_LINKINFO_Attributes: - def __init__(self, atype, string, logger): - self.atype = atype - self.string = string - self.HEADER_PACK = '=HH' - self.HEADER_LEN = calcsize(self.HEADER_PACK) - self.PACK = None - self.LEN = None - self.value = None - self.nested = False - self.net_byteorder = False - self.log = logger + # ========================================= + # IFLA_LINKINFO attributes + # ========================================= + IFLA_INFO_UNSPEC = 0 + IFLA_INFO_KIND = 1 + IFLA_INFO_DATA = 2 + IFLA_INFO_XSTATS = 3 + IFLA_INFO_SLAVE_KIND = 4 + IFLA_INFO_SLAVE_DATA = 5 + IFLA_INFO_MAX = 6 - def __str__(self): - return self.string + ifla_info_to_string = { + IFLA_INFO_UNSPEC : 'IFLA_INFO_UNSPEC', + IFLA_INFO_KIND : 'IFLA_INFO_KIND', + IFLA_INFO_DATA : 'IFLA_INFO_DATA', + IFLA_INFO_XSTATS : 'IFLA_INFO_XSTATS', + IFLA_INFO_SLAVE_KIND : 'IFLA_INFO_SLAVE_KIND', + IFLA_INFO_SLAVE_DATA : 'IFLA_INFO_SLAVE_DATA', + IFLA_INFO_MAX : 'IFLA_INFO_MAX' + } - def set_value(self, value): - self.value = value + # ========================================= + # IFLA_INFO_DATA attributes for vlan + # ========================================= + IFLA_VLAN_UNSPEC = 0 + IFLA_VLAN_ID = 1 + IFLA_VLAN_FLAGS = 2 + IFLA_VLAN_EGRESS_QOS = 3 + IFLA_VLAN_INGRESS_QOS = 4 + IFLA_VLAN_PROTOCOL = 5 - def set_nested(self, nested): - self.nested = nested + ifla_vlan_to_string = { + IFLA_VLAN_UNSPEC : 'IFLA_VLAN_UNSPEC', + IFLA_VLAN_ID : 'IFLA_VLAN_ID', + IFLA_VLAN_FLAGS : 'IFLA_VLAN_FLAGS', + IFLA_VLAN_EGRESS_QOS : 'IFLA_VLAN_EGRESS_QOS', + IFLA_VLAN_INGRESS_QOS : 'IFLA_VLAN_INGRESS_QOS', + IFLA_VLAN_PROTOCOL : 'IFLA_VLAN_PROTOCOL' + } - def set_net_byteorder(self, net_byteorder): - self.net_byteorder = net_byteorder + ifla_vlan_protocol_dict = { + '802.1Q': 0x8100, + '802.1q': 0x8100, - def pad_bytes_needed(self, length): - """ - Return the number of bytes that should be added to align on a 4-byte boundry - """ - remainder = length % 4 + '802.1ad': 0x88A8, + '802.1AD': 0x88A8, + '802.1Ad': 0x88A8, + '802.1aD': 0x88A8, - if remainder: - return 4 - remainder + 0x8100: '802.1Q', + 0x88A8: '802.1ad' + } - return 0 + # ========================================= + # IFLA_INFO_DATA attributes for macvlan + # ========================================= + IFLA_MACVLAN_UNSPEC = 0 + IFLA_MACVLAN_MODE = 1 - def pad(self, length, raw): - pad = self.pad_bytes_needed(length) + ifla_macvlan_to_string = { + IFLA_MACVLAN_UNSPEC : 'IFLA_MACVLAN_UNSPEC', + IFLA_MACVLAN_MODE : 'IFLA_MACVLAN_MODE' + } - if pad: - raw += '\0' * pad + # enum macvlan_mode + MACVLAN_MODE_PRIVATE = 1 # don't talk to other macvlans */ + MACVLAN_MODE_VEPA = 2 # talk to other ports through ext bridge */ + MACVLAN_MODE_BRIDGE = 4 # talk to bridge ports directly */ + MACVLAN_MODE_PASSTHRU = 8 # take over the underlying device */ + MACVLAN_MODE_SOURCE = 16 # use source MAC address list to assign */ - return raw + macvlan_mode_to_string = { + MACVLAN_MODE_PRIVATE : 'MACVLAN_MODE_PRIVATE', + MACVLAN_MODE_VEPA : 'MACVLAN_MODE_VEPA', + MACVLAN_MODE_BRIDGE : 'MACVLAN_MODE_BRIDGE', + MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU', + MACVLAN_MODE_SOURCE : 'MACVLAN_MODE_SOURCE' + } - def encode(self): + # ========================================= + # IFLA_INFO_DATA attributes for xfrm + # ========================================= + IFLA_XFRM_UNSPEC = 0 + IFLA_XFRM_LINK = 1 + IFLA_XFRM_IF_ID = 2 - if not self.LEN: - raise Exception('Please define an encode() method in your child attribute class, or do not use AttributeGeneric') + ifla_xfrm_to_string = { + IFLA_XFRM_UNSPEC: 'IFLA_XFRM_UNSPEC', + IFLA_XFRM_LINK : 'IFLA_XFRM_LINK', + IFLA_XFRM_IF_ID : 'IFLA_XFRM_IF_ID' + } - length = self.HEADER_LEN + self.LEN - attr_type_with_flags = self.atype + # ========================================= + # IFLA_INFO_DATA attributes for vxlan + # ========================================= + IFLA_VXLAN_UNSPEC = 0 + IFLA_VXLAN_ID = 1 + IFLA_VXLAN_GROUP = 2 + IFLA_VXLAN_LINK = 3 + IFLA_VXLAN_LOCAL = 4 + IFLA_VXLAN_TTL = 5 + IFLA_VXLAN_TOS = 6 + IFLA_VXLAN_LEARNING = 7 + IFLA_VXLAN_AGEING = 8 + IFLA_VXLAN_LIMIT = 9 + IFLA_VXLAN_PORT_RANGE = 10 + IFLA_VXLAN_PROXY = 11 + IFLA_VXLAN_RSC = 12 + IFLA_VXLAN_L2MISS = 13 + IFLA_VXLAN_L3MISS = 14 + IFLA_VXLAN_PORT = 15 + IFLA_VXLAN_GROUP6 = 16 + IFLA_VXLAN_LOCAL6 = 17 + IFLA_VXLAN_UDP_CSUM = 18 + IFLA_VXLAN_UDP_ZERO_CSUM6_TX = 19 + IFLA_VXLAN_UDP_ZERO_CSUM6_RX = 20 + IFLA_VXLAN_REMCSUM_TX = 21 + IFLA_VXLAN_REMCSUM_RX = 22 + IFLA_VXLAN_GBP = 23 + IFLA_VXLAN_REMCSUM_NOPARTIAL = 24 + IFLA_VXLAN_COLLECT_METADATA = 25 + IFLA_VXLAN_REPLICATION_NODE = 253 + IFLA_VXLAN_REPLICATION_TYPE = 254 - if self.nested: - attr_type_with_flags = attr_type_with_flags | NLA_F_NESTED + ifla_vxlan_to_string = { + IFLA_VXLAN_UNSPEC : 'IFLA_VXLAN_UNSPEC', + IFLA_VXLAN_ID : 'IFLA_VXLAN_ID', + IFLA_VXLAN_GROUP : 'IFLA_VXLAN_GROUP', + IFLA_VXLAN_LINK : 'IFLA_VXLAN_LINK', + IFLA_VXLAN_LOCAL : 'IFLA_VXLAN_LOCAL', + IFLA_VXLAN_TTL : 'IFLA_VXLAN_TTL', + IFLA_VXLAN_TOS : 'IFLA_VXLAN_TOS', + IFLA_VXLAN_LEARNING : 'IFLA_VXLAN_LEARNING', + IFLA_VXLAN_AGEING : 'IFLA_VXLAN_AGEING', + IFLA_VXLAN_LIMIT : 'IFLA_VXLAN_LIMIT', + IFLA_VXLAN_PORT_RANGE : 'IFLA_VXLAN_PORT_RANGE', + IFLA_VXLAN_PROXY : 'IFLA_VXLAN_PROXY', + IFLA_VXLAN_RSC : 'IFLA_VXLAN_RSC', + IFLA_VXLAN_L2MISS : 'IFLA_VXLAN_L2MISS', + IFLA_VXLAN_L3MISS : 'IFLA_VXLAN_L3MISS', + IFLA_VXLAN_PORT : 'IFLA_VXLAN_PORT', + IFLA_VXLAN_GROUP6 : 'IFLA_VXLAN_GROUP6', + IFLA_VXLAN_LOCAL6 : 'IFLA_VXLAN_LOCAL6', + IFLA_VXLAN_UDP_CSUM : 'IFLA_VXLAN_UDP_CSUM', + IFLA_VXLAN_UDP_ZERO_CSUM6_TX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_TX', + IFLA_VXLAN_UDP_ZERO_CSUM6_RX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_RX', + IFLA_VXLAN_REMCSUM_TX : 'IFLA_VXLAN_REMCSUM_TX', + IFLA_VXLAN_REMCSUM_RX : 'IFLA_VXLAN_REMCSUM_RX', + IFLA_VXLAN_GBP : 'IFLA_VXLAN_GBP', + IFLA_VXLAN_REMCSUM_NOPARTIAL : 'IFLA_VXLAN_REMCSUM_NOPARTIAL', + IFLA_VXLAN_COLLECT_METADATA : 'IFLA_VXLAN_COLLECT_METADATA', + IFLA_VXLAN_REPLICATION_NODE : 'IFLA_VXLAN_REPLICATION_NODE', + IFLA_VXLAN_REPLICATION_TYPE : 'IFLA_VXLAN_REPLICATION_TYPE' + } - if self.net_byteorder: - attr_type_with_flags = attr_type_with_flags | NLA_F_NET_BYTEORDER + # ========================================= + # IFLA_INFO_DATA attributes for bonds + # ========================================= + IFLA_BOND_UNSPEC = 0 + IFLA_BOND_MODE = 1 + IFLA_BOND_ACTIVE_SLAVE = 2 + IFLA_BOND_MIIMON = 3 + IFLA_BOND_UPDELAY = 4 + IFLA_BOND_DOWNDELAY = 5 + IFLA_BOND_USE_CARRIER = 6 + IFLA_BOND_ARP_INTERVAL = 7 + IFLA_BOND_ARP_IP_TARGET = 8 + IFLA_BOND_ARP_VALIDATE = 9 + IFLA_BOND_ARP_ALL_TARGETS = 10 + IFLA_BOND_PRIMARY = 11 + IFLA_BOND_PRIMARY_RESELECT = 12 + IFLA_BOND_FAIL_OVER_MAC = 13 + IFLA_BOND_XMIT_HASH_POLICY = 14 + IFLA_BOND_RESEND_IGMP = 15 + IFLA_BOND_NUM_PEER_NOTIF = 16 + IFLA_BOND_ALL_SLAVES_ACTIVE = 17 + IFLA_BOND_MIN_LINKS = 18 + IFLA_BOND_LP_INTERVAL = 19 + IFLA_BOND_PACKETS_PER_SLAVE = 20 + IFLA_BOND_AD_LACP_RATE = 21 + IFLA_BOND_AD_SELECT = 22 + IFLA_BOND_AD_INFO = 23 + IFLA_BOND_AD_ACTOR_SYS_PRIO = 24 + IFLA_BOND_AD_USER_PORT_KEY = 25 + IFLA_BOND_AD_ACTOR_SYSTEM = 26 + IFLA_BOND_CL_START = 60 + IFLA_BOND_AD_LACP_BYPASS = IFLA_BOND_CL_START - raw = pack(self.HEADER_PACK, length, attr_type_with_flags) + pack(self.PACK, self.value) - raw = self.pad(length, raw) - return raw - def decode_length_type(self, data): - """ - The first two bytes of an attribute are the length, the next two bytes are the type - """ - self.data = data - prev_atype = self.atype - (data1, data2) = unpack(self.HEADER_PACK, data[:self.HEADER_LEN]) - self.length = int(data1) - self.atype = int(data2) - self.attr_end = padded_length(self.length) + ifla_bond_to_string = { + IFLA_BOND_UNSPEC : 'IFLA_BOND_UNSPEC', + IFLA_BOND_MODE : 'IFLA_BOND_MODE', + IFLA_BOND_ACTIVE_SLAVE : 'IFLA_BOND_ACTIVE_SLAVE', + IFLA_BOND_MIIMON : 'IFLA_BOND_MIIMON', + IFLA_BOND_UPDELAY : 'IFLA_BOND_UPDELAY', + IFLA_BOND_DOWNDELAY : 'IFLA_BOND_DOWNDELAY', + IFLA_BOND_USE_CARRIER : 'IFLA_BOND_USE_CARRIER', + IFLA_BOND_ARP_INTERVAL : 'IFLA_BOND_ARP_INTERVAL', + IFLA_BOND_ARP_IP_TARGET : 'IFLA_BOND_ARP_IP_TARGET', + IFLA_BOND_ARP_VALIDATE : 'IFLA_BOND_ARP_VALIDATE', + IFLA_BOND_ARP_ALL_TARGETS : 'IFLA_BOND_ARP_ALL_TARGETS', + IFLA_BOND_PRIMARY : 'IFLA_BOND_PRIMARY', + IFLA_BOND_PRIMARY_RESELECT : 'IFLA_BOND_PRIMARY_RESELECT', + IFLA_BOND_FAIL_OVER_MAC : 'IFLA_BOND_FAIL_OVER_MAC', + IFLA_BOND_XMIT_HASH_POLICY : 'IFLA_BOND_XMIT_HASH_POLICY', + IFLA_BOND_RESEND_IGMP : 'IFLA_BOND_RESEND_IGMP', + IFLA_BOND_NUM_PEER_NOTIF : 'IFLA_BOND_NUM_PEER_NOTIF', + IFLA_BOND_ALL_SLAVES_ACTIVE : 'IFLA_BOND_ALL_SLAVES_ACTIVE', + IFLA_BOND_MIN_LINKS : 'IFLA_BOND_MIN_LINKS', + IFLA_BOND_LP_INTERVAL : 'IFLA_BOND_LP_INTERVAL', + IFLA_BOND_PACKETS_PER_SLAVE : 'IFLA_BOND_PACKETS_PER_SLAVE', + IFLA_BOND_AD_LACP_RATE : 'IFLA_BOND_AD_LACP_RATE', + IFLA_BOND_AD_SELECT : 'IFLA_BOND_AD_SELECT', + IFLA_BOND_AD_INFO : 'IFLA_BOND_AD_INFO', + IFLA_BOND_AD_ACTOR_SYS_PRIO : 'IFLA_BOND_AD_ACTOR_SYS_PRIO', + IFLA_BOND_AD_USER_PORT_KEY : 'IFLA_BOND_AD_USER_PORT_KEY', + IFLA_BOND_AD_ACTOR_SYSTEM : 'IFLA_BOND_AD_ACTOR_SYSTEM', + IFLA_BOND_CL_START : 'IFLA_BOND_CL_START', + IFLA_BOND_AD_LACP_BYPASS : 'IFLA_BOND_AD_LACP_BYPASS' + } - self.nested = True if self.atype & NLA_F_NESTED else False - self.net_byteorder = True if self.atype & NLA_F_NET_BYTEORDER else False - self.atype = self.atype & NLA_TYPE_MASK - - # Should never happen - assert self.atype == prev_atype, "This object changes attribute type from %d to %d, this is bad" % (prev_atype, self.atype) + IFLA_BOND_AD_INFO_UNSPEC = 0 + IFLA_BOND_AD_INFO_AGGREGATOR = 1 + IFLA_BOND_AD_INFO_NUM_PORTS = 2 + IFLA_BOND_AD_INFO_ACTOR_KEY = 3 + IFLA_BOND_AD_INFO_PARTNER_KEY = 4 + IFLA_BOND_AD_INFO_PARTNER_MAC = 5 - def dump_first_line(self, dump_buffer, line_number, color): - """ - Add the "Length....Type..." line to the dump buffer - """ - if self.attr_end == self.length: - padded_to = ', ' - else: - padded_to = ' padded to %d, ' % self.attr_end + ifla_bond_ad_to_string = { + IFLA_BOND_AD_INFO_UNSPEC : 'IFLA_BOND_AD_INFO_UNSPEC', + IFLA_BOND_AD_INFO_AGGREGATOR : 'IFLA_BOND_AD_INFO_AGGREGATOR', + IFLA_BOND_AD_INFO_NUM_PORTS : 'IFLA_BOND_AD_INFO_NUM_PORTS', + IFLA_BOND_AD_INFO_ACTOR_KEY : 'IFLA_BOND_AD_INFO_ACTOR_KEY', + IFLA_BOND_AD_INFO_PARTNER_KEY : 'IFLA_BOND_AD_INFO_PARTNER_KEY', + IFLA_BOND_AD_INFO_PARTNER_MAC : 'IFLA_BOND_AD_INFO_PARTNER_MAC' + } - extra = 'Length %s (%d)%sType %s%s%s (%d) %s' % \ - (zfilled_hex(self.length, 4), self.length, - padded_to, - zfilled_hex(self.atype, 4), - " (NLA_F_NESTED set)" if self.nested else "", - " (NLA_F_NET_BYTEORDER set)" if self.net_byteorder else "", - self.atype, - self) + ifla_bond_mode_tbl = { + 'balance-rr': 0, + 'active-backup': 1, + 'balance-xor': 2, + 'broadcast': 3, + '802.3ad': 4, + 'balance-tlb': 5, + 'balance-alb': 6, + '0': 0, + '1': 1, + '2': 2, + '3': 3, + '4': 4, + '5': 5, + '6': 6, + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6 + } - dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], extra)) - return line_number + 1 + ifla_bond_mode_pretty_tbl = { + 0: 'balance-rr', + 1: 'active-backup', + 2: 'balance-xor', + 3: 'broadcast', + 4: '802.3ad', + 5: 'balance-tlb', + 6: 'balance-alb' + } - def dump_lines(self, dump_buffer, line_number, color): - line_number = self.dump_first_line(dump_buffer, line_number, color) + ifla_bond_xmit_hash_policy_tbl = { + 'layer2': 0, + 'layer3+4': 1, + 'layer2+3': 2, + 'encap2+3': 3, + 'encap3+4': 4, + '0': 0, + '1': 1, + '2': 2, + '3': 3, + '4': 4, + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4 + } - for x in xrange(1, self.attr_end/4): - start = x * 4 - end = start + 4 - dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], '')) - line_number += 1 + ifla_bond_xmit_hash_policy_pretty_tbl = { + 0: 'layer2', + 1: 'layer3+4', + 2: 'layer2+3', + 3: 'encap2+3', + 4: 'encap3+4', + } - return line_number + # ========================================= + # IFLA_INFO_SLAVE_DATA attributes for bonds + # ========================================= + IFLA_BOND_SLAVE_UNSPEC = 0 + IFLA_BOND_SLAVE_STATE = 1 + IFLA_BOND_SLAVE_MII_STATUS = 2 + IFLA_BOND_SLAVE_LINK_FAILURE_COUNT = 3 + IFLA_BOND_SLAVE_PERM_HWADDR = 4 + IFLA_BOND_SLAVE_QUEUE_ID = 5 + IFLA_BOND_SLAVE_AD_AGGREGATOR_ID = 6 + IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE = 7 + IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE = 8 + IFLA_BOND_SLAVE_CL_START = 50 + IFLA_BOND_SLAVE_AD_RX_BYPASS = IFLA_BOND_SLAVE_CL_START - def get_pretty_value(self, obj=None): - if obj and callable(obj): - return obj(self.value) - return self.value + ifla_bond_slave_to_string = { + IFLA_BOND_SLAVE_UNSPEC : 'IFLA_BOND_SLAVE_UNSPEC', + IFLA_BOND_SLAVE_STATE : 'IFLA_BOND_SLAVE_STATE', + IFLA_BOND_SLAVE_MII_STATUS : 'IFLA_BOND_SLAVE_MII_STATUS', + IFLA_BOND_SLAVE_LINK_FAILURE_COUNT : 'IFLA_BOND_SLAVE_LINK_FAILURE_COUNT', + IFLA_BOND_SLAVE_PERM_HWADDR : 'IFLA_BOND_SLAVE_PERM_HWADDR', + IFLA_BOND_SLAVE_QUEUE_ID : 'IFLA_BOND_SLAVE_QUEUE_ID', + IFLA_BOND_SLAVE_AD_AGGREGATOR_ID : 'IFLA_BOND_SLAVE_AD_AGGREGATOR_ID', + IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE : 'IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE', + IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE : 'IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE', + IFLA_BOND_SLAVE_CL_START : 'IFLA_BOND_SLAVE_CL_START', + IFLA_BOND_SLAVE_AD_RX_BYPASS : 'IFLA_BOND_SLAVE_AD_RX_BYPASS' + } + # ========================================= + # IFLA_PROTINFO attributes for bridge ports + # ========================================= + IFLA_BRPORT_UNSPEC = 0 + IFLA_BRPORT_STATE = 1 + IFLA_BRPORT_PRIORITY = 2 + IFLA_BRPORT_COST = 3 + IFLA_BRPORT_MODE = 4 + IFLA_BRPORT_GUARD = 5 + IFLA_BRPORT_PROTECT = 6 + IFLA_BRPORT_FAST_LEAVE = 7 + IFLA_BRPORT_LEARNING = 8 + IFLA_BRPORT_UNICAST_FLOOD = 9 + IFLA_BRPORT_PROXYARP = 10 + IFLA_BRPORT_LEARNING_SYNC = 11 + IFLA_BRPORT_PROXYARP_WIFI = 12 + IFLA_BRPORT_ROOT_ID = 13 + IFLA_BRPORT_BRIDGE_ID = 14 + IFLA_BRPORT_DESIGNATED_PORT = 15 + IFLA_BRPORT_DESIGNATED_COST = 16 + IFLA_BRPORT_ID = 17 + IFLA_BRPORT_NO = 18 + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19 + IFLA_BRPORT_CONFIG_PENDING = 20 + IFLA_BRPORT_MESSAGE_AGE_TIMER = 21 + IFLA_BRPORT_FORWARD_DELAY_TIMER = 22 + IFLA_BRPORT_HOLD_TIMER = 23 + IFLA_BRPORT_FLUSH = 24 + IFLA_BRPORT_MULTICAST_ROUTER = 25 + IFLA_BRPORT_PAD = 26 + IFLA_BRPORT_MCAST_FLOOD = 27 + IFLA_BRPORT_MCAST_TO_UCAST = 28 + IFLA_BRPORT_VLAN_TUNNEL = 29 + IFLA_BRPORT_BCAST_FLOOD = 30 + IFLA_BRPORT_GROUP_FWD_MASK = 31 + IFLA_BRPORT_NEIGH_SUPPRESS = 32 + IFLA_BRPORT_ISOLATED = 33 + IFLA_BRPORT_BACKUP_PORT = 34 -class AttributeFourByteList(Attribute): + IFLA_BRPORT_PEER_LINK = 60 + IFLA_BRPORT_DUAL_LINK = 61 + IFLA_BRPORT_GROUP_FWD_MASKHI = 62 - def __init__(self, atype, string, family, logger): - Attribute.__init__(self, atype, string, logger) + ifla_brport_to_string = { + IFLA_BRPORT_UNSPEC : 'IFLA_BRPORT_UNSPEC', + IFLA_BRPORT_STATE : 'IFLA_BRPORT_STATE', + IFLA_BRPORT_PRIORITY : 'IFLA_BRPORT_PRIORITY', + IFLA_BRPORT_COST : 'IFLA_BRPORT_COST', + IFLA_BRPORT_MODE : 'IFLA_BRPORT_MODE', + IFLA_BRPORT_GUARD : 'IFLA_BRPORT_GUARD', + IFLA_BRPORT_PROTECT : 'IFLA_BRPORT_PROTECT', + IFLA_BRPORT_FAST_LEAVE : 'IFLA_BRPORT_FAST_LEAVE', + IFLA_BRPORT_LEARNING : 'IFLA_BRPORT_LEARNING', + IFLA_BRPORT_UNICAST_FLOOD : 'IFLA_BRPORT_UNICAST_FLOOD', + IFLA_BRPORT_PROXYARP : 'IFLA_BRPORT_PROXYARP', + IFLA_BRPORT_LEARNING_SYNC : 'IFLA_BRPORT_LEARNING_SYNC', + IFLA_BRPORT_PROXYARP_WIFI : 'IFLA_BRPORT_PROXYARP_WIFI', + IFLA_BRPORT_ROOT_ID : 'IFLA_BRPORT_ROOT_ID', + IFLA_BRPORT_BRIDGE_ID : 'IFLA_BRPORT_BRIDGE_ID', + IFLA_BRPORT_DESIGNATED_PORT : 'IFLA_BRPORT_DESIGNATED_PORT', + IFLA_BRPORT_DESIGNATED_COST : 'IFLA_BRPORT_DESIGNATED_COST', + IFLA_BRPORT_ID : 'IFLA_BRPORT_ID', + IFLA_BRPORT_NO : 'IFLA_BRPORT_NO', + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK : 'IFLA_BRPORT_TOPOLOGY_CHANGE_ACK', + IFLA_BRPORT_CONFIG_PENDING : 'IFLA_BRPORT_CONFIG_PENDING', + IFLA_BRPORT_MESSAGE_AGE_TIMER : 'IFLA_BRPORT_MESSAGE_AGE_TIMER', + IFLA_BRPORT_FORWARD_DELAY_TIMER : 'IFLA_BRPORT_FORWARD_DELAY_TIMER', + IFLA_BRPORT_HOLD_TIMER : 'IFLA_BRPORT_HOLD_TIMER', + IFLA_BRPORT_FLUSH : 'IFLA_BRPORT_FLUSH', + IFLA_BRPORT_MULTICAST_ROUTER : 'IFLA_BRPORT_MULTICAST_ROUTER', + IFLA_BRPORT_PAD : 'IFLA_BRPORT_PAD', + IFLA_BRPORT_MCAST_FLOOD : 'IFLA_BRPORT_MCAST_FLOOD', + IFLA_BRPORT_MCAST_TO_UCAST : 'IFLA_BRPORT_MCAST_TO_UCAST', + IFLA_BRPORT_VLAN_TUNNEL : 'IFLA_BRPORT_VLAN_TUNNEL', + IFLA_BRPORT_BCAST_FLOOD : 'IFLA_BRPORT_BCAST_FLOOD', + IFLA_BRPORT_GROUP_FWD_MASK : 'IFLA_BRPORT_GROUP_FWD_MASK', + IFLA_BRPORT_NEIGH_SUPPRESS : 'IFLA_BRPORT_NEIGH_SUPPRESS', + IFLA_BRPORT_ISOLATED : 'IFLA_BRPORT_ISOLATED', + IFLA_BRPORT_BACKUP_PORT : 'IFLA_BRPORT_BACKUP_PORT', + IFLA_BRPORT_PEER_LINK : 'IFLA_BRPORT_PEER_LINK', + IFLA_BRPORT_DUAL_LINK : 'IFLA_BRPORT_DUAL_LINK', + IFLA_BRPORT_GROUP_FWD_MASKHI : 'IFLA_BRPORT_GROUP_FWD_MASKHI' + } - def decode(self, parent_msg, data): - self.decode_length_type(data) - wordcount = (self.attr_end - 4)/4 - self.PACK = '=%dL' % wordcount - self.LEN = calcsize(self.PACK) + IFLA_BR_UNSPEC = 0 + IFLA_BR_FORWARD_DELAY = 1 + IFLA_BR_HELLO_TIME = 2 + IFLA_BR_MAX_AGE = 3 + IFLA_BR_AGEING_TIME = 4 + IFLA_BR_STP_STATE = 5 + IFLA_BR_PRIORITY = 6 + IFLA_BR_VLAN_FILTERING = 7 + IFLA_BR_VLAN_PROTOCOL = 8 + IFLA_BR_GROUP_FWD_MASK = 9 + IFLA_BR_ROOT_ID = 10 + IFLA_BR_BRIDGE_ID = 11 + IFLA_BR_ROOT_PORT = 12 + IFLA_BR_ROOT_PATH_COST = 13 + IFLA_BR_TOPOLOGY_CHANGE = 14 + IFLA_BR_TOPOLOGY_CHANGE_DETECTED = 15 + IFLA_BR_HELLO_TIMER = 16 + IFLA_BR_TCN_TIMER = 17 + IFLA_BR_TOPOLOGY_CHANGE_TIMER = 18 + IFLA_BR_GC_TIMER = 19 + IFLA_BR_GROUP_ADDR = 20 + IFLA_BR_FDB_FLUSH = 21 + IFLA_BR_MCAST_ROUTER = 22 + IFLA_BR_MCAST_SNOOPING = 23 + IFLA_BR_MCAST_QUERY_USE_IFADDR = 24 + IFLA_BR_MCAST_QUERIER = 25 + IFLA_BR_MCAST_HASH_ELASTICITY = 26 + IFLA_BR_MCAST_HASH_MAX = 27 + IFLA_BR_MCAST_LAST_MEMBER_CNT = 28 + IFLA_BR_MCAST_STARTUP_QUERY_CNT = 29 + IFLA_BR_MCAST_LAST_MEMBER_INTVL = 30 + IFLA_BR_MCAST_MEMBERSHIP_INTVL = 31 + IFLA_BR_MCAST_QUERIER_INTVL = 32 + IFLA_BR_MCAST_QUERY_INTVL = 33 + IFLA_BR_MCAST_QUERY_RESPONSE_INTVL = 34 + IFLA_BR_MCAST_STARTUP_QUERY_INTVL = 35 + IFLA_BR_NF_CALL_IPTABLES = 36 + IFLA_BR_NF_CALL_IP6TABLES = 37 + IFLA_BR_NF_CALL_ARPTABLES = 38 + IFLA_BR_VLAN_DEFAULT_PVID = 39 + IFLA_BR_PAD = 40 + IFLA_BR_VLAN_STATS_ENABLED = 41 + IFLA_BR_MCAST_STATS_ENABLED = 42 + IFLA_BR_MCAST_IGMP_VERSION = 43 + IFLA_BR_MCAST_MLD_VERSION = 44 - try: - self.value = unpack(self.PACK, self.data[4:]) - except struct.error: - self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:]))) - raise + ifla_br_to_string = { + IFLA_BR_UNSPEC : 'IFLA_BR_UNSPEC', + IFLA_BR_FORWARD_DELAY : 'IFLA_BR_FORWARD_DELAY', + IFLA_BR_HELLO_TIME : 'IFLA_BR_HELLO_TIME', + IFLA_BR_MAX_AGE : 'IFLA_BR_MAX_AGE', + IFLA_BR_AGEING_TIME : 'IFLA_BR_AGEING_TIME', + IFLA_BR_STP_STATE : 'IFLA_BR_STP_STATE', + IFLA_BR_PRIORITY : 'IFLA_BR_PRIORITY', + IFLA_BR_VLAN_FILTERING : 'IFLA_BR_VLAN_FILTERING', + IFLA_BR_VLAN_PROTOCOL : 'IFLA_BR_VLAN_PROTOCOL', + IFLA_BR_GROUP_FWD_MASK : 'IFLA_BR_GROUP_FWD_MASK', + IFLA_BR_ROOT_ID : 'IFLA_BR_ROOT_ID', + IFLA_BR_BRIDGE_ID : 'IFLA_BR_BRIDGE_ID', + IFLA_BR_ROOT_PORT : 'IFLA_BR_ROOT_PORT', + IFLA_BR_ROOT_PATH_COST : 'IFLA_BR_ROOT_PATH_COST', + IFLA_BR_TOPOLOGY_CHANGE : 'IFLA_BR_TOPOLOGY_CHANGE', + IFLA_BR_TOPOLOGY_CHANGE_DETECTED : 'IFLA_BR_TOPOLOGY_CHANGE_DETECTED', + IFLA_BR_HELLO_TIMER : 'IFLA_BR_HELLO_TIMER', + IFLA_BR_TCN_TIMER : 'IFLA_BR_TCN_TIMER', + IFLA_BR_TOPOLOGY_CHANGE_TIMER : 'IFLA_BR_TOPOLOGY_CHANGE_TIMER', + IFLA_BR_GC_TIMER : 'IFLA_BR_GC_TIMER', + IFLA_BR_GROUP_ADDR : 'IFLA_BR_GROUP_ADDR', + IFLA_BR_FDB_FLUSH : 'IFLA_BR_FDB_FLUSH', + IFLA_BR_MCAST_ROUTER : 'IFLA_BR_MCAST_ROUTER', + IFLA_BR_MCAST_SNOOPING : 'IFLA_BR_MCAST_SNOOPING', + IFLA_BR_MCAST_QUERY_USE_IFADDR : 'IFLA_BR_MCAST_QUERY_USE_IFADDR', + IFLA_BR_MCAST_QUERIER : 'IFLA_BR_MCAST_QUERIER', + IFLA_BR_MCAST_HASH_ELASTICITY : 'IFLA_BR_MCAST_HASH_ELASTICITY', + IFLA_BR_MCAST_HASH_MAX : 'IFLA_BR_MCAST_HASH_MAX', + IFLA_BR_MCAST_LAST_MEMBER_CNT : 'IFLA_BR_MCAST_LAST_MEMBER_CNT', + IFLA_BR_MCAST_STARTUP_QUERY_CNT : 'IFLA_BR_MCAST_STARTUP_QUERY_CNT', + IFLA_BR_MCAST_LAST_MEMBER_INTVL : 'IFLA_BR_MCAST_LAST_MEMBER_INTVL', + IFLA_BR_MCAST_MEMBERSHIP_INTVL : 'IFLA_BR_MCAST_MEMBERSHIP_INTVL', + IFLA_BR_MCAST_QUERIER_INTVL : 'IFLA_BR_MCAST_QUERIER_INTVL', + IFLA_BR_MCAST_QUERY_INTVL : 'IFLA_BR_MCAST_QUERY_INTVL', + IFLA_BR_MCAST_QUERY_RESPONSE_INTVL : 'IFLA_BR_MCAST_QUERY_RESPONSE_INTVL', + IFLA_BR_MCAST_STARTUP_QUERY_INTVL : 'IFLA_BR_MCAST_STARTUP_QUERY_INTVL', + IFLA_BR_NF_CALL_IPTABLES : 'IFLA_BR_NF_CALL_IPTABLES', + IFLA_BR_NF_CALL_IP6TABLES : 'IFLA_BR_NF_CALL_IP6TABLES', + IFLA_BR_NF_CALL_ARPTABLES : 'IFLA_BR_NF_CALL_ARPTABLES', + IFLA_BR_VLAN_DEFAULT_PVID : 'IFLA_BR_VLAN_DEFAULT_PVID', + IFLA_BR_PAD : 'IFLA_BR_PAD', + IFLA_BR_VLAN_STATS_ENABLED : 'IFLA_BR_VLAN_STATS_ENABLED', + IFLA_BR_MCAST_STATS_ENABLED : 'IFLA_BR_MCAST_STATS_ENABLED', + IFLA_BR_MCAST_IGMP_VERSION : 'IFLA_BR_MCAST_IGMP_VERSION', + IFLA_BR_MCAST_MLD_VERSION : 'IFLA_BR_MCAST_MLD_VERSION' + } - def dump_lines(self, dump_buffer, line_number, color): - line_number = self.dump_first_line(dump_buffer, line_number, color) - idx = 1 - for val in self.value: - dump_buffer.append(data_to_color_text(line_number, color, self.data[4*idx:4*(idx+1)], val)) - line_number += 1 - idx += 1 - return line_number + # ========================================= + # IFLA_INFO_DATA attributes for vrfs + # ========================================= + IFLA_VRF_UNSPEC = 0 + IFLA_VRF_TABLE = 1 + ifla_vrf_to_string = { + IFLA_VRF_UNSPEC : 'IFLA_VRF_UNSPEC', + IFLA_VRF_TABLE : 'IFLA_VRF_TABLE' + } -class AttributeFourByteValue(Attribute): + # ================================================================ + # IFLA_INFO_DATA attributes for (ip6)gre, (ip6)gretap, (ip6)erspan + # ================================================================ + IFLA_GRE_UNSPEC = 0 + IFLA_GRE_LINK = 1 + IFLA_GRE_IFLAGS = 2 + IFLA_GRE_OFLAGS = 3 + IFLA_GRE_IKEY = 4 + IFLA_GRE_OKEY = 5 + IFLA_GRE_LOCAL = 6 + IFLA_GRE_REMOTE = 7 + IFLA_GRE_TTL = 8 + IFLA_GRE_TOS = 9 + IFLA_GRE_PMTUDISC = 10 + IFLA_GRE_ENCAP_LIMIT = 11 + IFLA_GRE_FLOWINFO = 12 + IFLA_GRE_FLAGS = 13 + IFLA_GRE_ENCAP_TYPE = 14 + IFLA_GRE_ENCAP_FLAGS = 15 + IFLA_GRE_ENCAP_SPORT = 16 + IFLA_GRE_ENCAP_DPORT = 17 + IFLA_GRE_COLLECT_METADATA = 18 + IFLA_GRE_IGNORE_DF = 19 + IFLA_GRE_FWMARK = 20 + IFLA_GRE_ERSPAN_INDEX = 21 + IFLA_GRE_ERSPAN_VER = 22 + IFLA_GRE_ERSPAN_DIR = 23 + IFLA_GRE_ERSPAN_HWID = 24 - def __init__(self, atype, string, family, logger): - Attribute.__init__(self, atype, string, logger) - self.PACK = '=L' - self.LEN = calcsize(self.PACK) + ifla_gre_to_string = { + IFLA_GRE_UNSPEC : "IFLA_GRE_UNSPEC", + IFLA_GRE_LINK : "IFLA_GRE_LINK", + IFLA_GRE_IFLAGS : "IFLA_GRE_IFLAGS", + IFLA_GRE_OFLAGS : "IFLA_GRE_OFLAGS", + IFLA_GRE_IKEY : "IFLA_GRE_IKEY", + IFLA_GRE_OKEY : "IFLA_GRE_OKEY", + IFLA_GRE_LOCAL : "IFLA_GRE_LOCAL", + IFLA_GRE_REMOTE : "IFLA_GRE_REMOTE", + IFLA_GRE_TTL : "IFLA_GRE_TTL", + IFLA_GRE_TOS : "IFLA_GRE_TOS", + IFLA_GRE_PMTUDISC : "IFLA_GRE_PMTUDISC", + IFLA_GRE_ENCAP_LIMIT : "IFLA_GRE_ENCAP_LIMIT", + IFLA_GRE_FLOWINFO : "IFLA_GRE_FLOWINFO", + IFLA_GRE_FLAGS : "IFLA_GRE_FLAGS", + IFLA_GRE_ENCAP_TYPE : "IFLA_GRE_ENCAP_TYPE", + IFLA_GRE_ENCAP_FLAGS : "IFLA_GRE_ENCAP_FLAGS", + IFLA_GRE_ENCAP_SPORT : "IFLA_GRE_ENCAP_SPORT", + IFLA_GRE_ENCAP_DPORT : "IFLA_GRE_ENCAP_DPORT", + IFLA_GRE_COLLECT_METADATA : "IFLA_GRE_COLLECT_METADATA", + IFLA_GRE_IGNORE_DF : "IFLA_GRE_IGNORE_DF", + IFLA_GRE_FWMARK : "IFLA_GRE_FWMARK", + IFLA_GRE_ERSPAN_INDEX : "IFLA_GRE_ERSPAN_INDEX", + IFLA_GRE_ERSPAN_VER : "IFLA_GRE_ERSPAN_VER", + IFLA_GRE_ERSPAN_DIR : "IFLA_GRE_ERSPAN_DIR", + IFLA_GRE_ERSPAN_HWID : "IFLA_GRE_ERSPAN_HWID", + } - def decode(self, parent_msg, data): - self.decode_length_type(data) - assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end) + # =============================================== + # IFLA_INFO_DATA attributes for ipip, sit, ip6tnl + # =============================================== + IFLA_IPTUN_UNSPEC = 0 + IFLA_IPTUN_LINK = 1 + IFLA_IPTUN_LOCAL = 2 + IFLA_IPTUN_REMOTE = 3 + IFLA_IPTUN_TTL = 4 + IFLA_IPTUN_TOS = 5 + IFLA_IPTUN_ENCAP_LIMIT = 6 + IFLA_IPTUN_FLOWINFO = 7 + IFLA_IPTUN_FLAGS = 8 + IFLA_IPTUN_PROTO = 9 + IFLA_IPTUN_PMTUDISC = 10 + IFLA_IPTUN_6RD_PREFIX = 11 + IFLA_IPTUN_6RD_RELAY_PREFIX = 12 + IFLA_IPTUN_6RD_PREFIXLEN = 13 + IFLA_IPTUN_6RD_RELAY_PREFIXLEN = 14 + IFLA_IPTUN_ENCAP_TYPE = 15 + IFLA_IPTUN_ENCAP_FLAGS = 16 + IFLA_IPTUN_ENCAP_SPORT = 17 + IFLA_IPTUN_ENCAP_DPORT = 18 + IFLA_IPTUN_COLLECT_METADATA = 19 + IFLA_IPTUN_FWMARK = 20 - try: - self.value = int(unpack(self.PACK, self.data[4:])[0]) - except struct.error: - self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:]))) - raise + ifla_iptun_to_string = { + IFLA_IPTUN_UNSPEC : "IFLA_IPTUN_UNSPEC", + IFLA_IPTUN_LINK : "IFLA_IPTUN_LINK", + IFLA_IPTUN_LOCAL : "IFLA_IPTUN_LOCAL", + IFLA_IPTUN_REMOTE : "IFLA_IPTUN_REMOTE", + IFLA_IPTUN_TTL : "IFLA_IPTUN_TTL", + IFLA_IPTUN_TOS : "IFLA_IPTUN_TOS", + IFLA_IPTUN_ENCAP_LIMIT : "IFLA_IPTUN_ENCAP_LIMIT", + IFLA_IPTUN_FLOWINFO : "IFLA_IPTUN_FLOWINFO", + IFLA_IPTUN_FLAGS : "IFLA_IPTUN_FLAGS", + IFLA_IPTUN_PROTO : "IFLA_IPTUN_PROTO", + IFLA_IPTUN_PMTUDISC : "IFLA_IPTUN_PMTUDISC", + IFLA_IPTUN_6RD_PREFIX : "IFLA_IPTUN_6RD_PREFIX", + IFLA_IPTUN_6RD_RELAY_PREFIX : "IFLA_IPTUN_6RD_RELAY_PREFIX", + IFLA_IPTUN_6RD_PREFIXLEN : "IFLA_IPTUN_6RD_PREFIXLEN", + IFLA_IPTUN_6RD_RELAY_PREFIXLEN : "IFLA_IPTUN_6RD_RELAY_PREFIXLEN", + IFLA_IPTUN_ENCAP_TYPE : "IFLA_IPTUN_ENCAP_TYPE", + IFLA_IPTUN_ENCAP_FLAGS : "IFLA_IPTUN_ENCAP_FLAGS", + IFLA_IPTUN_ENCAP_SPORT : "IFLA_IPTUN_ENCAP_SPORT", + IFLA_IPTUN_ENCAP_DPORT : "IFLA_IPTUN_ENCAP_DPORT", + IFLA_IPTUN_COLLECT_METADATA : "IFLA_IPTUN_COLLECT_METADATA", + IFLA_IPTUN_FWMARK : "IFLA_IPTUN_FWMARK", + } - def dump_lines(self, dump_buffer, line_number, color): - line_number = self.dump_first_line(dump_buffer, line_number, color) - dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value)) - return line_number + 1 + # ========================================= + # IFLA_INFO_DATA attributes for vti, vti6 + # ========================================= + IFLA_VTI_UNSPEC = 0 + IFLA_VTI_LINK = 1 + IFLA_VTI_IKEY = 2 + IFLA_VTI_OKEY = 3 + IFLA_VTI_LOCAL = 4 + IFLA_VTI_REMOTE = 5 + IFLA_VTI_FWMARK = 6 + ifla_vti_to_string = { + IFLA_VTI_UNSPEC : "IFLA_VTI_UNSPEC", + IFLA_VTI_LINK : "IFLA_VTI_LINK", + IFLA_VTI_IKEY : "IFLA_VTI_IKEY", + IFLA_VTI_OKEY : "IFLA_VTI_OKEY", + IFLA_VTI_LOCAL : "IFLA_VTI_LOCAL", + IFLA_VTI_REMOTE : "IFLA_VTI_REMOTE", + IFLA_VTI_FWMARK : "IFLA_VTI_FWMARK", + } -class AttributeTwoByteValue(Attribute): - def __init__(self, atype, string, family, logger): - Attribute.__init__(self, atype, string, logger) - self.PACK = '=Hxx' - self.LEN = calcsize(self.PACK) +class Attribute(object): - def decode(self, parent_msg, data): - self.decode_length_type(data) - assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end) + def __init__(self, atype, string, logger): + self.atype = atype + self.string = string + self.HEADER_PACK = '=HH' + self.HEADER_LEN = calcsize(self.HEADER_PACK) + self.PACK = None + self.LEN = None + self.raw = None # raw value (i.e. int for mac address) + self.value = None + self.nested = False + self.net_byteorder = False + self.log = logger - try: - self.value = int(unpack(self.PACK, self.data[4:8])[0]) - except struct.error: - self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:6]))) - raise + def __str__(self): + return self.string - def encode(self): - length = self.HEADER_LEN + self.LEN - raw = pack(self.HEADER_PACK, length-2, self.atype) + pack(self.PACK, self.value) - raw = self.pad(length, raw) - return raw + def set_value(self, value): + self.value = value - def dump_lines(self, dump_buffer, line_number, color): - line_number = self.dump_first_line(dump_buffer, line_number, color) - dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value)) - return line_number + 1 + def set_nested(self, nested): + self.nested = nested + def set_net_byteorder(self, net_byteorder): + self.net_byteorder = net_byteorder -class AttributeString(Attribute): + @staticmethod + def pad_bytes_needed(length): + """ + Return the number of bytes that should be added to align on a 4-byte boundry + """ + remainder = length % 4 - def __init__(self, atype, string, family, logger): - Attribute.__init__(self, atype, string, logger) - self.PACK = None - self.LEN = None + if remainder: + return 4 - remainder + + return 0 + + def pad(self, length, raw): + pad = self.pad_bytes_needed(length) + + if pad: + raw += '\0' * pad + + return raw + + def encode(self): + + if not self.LEN: + raise Exception('Please define an encode() method in your child attribute class, or do not use AttributeGeneric') + + length = self.HEADER_LEN + self.LEN + attr_type_with_flags = self.atype + + if self.nested: + attr_type_with_flags = attr_type_with_flags | NLA_F_NESTED + + if self.net_byteorder: + attr_type_with_flags = attr_type_with_flags | NLA_F_NET_BYTEORDER + + raw = pack(self.HEADER_PACK, length, attr_type_with_flags) + pack(self.PACK, self.value) + raw = self.pad(length, raw) + return raw + + def decode_length_type(self, data): + """ + The first two bytes of an attribute are the length, the next two bytes are the type + """ + self.data = data + prev_atype = self.atype + (data1, data2) = unpack(self.HEADER_PACK, data[:self.HEADER_LEN]) + self.length = int(data1) + self.atype = int(data2) + self.attr_end = padded_length(self.length) + + self.nested = True if self.atype & NLA_F_NESTED else False + self.net_byteorder = True if self.atype & NLA_F_NET_BYTEORDER else False + self.atype = self.atype & NLA_TYPE_MASK + + # Should never happen + assert self.atype == prev_atype, "This object changes attribute type from %d to %d, this is bad" % (prev_atype, self.atype) + + def dump_first_line(self, dump_buffer, line_number, color): + """ + Add the "Length....Type..." line to the dump buffer + """ + if self.attr_end == self.length: + padded_to = ', ' + else: + padded_to = ' padded to %d, ' % self.attr_end + + extra = 'Length %s (%d)%sType %s%s%s (%d) %s' % \ + (zfilled_hex(self.length, 4), self.length, + padded_to, + zfilled_hex(self.atype, 4), + " (NLA_F_NESTED set)" if self.nested else "", + " (NLA_F_NET_BYTEORDER set)" if self.net_byteorder else "", + self.atype, + self) + + dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], extra)) + return line_number + 1 + + def dump_lines(self, dump_buffer, line_number, color): + line_number = self.dump_first_line(dump_buffer, line_number, color) + + for x in xrange(1, self.attr_end/4): + start = x * 4 + end = start + 4 + dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], '')) + line_number += 1 + + return line_number + + def get_pretty_value(self, obj=None): + if obj and callable(obj): + return obj(self.value) + return self.value + + @staticmethod + def decode_one_byte_attribute(data, _=None): + return unpack("=B", data[4])[0] + + @staticmethod + def decode_two_bytes_attribute(data, _=None): + return unpack("=H", data[4:6])[0] + + @staticmethod + def decode_two_bytes_network_byte_order_attribute(data, _=None): + # The form '!' is available for those poor souls who claim they can't + # remember whether network byte order is big-endian or little-endian. + return unpack("!H", data[4:6])[0] + + @staticmethod + def decode_four_bytes_attribute(data, _=None): + return unpack("=L", data[4:8])[0] + + @staticmethod + def decode_eight_bytes_attribute(data, _=None): + return unpack("=Q", data[4:12])[0] + + @staticmethod + def decode_mac_address_attribute(data, _=None): + (data1, data2) = unpack(">LHxx", data[4:12]) + return mac_int_to_str(data1 << 16 | data2) + + @staticmethod + def decode_ipv4_address_attribute(data, _=None): + return IPv4Address(unpack(">L", data[4:8])[0]) + + @staticmethod + def decode_ipv6_address_attribute(data, _=None): + (data1, data2) = unpack(">QQ", data[4:20]) + return IPv6Address(data1 << 64 | data2) + + @staticmethod + def decode_bond_ad_info_attribute(data, info_data_end): + ifla_bond_ad_info = {} + ad_attr_data = data[4:info_data_end] + + while ad_attr_data: + (ad_data_length, ad_data_type) = unpack("=HH", ad_attr_data[:4]) + ad_data_end = padded_length(ad_data_length) + + if ad_data_type == Link.IFLA_BOND_AD_INFO_PARTNER_MAC: + (data1, data2) = unpack(">LHxx", ad_attr_data[4:12]) + ifla_bond_ad_info[ad_data_type] = mac_int_to_str(data1 << 16 | data2) + + ad_attr_data = ad_attr_data[ad_data_end:] + + return ifla_bond_ad_info + + @staticmethod + def decode_vlan_protocol_attribute(data, _=None): + return Link.ifla_vlan_protocol_dict.get(int("0x%s" % data[4:6].encode("hex"), base=16)) + + ############################################################################ + # encode methods + ############################################################################ + + @staticmethod + def encode_one_byte_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + sub_attr_pack_layout.append("HH") + sub_attr_payload.append(5) # length + sub_attr_payload.append(info_data_type) + + sub_attr_pack_layout.append("B") + sub_attr_payload.append(info_data_value) + + # pad 3 bytes + sub_attr_pack_layout.extend("xxx") + + @staticmethod + def encode_one_byte_attribute_from_string_boolean(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + return Attribute.encode_one_byte_attribute( + sub_attr_pack_layout, sub_attr_payload, info_data_type, value_to_bool_dict.get(info_data_value) + ) + + @staticmethod + def encode_bond_xmit_hash_policy_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + return Attribute.encode_one_byte_attribute( + sub_attr_pack_layout, + sub_attr_payload, + info_data_type, + Link.ifla_bond_xmit_hash_policy_tbl.get(info_data_value, 0), + ) + + @staticmethod + def encode_bond_mode_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + return Attribute.encode_one_byte_attribute( + sub_attr_pack_layout, + sub_attr_payload, + info_data_type, + Link.ifla_bond_mode_tbl.get(info_data_value, 0), + ) + + @staticmethod + def encode_two_bytes_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + sub_attr_pack_layout.append("HH") + sub_attr_payload.append(6) # length + sub_attr_payload.append(info_data_type) + + sub_attr_pack_layout.append("H") + sub_attr_payload.append(info_data_value) + + # pad 2 bytes + sub_attr_pack_layout.extend("xx") + + @staticmethod + def encode_four_bytes_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + sub_attr_pack_layout.append("HH") + sub_attr_payload.append(8) # length + sub_attr_payload.append(info_data_type) + + sub_attr_pack_layout.append("L") + sub_attr_payload.append(info_data_value) + + @staticmethod + def encode_eight_bytes_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + sub_attr_pack_layout.append("HH") + sub_attr_payload.append(12) # length + sub_attr_payload.append(info_data_type) + + sub_attr_pack_layout.append("Q") + sub_attr_payload.append(info_data_value) + + @staticmethod + def encode_ipv4_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value): + sub_attr_pack_layout.append("HH") + sub_attr_payload.append(8) # length + sub_attr_payload.append(info_data_type) + + sub_attr_pack_layout.append("L") + + if info_data_value: + reorder = unpack("H", info_data_value) + sub_attr_payload.append(unpack("L', self.data[4:])[0]) self.value_int = int(self.value) self.value_int_str = str(self.value_int) - # MAC Address + # MAC Address elif self.length == 10: (data1, data2) = unpack(self.PACK, self.data[4:]) - self.value = mac_int_to_str(data1 << 16 | data2) - # GREv6 interface uses a 16-byte IP address for this attribute + self.raw = data1 << 16 | data2 + self.value = mac_int_to_str(self.raw) + # GREv6 interface uses a 16-byte IP address for this attribute elif self.length == 20: self.value = IPv6Address(unpack('>L', self.data[16:])[0]) self.value_int = int(self.value) @@ -1094,9 +2004,777 @@ class AttributeIFLA_LINKINFO(Attribute): } } """ + decode_ifla_info_nested_data_handlers = { + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_DATA: { + "bridge": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_FILTERING: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE_DETECTED: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_ROUTER: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_SNOOPING: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_USE_IFADDR: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IPTABLES: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IP6TABLES: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_ARPTABLES: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_STATS_ENABLED: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STATS_ENABLED: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_IGMP_VERSION: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MLD_VERSION: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_PRIORITY: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_GROUP_FWD_MASK: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_DEFAULT_PVID: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_AGEING_TIME: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_FORWARD_DELAY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_HELLO_TIME: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MAX_AGE: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_STP_STATE: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PATH_COST: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_ELASTICITY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_MAX: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_CNT: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_CNT: Attribute.decode_four_bytes_attribute, + + # 8 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MEMBERSHIP_INTVL: Attribute.decode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER_INTVL: Attribute.decode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_INTVL: Attribute.decode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_INTVL: Attribute.decode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: Attribute.decode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: Attribute.decode_eight_bytes_attribute, + + # vlan-protocol attribute ###################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_PROTOCOL: Attribute.decode_vlan_protocol_attribute + }, + "bond": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MODE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_USE_CARRIER: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_RATE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_BYPASS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_XMIT_HASH_POLICY: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_NUM_PEER_NOTIF: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYS_PRIO: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIIMON: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_UPDELAY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_DOWNDELAY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIN_LINKS: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_PRIMARY: Attribute.decode_four_bytes_attribute, + + # mac address attributes ####################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYSTEM: Attribute.decode_mac_address_attribute, + + # bond ad info attribute ####################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_INFO: Attribute.decode_bond_ad_info_attribute, + }, + "vlan": { + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_ID: Attribute.decode_two_bytes_attribute, + + # vlan-protocol attribute ###################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_PROTOCOL: Attribute.decode_vlan_protocol_attribute + }, + "macvlan": { + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_MACVLAN_MODE: Attribute.decode_four_bytes_attribute, + }, + "vxlan": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LEARNING: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PROXY: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_RSC: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L2MISS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L3MISS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_CSUM: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_TX: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_RX: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_TX: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_RX: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REPLICATION_TYPE: Attribute.decode_one_byte_attribute, + + # 2 bytes network byte order attributes ######################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT: Attribute.decode_two_bytes_network_byte_order_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_ID: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_AGEING: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LIMIT: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT_RANGE: Attribute.decode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_GROUP: Attribute.decode_ipv4_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LOCAL: Attribute.decode_ipv4_address_attribute, + }, + "vrf": { + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VRF_TABLE: Attribute.decode_four_bytes_attribute + }, + "xfrm": { + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_XFRM_IF_ID: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_XFRM_LINK: Attribute.decode_four_bytes_attribute + }, + + # Tunnels: + # There's is a lot of copy paste here because most of the tunnels + # share the same attribute key / attribute index value, but ipv6 + # tunnels needs special handling for their ipv6 attributes. + # + # "gre", "gretap", "erspan", "ip6gre", "ip6gretap", "ip6erspan" are + # identical as well with special handling for LOCAL and REMOTE for + # the ipv6 tunnels + "gre": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv4_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv4_address_attribute + }, + "gretap": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv4_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv4_address_attribute + }, + "erspan": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv4_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv4_address_attribute + }, + "ip6gre": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv6_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv6_address_attribute + }, + "ip6gretap": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv6_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv6_address_attribute + }, + "ip6erspan": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.decode_ipv6_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.decode_ipv6_address_attribute + }, + + # "ipip", "sit", "ip6tnl" are identical as well except for some + # special ipv6 handling for LOCAL and REMOTE. + "ipip": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LINK: Attribute.decode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LOCAL: Attribute.decode_ipv4_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_REMOTE: Attribute.decode_ipv4_address_attribute, + }, + "sit": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LINK: Attribute.decode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LOCAL: Attribute.decode_ipv4_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_REMOTE: Attribute.decode_ipv4_address_attribute, + }, + "ip6tnl": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TTL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_TOS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_PMTUDISC: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_TYPE: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_SPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_DPORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_ENCAP_FLAGS: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LINK: Attribute.decode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_LOCAL: Attribute.decode_ipv6_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_IPTUN_REMOTE: Attribute.decode_ipv6_address_attribute, + }, + + # Same story with "vti", "vti6"... + "vti": { + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LOCAL: Attribute.decode_ipv4_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_REMOTE: Attribute.decode_ipv4_address_attribute + }, + "vti6": { + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LINK: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_IKEY: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_OKEY: Attribute.decode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_LOCAL: Attribute.decode_ipv6_address_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VTI_REMOTE: Attribute.decode_ipv6_address_attribute + } + }, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_SLAVE_DATA: { + "bridge": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_STATE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MODE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GUARD: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROTECT: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_FAST_LEAVE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_UNICAST_FLOOD: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING_SYNC: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP_WIFI: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_CONFIG_PENDING: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MULTICAST_ROUTER: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_FLOOD: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BCAST_FLOOD: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_TO_UCAST: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_VLAN_TUNNEL: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PEER_LINK: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DUAL_LINK: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NEIGH_SUPPRESS: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PRIORITY: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_PORT: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_COST: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_ID: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NO: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASK: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASKHI: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_COST: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BACKUP_PORT: Attribute.decode_four_bytes_attribute, + }, + "bond": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_STATE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_MII_STATUS: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE: Attribute.decode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_AD_RX_BYPASS: Attribute.decode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_QUEUE_ID: Attribute.decode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID: Attribute.decode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_PERM_HWADDR: Attribute.decode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT: Attribute.decode_four_bytes_attribute, + } + } + } + + encode_ifla_info_nested_data_handlers = { + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_DATA: { + "vlan": { + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_ID: Attribute.encode_two_bytes_attribute, + + # vlan-protocol attribute ###################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VLAN_PROTOCOL: Attribute.encode_vlan_protocol_attribute, + }, + "macvlan": { + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_MACVLAN_MODE: Attribute.encode_four_bytes_attribute, + }, + "vxlan": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TTL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_TOS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LEARNING: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PROXY: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_RSC: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L2MISS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_L3MISS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_CSUM: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_TX: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_UDP_ZERO_CSUM6_RX: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_TX: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_RX: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REPLICATION_TYPE: Attribute.encode_one_byte_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_ID: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LINK: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_AGEING: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LIMIT: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT_RANGE: Attribute.encode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_GROUP: Attribute.encode_ipv4_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LOCAL: Attribute.encode_ipv4_attribute, + + # vxlan-port attribute ######################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT: Attribute.encode_vxlan_port_attribute, + }, + "bond": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_NUM_PEER_NOTIF: Attribute.encode_one_byte_attribute, + + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_USE_CARRIER: Attribute.encode_one_byte_attribute_from_string_boolean, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_BYPASS: Attribute.encode_one_byte_attribute_from_string_boolean, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_LACP_RATE: Attribute.encode_one_byte_attribute_from_string_boolean, + + # bond-mode attribute ########################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MODE: Attribute.encode_bond_mode_attribute, + + # bond-xmit-hash-policy attribute ############################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_XMIT_HASH_POLICY: Attribute.encode_bond_xmit_hash_policy_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYS_PRIO: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIIMON: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_UPDELAY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_DOWNDELAY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_MIN_LINKS: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_PRIMARY: Attribute.encode_four_bytes_attribute, + + # mac address attribute ######################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BOND_AD_ACTOR_SYSTEM: Attribute.encode_mac_address_attribute + }, + "vrf": { + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VRF_TABLE: Attribute.encode_four_bytes_attribute + }, + "bridge": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_FILTERING: Attribute.encode_one_byte_attribute_from_string_boolean, + + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_TOPOLOGY_CHANGE_DETECTED: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_ROUTER: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_SNOOPING: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_USE_IFADDR: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IPTABLES: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_IP6TABLES: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_NF_CALL_ARPTABLES: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_STATS_ENABLED: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STATS_ENABLED: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_IGMP_VERSION: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MLD_VERSION: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_PRIORITY: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_GROUP_FWD_MASK: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_DEFAULT_PVID: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_FORWARD_DELAY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_HELLO_TIME: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MAX_AGE: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_AGEING_TIME: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_STP_STATE: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_ROOT_PATH_COST: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_ELASTICITY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_HASH_MAX: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_CNT: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_CNT: Attribute.encode_four_bytes_attribute, + + # 8 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_LAST_MEMBER_INTVL: Attribute.encode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_MEMBERSHIP_INTVL: Attribute.encode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERIER_INTVL: Attribute.encode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_INTVL: Attribute.encode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: Attribute.encode_eight_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: Attribute.encode_eight_bytes_attribute, + + # vlan-protocol attribute ###################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BR_VLAN_PROTOCOL: Attribute.encode_vlan_protocol_attribute + }, + # Tunnels: + # There's is a lot of copy paste here because most of the tunnels + # share the same attribute key / attribute index value, but ipv6 + # tunnels needs special handling for their ipv6 attributes. + # + # "gre", "gretap", "erspan", "ip6gre", "ip6gretap", "ip6erspan" are + # identical as well with special handling for LOCAL and REMOTE for + # the ipv6 tunnels + "gre": { # == ("gre", "gretap", "erspan") + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv4_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv4_attribute, + }, + "gretap": { # == ("gre", "gretap", "erspan") + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv4_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv4_attribute, + }, + "erspan": { # == ("gre", "gretap", "erspan") + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute, + + # ipv4 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv4_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv4_attribute, + }, + "ip6gre": { # == ("ip6gre", "ip6gretap", "ip6erspan") + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv6_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv6_attribute, + }, + "ip6gretap": { # == ("ip6gre", "ip6gretap", "ip6erspan") + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv6_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv6_attribute, + }, + "ip6erspan": { # == ("ip6gre", "ip6gretap", "ip6erspan") + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TTL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_TOS: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_PMTUDISC: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OFLAGS: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_TYPE: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_SPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_DPORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_ENCAP_FLAGS: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LINK: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_IKEY: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_OKEY: Attribute.encode_four_bytes_attribute, + + # ipv6 attributes ############################################## + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_LOCAL: Attribute.encode_ipv6_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_GRE_REMOTE: Attribute.encode_ipv6_attribute, + } + }, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_SLAVE_DATA: { + "bridge": { + # 1 byte attributes ############################################ + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_STATE: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MODE: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GUARD: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROTECT: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_FAST_LEAVE: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_UNICAST_FLOOD: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_LEARNING_SYNC: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PROXYARP_WIFI: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_CONFIG_PENDING: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MULTICAST_ROUTER: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_FLOOD: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_MCAST_TO_UCAST: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_VLAN_TUNNEL: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BCAST_FLOOD: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PEER_LINK: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DUAL_LINK: Attribute.encode_one_byte_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NEIGH_SUPPRESS: Attribute.encode_one_byte_attribute, + + # 2 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_PRIORITY: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_PORT: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_DESIGNATED_COST: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_ID: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_NO: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASK: Attribute.encode_two_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_GROUP_FWD_MASKHI: Attribute.encode_two_bytes_attribute, + + # 4 bytes attributes ########################################### + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_COST: Attribute.encode_four_bytes_attribute, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_BRPORT_BACKUP_PORT: Attribute.encode_four_bytes_attribute, + }, + } + } + + ifla_info_nested_data_attributes_to_string_dict = { + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_DATA: { + "bond": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_bond_to_string, + "vlan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vlan_to_string, + "vxlan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vxlan_to_string, + "bridge": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_br_to_string, + "macvlan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_macvlan_to_string, + "vrf": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vrf_to_string, + "gre": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string, + "gretap": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string, + "erspan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string, + "ip6gre": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string, + "ip6gretap": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string, + "ip6erspan": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_gre_to_string, + "vti": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vti_to_string, + "vti6": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_vti_to_string, + "ipip": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_iptun_to_string, + "sit": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_iptun_to_string, + "ip6tnl": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_iptun_to_string + }, + NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_INFO_SLAVE_DATA: { + "bridge": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_brport_to_string, + "bond": NetlinkPacket_IFLA_LINKINFO_Attributes.ifla_bond_slave_to_string + } + } + def __init__(self, atype, string, family, logger): Attribute.__init__(self, atype, string, logger) + def encode_ifla_info_nested_data(self, sub_attr_pack_layout, sub_attr_type, sub_attr_type_string, sub_attr_value, encode_handlers, ifla_info_nested_attr_to_str_dict, kind): + sub_attr_payload = [0, sub_attr_type | NLA_F_NESTED] + + if not encode_handlers: + self.log.log( + SYSLOG_EXTRA_DEBUG, + "Add support for encoding %s for %s link kind" % (sub_attr_type_string, kind) + ) + else: + for (info_data_type, info_data_value) in sub_attr_value.iteritems(): + encode_handler = encode_handlers.get(info_data_type) + + if encode_handler: + encode_handler(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value) + else: + self.log.log( + SYSLOG_EXTRA_DEBUG, + "Add support for encoding %s %s sub-attribute %s (%d)" + % ( + sub_attr_type_string, + kind, + ifla_info_nested_attr_to_str_dict.get(info_data_type), + info_data_type + ) + ) + + return sub_attr_payload + def encode(self): pack_layout = [self.HEADER_PACK] payload = [0, self.atype | NLA_F_NESTED] @@ -1105,8 +2783,18 @@ def encode(self): kind = self.value.get(Link.IFLA_INFO_KIND) slave_kind = self.value.get(Link.IFLA_INFO_SLAVE_KIND) - if not slave_kind and kind not in ('vlan', 'macvlan', 'vxlan', 'bond', 'bridge', 'xfrm'): - raise Exception('Unsupported IFLA_INFO_KIND %s' % kind) + if not slave_kind and kind not in ( + "vrf", + "vlan", + "vxlan", + "bond", + "dummy", + "bridge", + "macvlan", + ): + self.log.debug('Unsupported IFLA_INFO_KIND %s' % kind) + return + elif not kind and slave_kind != 'bridge': # only support brport for now. raise Exception('Unsupported IFLA_INFO_SLAVE_KIND %s' % slave_kind) @@ -1115,436 +2803,69 @@ def encode(self): # order (=). If a field is added that needs to be packed via network # order (>) then some smarts will need to be added to split the pack_layout # string at the >, split the payload and make the needed pack() calls. - # # Until we cross that bridge though we will keep things nice and simple and # pack everything via a single pack() call. + for (sub_attr_type, sub_attr_value) in self.value.iteritems(): sub_attr_pack_layout = ['=', 'HH'] sub_attr_payload = [0, sub_attr_type] sub_attr_length_index = 0 - if sub_attr_type == Link.IFLA_INFO_KIND: + if sub_attr_type in (Link.IFLA_INFO_KIND, Link.IFLA_INFO_SLAVE_KIND): sub_attr_pack_layout.append('%ds' % len(sub_attr_value)) sub_attr_payload.append(sub_attr_value) elif sub_attr_type == Link.IFLA_INFO_DATA: + sub_attr_payload = self.encode_ifla_info_nested_data( + sub_attr_pack_layout, + sub_attr_type, + "IFLA_INFO_DATA", + sub_attr_value, + self.encode_ifla_info_nested_data_handlers.get(Link.IFLA_INFO_DATA, {}).get(kind), + self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_DATA, {}).get(kind), + kind + ) - sub_attr_payload = [0, sub_attr_type | NLA_F_NESTED] + elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA: + sub_attr_payload = self.encode_ifla_info_nested_data( + sub_attr_pack_layout, + sub_attr_type, + "IFLA_INFO_SLAVE_DATA", + sub_attr_value, + self.encode_ifla_info_nested_data_handlers.get(Link.IFLA_INFO_SLAVE_DATA, {}).get(slave_kind), + self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_SLAVE_DATA, {}).get(slave_kind), + slave_kind + ) - for (info_data_type, info_data_value) in sub_attr_value.iteritems(): + else: + self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_LINKINFO sub-attribute type %d' % sub_attr_type) + continue - if kind == 'vlan': - if info_data_type == Link.IFLA_VLAN_ID: - sub_attr_pack_layout.append('HH') - sub_attr_payload.append(6) # length - sub_attr_payload.append(info_data_type) + sub_attr_length = calcsize(''.join(sub_attr_pack_layout)) + sub_attr_payload[sub_attr_length_index] = sub_attr_length - # The vlan-id - sub_attr_pack_layout.append('H') - sub_attr_payload.append(info_data_value) + # add padding + for x in xrange(self.pad_bytes_needed(sub_attr_length)): + sub_attr_pack_layout.append('x') - # pad 2 bytes - sub_attr_pack_layout.extend('xx') + # The [1:] is to remove the leading = so that when we do the ''.join() later + # we do not end up with an = in the middle of the pack layout string. There + # will be an = at the beginning via self.HEADER_PACK + sub_attr_pack_layout = sub_attr_pack_layout[1:] - elif info_data_type == Link.IFLA_VLAN_PROTOCOL: - sub_attr_pack_layout.append('HH') - sub_attr_payload.append(6) # length - sub_attr_payload.append(info_data_type) + # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute + pack_layout.extend(sub_attr_pack_layout) + payload.extend(sub_attr_payload) - # vlan protocol - vlan_protocol = Link.ifla_vlan_protocol_dict.get(info_data_value) - if not vlan_protocol: - raise NotImplementedError('vlan protocol %s not implemented' % info_data_value) + pack_layout = ''.join(pack_layout) - sub_attr_pack_layout.append('H') - sub_attr_payload.append(htons(vlan_protocol)) + # Fill in the length field + length = calcsize(pack_layout) + payload[attr_length_index] = length - # pad 2 bytes - sub_attr_pack_layout.extend('xx') - else: - self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA vlan sub-attribute type %d' % info_data_type) - - elif kind == 'macvlan': - if info_data_type == Link.IFLA_MACVLAN_MODE: - sub_attr_pack_layout.append('HH') - sub_attr_payload.append(8) # length - sub_attr_payload.append(info_data_type) - - # macvlan mode - sub_attr_pack_layout.append('L') - sub_attr_payload.append(info_data_value) - - else: - self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA macvlan sub-attribute type %d' % info_data_type) - - elif kind == 'xfrm': - if info_data_type in (Link.IFLA_XFRM_IF_ID, Link.IFLA_XFRM_LINK): - sub_attr_pack_layout.append('HH') - sub_attr_payload.append(8) # length - sub_attr_payload.append(info_data_type) - - sub_attr_pack_layout.append('L') - sub_attr_payload.append(info_data_value) - - elif kind == 'vxlan': - if info_data_type in (Link.IFLA_VXLAN_ID, - Link.IFLA_VXLAN_LINK, - Link.IFLA_VXLAN_AGEING, - Link.IFLA_VXLAN_LIMIT, - Link.IFLA_VXLAN_PORT_RANGE): - sub_attr_pack_layout.append('HH') - sub_attr_payload.append(8) # length - sub_attr_payload.append(info_data_type) - - sub_attr_pack_layout.append('L') - sub_attr_payload.append(info_data_value) - - elif info_data_type in (Link.IFLA_VXLAN_GROUP, - Link.IFLA_VXLAN_LOCAL): - sub_attr_pack_layout.append('HH') - sub_attr_payload.append(8) # length - sub_attr_payload.append(info_data_type) - - sub_attr_pack_layout.append('L') - - reorder = unpack('H", info_data_value) - sub_attr_payload.append(unpack("L', sub_attr_data[4:8])[0]) - - # 4-byte int - elif info_data_type in (Link.IFLA_VXLAN_ID, - Link.IFLA_VXLAN_LINK, - Link.IFLA_VXLAN_AGEING, - Link.IFLA_VXLAN_LIMIT, - Link.IFLA_VXLAN_PORT_RANGE): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0] - - # 2-byte int - elif info_data_type in (Link.IFLA_VXLAN_PORT, ): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('!H', sub_attr_data[4:6])[0] - # The form '!' is available for those poor souls who claim they can't - # remember whether network byte order is big-endian or little-endian. - - # 1-byte int - elif info_data_type in (Link.IFLA_VXLAN_TTL, - Link.IFLA_VXLAN_TOS, - Link.IFLA_VXLAN_LEARNING, - Link.IFLA_VXLAN_PROXY, - Link.IFLA_VXLAN_RSC, - Link.IFLA_VXLAN_L2MISS, - Link.IFLA_VXLAN_L3MISS, - Link.IFLA_VXLAN_UDP_CSUM, - Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX, - Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX, - Link.IFLA_VXLAN_REMCSUM_TX, - Link.IFLA_VXLAN_REMCSUM_RX, - Link.IFLA_VXLAN_REPLICATION_TYPE): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0] - - else: - # sub_attr_end = padded_length(sub_attr_length) - self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND vxlan type %s (%d), length %d, padded to %d' % - (parent_msg.get_ifla_vxlan_string(info_data_type), info_data_type, info_data_length, info_data_end)) - - elif ifla_info_kind == 'bond': - - if info_data_type in (Link.IFLA_BOND_AD_INFO, ): - ad_attr_data = sub_attr_data[4:info_data_end] - self.value[Link.IFLA_INFO_DATA][Link.IFLA_BOND_AD_INFO] = {} - - while ad_attr_data: - (ad_data_length, ad_data_type) = unpack('=HH', ad_attr_data[:4]) - ad_data_end = padded_length(ad_data_length) - - if ad_data_type in (Link.IFLA_BOND_AD_INFO_PARTNER_MAC,): - (data1, data2) = unpack('>LHxx', ad_attr_data[4:12]) - self.value[Link.IFLA_INFO_DATA][Link.IFLA_BOND_AD_INFO][ad_data_type] = mac_int_to_str(data1 << 16 | data2) - - ad_attr_data = ad_attr_data[ad_data_end:] - - # 1-byte int - elif info_data_type in (Link.IFLA_BOND_MODE, - Link.IFLA_BOND_USE_CARRIER, - Link.IFLA_BOND_AD_LACP_RATE, - Link.IFLA_BOND_AD_LACP_BYPASS, - Link.IFLA_BOND_XMIT_HASH_POLICY, - Link.IFLA_BOND_NUM_PEER_NOTIF): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0] - - # 2-bytes int - elif info_data_type == Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0] - - # 4-bytes int - elif info_data_type in (Link.IFLA_BOND_MIIMON, - Link.IFLA_BOND_UPDELAY, - Link.IFLA_BOND_DOWNDELAY, - Link.IFLA_BOND_MIN_LINKS): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0] - - # mac address - elif info_data_type in (Link.IFLA_BOND_AD_ACTOR_SYSTEM, ): - (data1, data2) = unpack('>LHxx', sub_attr_data[4:12]) - self.value[Link.IFLA_INFO_DATA][info_data_type] = mac_int_to_str(data1 << 16 | data2) - - else: - self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND bond type %s (%d), length %d, padded to %d' % - (parent_msg.get_ifla_bond_string(info_data_type), info_data_type, info_data_length, info_data_end)) - - elif ifla_info_kind == 'bridge': - # 4 bytes - if info_data_type in (Link.IFLA_BR_AGEING_TIME, - Link.IFLA_BR_FORWARD_DELAY, - Link.IFLA_BR_HELLO_TIME, - Link.IFLA_BR_MAX_AGE, - Link.IFLA_BR_STP_STATE, - Link.IFLA_BR_ROOT_PATH_COST, - Link.IFLA_BR_MCAST_HASH_ELASTICITY, - Link.IFLA_BR_MCAST_HASH_MAX, - Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, - Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0] - - # 2 bytes - elif info_data_type in (Link.IFLA_BR_PRIORITY, - Link.IFLA_BR_GROUP_FWD_MASK, - Link.IFLA_BR_ROOT_PORT, - Link.IFLA_BR_VLAN_DEFAULT_PVID): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0] - - elif info_data_type == Link.IFLA_BR_VLAN_PROTOCOL: - hex_value = '0x%s' % sub_attr_data[4:6].encode('hex') - vlan_protocol = Link.ifla_vlan_protocol_dict.get(int(hex_value, base=16)) - - if vlan_protocol: - self.value[Link.IFLA_INFO_DATA][info_data_type] = vlan_protocol - else: - self.log.warning('IFLA_VLAN_PROTOCOL: cannot decode vlan protocol %s' % hex_value) - - # 8 bytes - elif info_data_type in (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, - Link.IFLA_BR_MCAST_QUERIER_INTVL, - Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, - Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, - Link.IFLA_BR_MCAST_QUERIER_INTVL, - Link.IFLA_BR_MCAST_QUERY_INTVL, - Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, - Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=Q', sub_attr_data[4:12])[0] - - # 1 bytes - elif info_data_type in (Link.IFLA_BR_VLAN_FILTERING, - Link.IFLA_BR_TOPOLOGY_CHANGE, - Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, - Link.IFLA_BR_MCAST_ROUTER, - Link.IFLA_BR_MCAST_SNOOPING, - Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, - Link.IFLA_BR_MCAST_QUERIER, - Link.IFLA_BR_NF_CALL_IPTABLES, - Link.IFLA_BR_NF_CALL_IP6TABLES, - Link.IFLA_BR_NF_CALL_ARPTABLES, - Link.IFLA_BR_VLAN_STATS_ENABLED, - Link.IFLA_BR_MCAST_STATS_ENABLED, - Link.IFLA_BR_MCAST_IGMP_VERSION, - Link.IFLA_BR_MCAST_MLD_VERSION): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0] - else: - self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND bridge type %s (%d), length %d, padded to %d' % - (parent_msg.get_ifla_br_string(info_data_type), info_data_type, info_data_length, info_data_end)) - - elif ifla_info_kind == 'vrf': - - if info_data_type in (Link.IFLA_VRF_TABLE,): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0] - - elif ifla_info_kind in ("gre", "gretap", "erspan", "ip6gre", "ip6gretap", "ip6erspan"): - - # 1-byte - if info_data_type in (Link.IFLA_GRE_TTL, Link.IFLA_GRE_TOS, Link.IFLA_GRE_PMTUDISC): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0] - - # 2-bytes - elif info_data_type in ( - Link.IFLA_GRE_IFLAGS, - Link.IFLA_GRE_OFLAGS, - Link.IFLA_GRE_ENCAP_TYPE, - Link.IFLA_GRE_ENCAP_SPORT, - Link.IFLA_GRE_ENCAP_DPORT, - Link.IFLA_GRE_ENCAP_FLAGS - ): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0] - - # 4 bytes - elif info_data_type in (Link.IFLA_GRE_LINK, Link.IFLA_GRE_IKEY, Link.IFLA_GRE_OKEY): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0] - - # ip addr - elif info_data_type in (Link.IFLA_GRE_LOCAL, Link.IFLA_GRE_REMOTE): - if ifla_info_kind in ("ip6gre", "ip6gretap", "ip6erspan"): - (data1, data2) = unpack(">QQ", sub_attr_data[4:20]) - self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv6Address(data1 << 64 | data2) - else: - self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack(">L", sub_attr_data[4:8])[0]) - - else: - self.log.log(SYSLOG_EXTRA_DEBUG, - 'Add support for decoding IFLA_INFO_KIND %s type %s (%d), length %d, padded to %d' % - (ifla_info_kind, parent_msg.get_ifla_gre_string(info_data_type), info_data_type, - info_data_length, info_data_end)) - - elif ifla_info_kind in ("ipip", "sit", "ip6tnl"): - - # 1-byte - if info_data_type in (Link.IFLA_IPTUN_TTL, Link.IFLA_IPTUN_TOS, Link.IFLA_IPTUN_PMTUDISC): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0] - - # 2-bytes - elif info_data_type in (Link.IFLA_IPTUN_ENCAP_TYPE, Link.IFLA_IPTUN_ENCAP_SPORT, Link.IFLA_IPTUN_ENCAP_DPORT, Link.IFLA_IPTUN_ENCAP_FLAGS): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[ - 0] - - # 4 bytes - elif info_data_type == Link.IFLA_IPTUN_LINK: - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[ - 0] - - # ip addr - elif info_data_type in (Link.IFLA_IPTUN_LOCAL, Link.IFLA_IPTUN_REMOTE): - if ifla_info_kind == "ip6tnl": - (data1, data2) = unpack(">QQ", sub_attr_data[4:20]) - self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv6Address(data1 << 64 | data2) - else: - self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack(">L", sub_attr_data[4:8])[0]) - - else: - self.log.log(SYSLOG_EXTRA_DEBUG, - 'Add support for decoding IFLA_INFO_KIND %s type %s (%d), length %d, padded to %d' % - (ifla_info_kind, parent_msg.get_ifla_iptun_string(info_data_type), info_data_type, - info_data_length, info_data_end)) - - elif ifla_info_kind in ("vti", "vti6"): - # 4 bytes - if info_data_type in (Link.IFLA_VTI_LINK, Link.IFLA_VTI_IKEY, Link.IFLA_VTI_OKEY): - self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0] - - # ip addr - elif info_data_type in (Link.IFLA_VTI_LOCAL, Link.IFLA_VTI_REMOTE): - if ifla_info_kind == "vti6": - (data1, data2) = unpack(">QQ", sub_attr_data[4:20]) - self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv6Address(data1 << 64 | data2) - else: - self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack(">L", sub_attr_data[4:8])[0]) - - else: - self.log.log(SYSLOG_EXTRA_DEBUG, - 'Add support for decoding IFLA_INFO_KIND %s type %s (%d), length %d, padded to %d' % - (ifla_info_kind, parent_msg.get_ifla_vti_string(info_data_type), info_data_type, - info_data_length, info_data_end)) + self.value[Link.IFLA_INFO_DATA] = self.decode_ifla_info_nested_data( + self.value.get(Link.IFLA_INFO_KIND), "IFLA_INFO_KIND", + Link.IFLA_INFO_DATA, "IFLA_INFO_DATA", + data, sub_attr_end, + ) - else: - self.log.log(SYSLOG_EXTRA_DEBUG, "Add support for decoding IFLA_INFO_KIND %s (%d), length %d, padded to %d" % - (ifla_info_kind, info_data_type, info_data_length, info_data_end)) - - except Exception as e: - self.log.debug('%s: attribute %s: %s' - % (self.value[Link.IFLA_INFO_KIND], - info_data_type, - str(e))) - sub_attr_data = sub_attr_data[info_data_end:] + elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA: + self.value[Link.IFLA_INFO_SLAVE_DATA] = self.decode_ifla_info_nested_data( + self.value.get(Link.IFLA_INFO_SLAVE_KIND), "IFLA_INFO_SLAVE_KIND", + Link.IFLA_INFO_SLAVE_DATA, "IFLA_INFO_SLAVE_DATA", + data, sub_attr_end, + ) else: - self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_LINKINFO sub-attribute type %s (%d), length %d, padded to %d' % - (parent_msg.get_ifla_info_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end)) + self.log.log( + SYSLOG_EXTRA_DEBUG, + 'Add support for decoding IFLA_LINKINFO sub-attribute type %s (%d), length %d, padded to %d' + % (parent_msg.get_ifla_info_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end) + ) data = data[sub_attr_end:] - # self.log.info('IFLA_LINKINFO values %s' % pformat(self.value)) - def dump_lines(self, dump_buffer, line_number, color): line_number = self.dump_first_line(dump_buffer, line_number, color) extra = '' @@ -2000,11 +3012,14 @@ def dump_lines(self, dump_buffer, line_number, color): else: padded_to = ' padded to %d, ' % sub_attr_end - extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \ + if sub_attr_type & NLA_F_NESTED: + sub_attr_type ^= NLA_F_NESTED + + extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s (%s)' % \ (zfilled_hex(sub_attr_length, 4), sub_attr_length, padded_to, zfilled_hex(sub_attr_type, 4), sub_attr_type, - Link.ifla_info_to_string.get(sub_attr_type)) + Link.ifla_info_to_string.get(sub_attr_type), sub_attr_type) else: extra = '' @@ -2022,38 +3037,10 @@ def get_pretty_value(self, obj=None): ifla_info_kind = self.value.get(Link.IFLA_INFO_KIND) ifla_info_slave_kind = self.value.get(Link.IFLA_INFO_SLAVE_KIND) - kind_dict = dict() - - # We do this so we can print a more human readable dictionary - # with the names of the nested keys instead of their numbers - - # Most of these are placeholders...we need to add support - # for more human readable dictionaries for bond, bridge, etc - kind_dict[Link.IFLA_INFO_DATA] = { - 'bond': Link.ifla_bond_to_string, - 'vlan': Link.ifla_vlan_to_string, - 'vxlan': Link.ifla_vxlan_to_string, - 'bridge': Link.ifla_br_to_string, - 'macvlan': Link.ifla_macvlan_to_string, - 'gre': Link.ifla_gre_to_string, - 'gretap': Link.ifla_gre_to_string, - 'erspan': Link.ifla_gre_to_string, - 'ip6gre': Link.ifla_gre_to_string, - 'ip6gretap': Link.ifla_gre_to_string, - 'ip6erspan': Link.ifla_gre_to_string, - 'vti': Link.ifla_vti_to_string, - 'vti6': Link.ifla_vti_to_string, - 'ipip': Link.ifla_iptun_to_string, - 'sit': Link.ifla_iptun_to_string, - 'ip6tnl': Link.ifla_iptun_to_string, - 'xfrm': Link.ifla_xfrm_to_string - }.get(ifla_info_kind, {}) - - kind_dict[Link.IFLA_INFO_SLAVE_DATA] = { - 'bridge': Link.ifla_brport_to_string, - 'bond': Link.ifla_bond_slave_to_string - }.get(ifla_info_slave_kind, {}) - + kind_dict = { + Link.IFLA_INFO_DATA: self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_DATA, {}).get(ifla_info_kind), + Link.IFLA_INFO_SLAVE_DATA: self.ifla_info_nested_data_attributes_to_string_dict.get(Link.IFLA_INFO_SLAVE_DATA, {}).get(ifla_info_slave_kind) + } if ifla_info_kind or ifla_info_slave_kind: value_pretty = {} @@ -2120,11 +3107,9 @@ def encode(self): Link.IFLA_BRPORT_MULTICAST_ROUTER, Link.IFLA_BRPORT_PEER_LINK, Link.IFLA_BRPORT_DUAL_LINK, - Link.IFLA_BRPORT_ARP_SUPPRESS, - Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT): + Link.IFLA_BRPORT_NEIGH_SUPPRESS): sub_attr_pack_layout.append('B') sub_attr_payload.append(sub_attr_value) - sub_attr_pack_layout.extend('xxx') # 2 Byte attributes elif sub_attr_type in (Link.IFLA_BRPORT_PRIORITY, @@ -2134,10 +3119,9 @@ def encode(self): Link.IFLA_BRPORT_NO): sub_attr_pack_layout.append('H') sub_attr_payload.append(sub_attr_value) - sub_attr_pack_layout.extend('xx') # 4 Byte attributes - elif sub_attr_type in (Link.IFLA_BRPORT_COST,): + elif sub_attr_type in (Link.IFLA_BRPORT_COST, Link.IFLA_BRPORT_BACKUP_PORT): sub_attr_pack_layout.append('L') sub_attr_payload.append(sub_attr_value) @@ -2219,8 +3203,7 @@ def decode(self, parent_msg, data): Link.IFLA_BRPORT_MULTICAST_ROUTER, Link.IFLA_BRPORT_PEER_LINK, Link.IFLA_BRPORT_DUAL_LINK, - Link.IFLA_BRPORT_ARP_SUPPRESS, - Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT): + Link.IFLA_BRPORT_NEIGH_SUPPRESS): self.value[sub_attr_type] = unpack('=B', data[4])[0] # 2 Byte attributes @@ -2232,7 +3215,7 @@ def decode(self, parent_msg, data): self.value[sub_attr_type] = unpack('=H', data[4:6])[0] # 4 Byte attributes - elif sub_attr_type in (Link.IFLA_BRPORT_COST,): + elif sub_attr_type in (Link.IFLA_BRPORT_COST, Link.IFLA_BRPORT_BACKUP_PORT): self.value[sub_attr_type] = unpack('=L', data[4:8])[0] # 8 Byte attributes @@ -2347,7 +3330,11 @@ class NetlinkPacket(object): RTM_DELQDISC : 'RTM_DELQDISC', RTM_GETQDISC : 'RTM_GETQDISC', RTM_NEWNETCONF: 'RTM_NEWNETCONF', - RTM_GETNETCONF: 'RTM_GETNETCONF' + RTM_GETNETCONF: 'RTM_GETNETCONF', + RTM_DELNETCONF: 'RTM_DELNETCONF', + RTM_NEWMDB : 'RTM_NEWMDB', + RTM_DELMDB : 'RTM_DELMDB', + RTM_GETMDB : 'RTM_GETMDB' } af_family_to_string = { @@ -2355,7 +3342,7 @@ class NetlinkPacket(object): AF_INET6 : 'inet6' } - def __init__(self, msgtype, debug, owner_logger=None, use_color=True): + def __init__(self, msgtype, debug, owner_logger=None, use_color=True, rx=False, tx=False): self.msgtype = msgtype self.attributes = {} self.dump_buffer = [''] @@ -2364,6 +3351,9 @@ def __init__(self, msgtype, debug, owner_logger=None, use_color=True): self.message = None self.use_color = use_color self.family = None + self.rx = rx + self.tx = tx + self.priv_flags = 0 if owner_logger: self.log = owner_logger @@ -2427,7 +3417,7 @@ def get_netlink_header_flags_string(self, msg_type, flags): foo.append('NLM_F_ECHO') # Modifiers to GET query - if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC, RTM_GETNETCONF): + if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC, RTM_GETNETCONF, RTM_GETMDB): if flags & NLM_F_DUMP: foo.append('NLM_F_DUMP') else: @@ -2441,7 +3431,7 @@ def get_netlink_header_flags_string(self, msg_type, flags): foo.append('NLM_F_ATOMIC') # Modifiers to NEW query - elif msg_type in (RTM_NEWLINK, RTM_NEWADDR, RTM_NEWNEIGH, RTM_NEWROUTE, RTM_NEWQDISC): + elif msg_type in (RTM_NEWLINK, RTM_NEWADDR, RTM_NEWNEIGH, RTM_NEWROUTE, RTM_NEWQDISC, RTM_NEWMDB): if flags & NLM_F_REPLACE: foo.append('NLM_F_REPLACE') @@ -2561,7 +3551,7 @@ def add_attribute(self, attr_type, value): take the family into account. For now we'll handle this as a special case for MPLS but long term we may need to make key a tuple of the attr_type and family. ''' - if self.msgtype not in (RTM_NEWNETCONF, RTM_GETNETCONF) and attr_type == Route.RTA_DST and self.family == AF_MPLS: + if self.msgtype not in (RTM_NEWNETCONF, RTM_GETNETCONF, RTM_DELNETCONF) and attr_type == Route.RTA_DST and self.family == AF_MPLS: attr_string = 'RTA_DST' attr_class = AttributeMplsLabel @@ -2675,6 +3665,7 @@ class Address(NetlinkPacket): IFA_CACHEINFO = 0x06 IFA_MULTICAST = 0x07 IFA_FLAGS = 0x08 + IFA_RT_PRIORITY = 0x09 # 32, priority / metricfor prefix route attribute_to_class = { IFA_UNSPEC : ('IFA_UNSPEC', AttributeGeneric), @@ -2683,9 +3674,10 @@ class Address(NetlinkPacket): IFA_LABEL : ('IFA_LABEL', AttributeString), IFA_BROADCAST : ('IFA_BROADCAST', AttributeIPAddress), IFA_ANYCAST : ('IFA_ANYCAST', AttributeIPAddress), - IFA_CACHEINFO : ('IFA_CACHEINFO', AttributeGeneric), + IFA_CACHEINFO : ('IFA_CACHEINFO', AttributeCACHEINFO), IFA_MULTICAST : ('IFA_MULTICAST', AttributeIPAddress), - IFA_FLAGS : ('IFA_FLAGS', AttributeGeneric) + IFA_FLAGS : ('IFA_FLAGS', AttributeGeneric), + IFA_RT_PRIORITY : ('IFA_RT_PRIORITY', AttributeFourByteValue) } # Address flags @@ -2787,6 +3779,7 @@ class Error(NetlinkPacket): NLE_NODEV = 0x1F NLE_IMMUTABLE = 0x20 NLE_DUMP_INTR = 0x21 + NLE_ATTRSIZE = 0x22 error_to_string = { NLE_SUCCESS : 'NLE_SUCCESS', @@ -2822,7 +3815,46 @@ class Error(NetlinkPacket): NLE_PARSE_ERR : 'NLE_PARSE_ERR', NLE_NODEV : 'NLE_NODEV', NLE_IMMUTABLE : 'NLE_IMMUTABLE', - NLE_DUMP_INTR : 'NLE_DUMP_INTR' + NLE_DUMP_INTR : 'NLE_DUMP_INTR', + NLE_ATTRSIZE : 'NLE_ATTRSIZE' + } + + error_to_human_readable_string = { + NLE_SUCCESS: "Success", + NLE_FAILURE: "Unspecific failure", + NLE_INTR: "Interrupted system call", + NLE_BAD_SOCK: "Bad socket", + NLE_AGAIN: "Try again", + NLE_NOMEM: "Out of memory", + NLE_EXIST: "Object exists", + NLE_INVAL: "Invalid input data or parameter", + NLE_RANGE: "Input data out of range", + NLE_MSGSIZE: "Message size not sufficient", + NLE_OPNOTSUPP: "Operation not supported", + NLE_AF_NOSUPPORT: "Address family not supported", + NLE_OBJ_NOTFOUND: "Object not found", + NLE_NOATTR: "Attribute not available", + NLE_MISSING_ATTR: "Missing attribute", + NLE_AF_MISMATCH: "Address family mismatch", + NLE_SEQ_MISMATCH: "Message sequence number mismatch", + NLE_MSG_OVERFLOW: "Kernel reported message overflow", + NLE_MSG_TRUNC: "Kernel reported truncated message", + NLE_NOADDR: "Invalid address for specified address family", + NLE_SRCRT_NOSUPPORT: "Source based routing not supported", + NLE_MSG_TOOSHORT: "Netlink message is too short", + NLE_MSGTYPE_NOSUPPORT: "Netlink message type is not supported", + NLE_OBJ_MISMATCH: "Object type does not match cache", + NLE_NOCACHE: "Unknown or invalid cache type", + NLE_BUSY: "Object busy", + NLE_PROTO_MISMATCH: "Protocol mismatch", + NLE_NOACCESS: "No Access", + NLE_PERM: "Operation not permitted", + NLE_PKTLOC_FILE: "Unable to open packet location file", + NLE_PARSE_ERR: "Unable to parse object", + NLE_NODEV: "No such device", + NLE_IMMUTABLE: "Immutable attribute", + NLE_DUMP_INTR: "Dump inconsistency detected, interrupted", + NLE_ATTRSIZE: "Attribute max length exceeded", } def __init__(self, msgtype, debug=False, logger=None, use_color=True): @@ -2849,7 +3881,8 @@ def decode_service_header(self): for x in range(0, self.LEN/4): if self.line_number == 5: - extra = "Error Number %s is %s" % (self.negative_errno, self.error_to_string.get(abs(self.negative_errno))) + error_number = abs(self.negative_errno) + extra = "Error Number %s is %s (%s)" % (self.negative_errno, self.error_to_string.get(error_number), self.error_to_human_readable_string.get(error_number)) # zfilled_hex(self.negative_errno, 2) elif self.line_number == 6: @@ -2875,7 +3908,7 @@ def decode_service_header(self): self.line_number += 1 -class Link(NetlinkPacket): +class Link(NetlinkPacket, NetlinkPacket_IFLA_LINKINFO_Attributes): """ Service Header @@ -2936,6 +3969,16 @@ class Link(NetlinkPacket): IFLA_PROTO_DOWN = 39 IFLA_GSO_MAX_SEGS = 40 IFLA_GSO_MAX_SIZE = 41 + IFLA_PAD = 42 + IFLA_XDP = 43 + IFLA_EVENT = 44 + IFLA_NEW_NETNSID = 45 + IFLA_IF_NETNSID = 46 + IFLA_CARRIER_UP_COUNT = 47 + IFLA_CARRIER_DOWN_COUNT = 48 + IFLA_NEW_IFINDEX = 49 + IFLA_MIN_MTU = 50 + IFLA_MAX_MTU = 51 attribute_to_class = { IFLA_UNSPEC : ('IFLA_UNSPEC', AttributeGeneric), @@ -2958,7 +4001,7 @@ class Link(NetlinkPacket): IFLA_LINKMODE : ('IFLA_LINKMODE', AttributeOneByteValue), IFLA_LINKINFO : ('IFLA_LINKINFO', AttributeIFLA_LINKINFO), IFLA_NET_NS_PID : ('IFLA_NET_NS_PID', AttributeGeneric), - IFLA_IFALIAS : ('IFLA_IFALIAS', AttributeGeneric), + IFLA_IFALIAS : ('IFLA_IFALIAS', AttributeString), IFLA_NUM_VF : ('IFLA_NUM_VF', AttributeGeneric), IFLA_VFINFO_LIST : ('IFLA_VFINFO_LIST', AttributeGeneric), IFLA_STATS64 : ('IFLA_STATS64', AttributeGeneric), @@ -2979,7 +4022,17 @@ class Link(NetlinkPacket): IFLA_PHYS_PORT_NAME : ('IFLA_PHYS_PORT_NAME', AttributeGeneric), IFLA_PROTO_DOWN : ('IFLA_PROTO_DOWN', AttributeOneByteValue), IFLA_GSO_MAX_SEGS : ('IFLA_GSO_MAX_SEGS', AttributeFourByteValue), - IFLA_GSO_MAX_SIZE : ('IFLA_GSO_MAX_SIZE', AttributeFourByteValue) + IFLA_GSO_MAX_SIZE : ('IFLA_GSO_MAX_SIZE', AttributeFourByteValue), + IFLA_PAD : ('IFLA_PAD', AttributeGeneric), + IFLA_XDP : ('IFLA_XDP', AttributeGeneric), + IFLA_EVENT : ('IFLA_EVENT', AttributeFourByteValue), + IFLA_NEW_NETNSID : ('IFLA_NEW_NETNSID', AttributeFourByteValue), + IFLA_IF_NETNSID : ('IFLA_IF_NETNSID', AttributeFourByteValue), + IFLA_CARRIER_UP_COUNT : ('IFLA_CARRIER_UP_COUNT', AttributeFourByteValue), + IFLA_CARRIER_DOWN_COUNT : ('IFLA_CARRIER_DOWN_COUNT', AttributeFourByteValue), + IFLA_NEW_IFINDEX : ('IFLA_NEW_IFINDEX', AttributeFourByteValue), + IFLA_MIN_MTU : ('IFLA_MIN_MTU', AttributeFourByteValue), + IFLA_MAX_MTU : ('IFLA_MAX_MTU', AttributeFourByteValue), } # Link flags @@ -3115,469 +4168,67 @@ class Link(NetlinkPacket): ARPHRD_NONE = 0xFFFE # zero header length link_type_to_string = { - ARPHRD_NETROM : 'ARPHRD_NETROM', - ARPHRD_ETHER : 'ARPHRD_ETHER', - ARPHRD_EETHER : 'ARPHRD_EETHER', - ARPHRD_AX25 : 'ARPHRD_AX25', - ARPHRD_PRONET : 'ARPHRD_PRONET', - ARPHRD_CHAOS : 'ARPHRD_CHAOS', - ARPHRD_IEEE802 : 'ARPHRD_IEEE802', - ARPHRD_ARCNET : 'ARPHRD_ARCNET', - ARPHRD_APPLETLK : 'ARPHRD_APPLETLK', - ARPHRD_DLCI : 'ARPHRD_DLCI', - ARPHRD_ATM : 'ARPHRD_ATM', - ARPHRD_METRICOM : 'ARPHRD_METRICOM', - ARPHRD_IEEE1394 : 'ARPHRD_IEEE1394', - ARPHRD_EUI64 : 'ARPHRD_EUI64', - ARPHRD_INFINIBAND : 'ARPHRD_INFINIBAND', - ARPHRD_SLIP : 'ARPHRD_SLIP', - ARPHRD_CSLIP : 'ARPHRD_CSLIP', - ARPHRD_SLIP6 : 'ARPHRD_SLIP6', - ARPHRD_CSLIP6 : 'ARPHRD_CSLIP6', - ARPHRD_RSRVD : 'ARPHRD_RSRVD', - ARPHRD_ADAPT : 'ARPHRD_ADAPT', - ARPHRD_ROSE : 'ARPHRD_ROSE', - ARPHRD_X25 : 'ARPHRD_X25', - ARPHRD_HWX25 : 'ARPHRD_HWX25', - ARPHRD_CAN : 'ARPHRD_CAN', - ARPHRD_PPP : 'ARPHRD_PPP', - ARPHRD_CISCO : 'ARPHRD_CISCO', - ARPHRD_HDLC : 'ARPHRD_HDLC', - ARPHRD_LAPB : 'ARPHRD_LAPB', - ARPHRD_DDCMP : 'ARPHRD_DDCMP', - ARPHRD_RAWHDLC : 'ARPHRD_RAWHDLC', - ARPHRD_TUNNEL : 'ARPHRD_TUNNEL', - ARPHRD_TUNNEL6 : 'ARPHRD_TUNNEL6', - ARPHRD_FRAD : 'ARPHRD_FRAD', - ARPHRD_SKIP : 'ARPHRD_SKIP', - ARPHRD_LOOPBACK : 'ARPHRD_LOOPBACK', - ARPHRD_LOCALTLK : 'ARPHRD_LOCALTLK', - ARPHRD_FDDI : 'ARPHRD_FDDI', - ARPHRD_BIF : 'ARPHRD_BIF', - ARPHRD_SIT : 'ARPHRD_SIT', - ARPHRD_IPDDP : 'ARPHRD_IPDDP', - ARPHRD_IPGRE : 'ARPHRD_IPGRE', - ARPHRD_PIMREG : 'ARPHRD_PIMREG', - ARPHRD_HIPPI : 'ARPHRD_HIPPI', - ARPHRD_ASH : 'ARPHRD_ASH', - ARPHRD_ECONET : 'ARPHRD_ECONET', - ARPHRD_IRDA : 'ARPHRD_IRDA', - ARPHRD_FCPP : 'ARPHRD_FCPP', - ARPHRD_FCAL : 'ARPHRD_FCAL', - ARPHRD_FCPL : 'ARPHRD_FCPL', - ARPHRD_FCFABRIC : 'ARPHRD_FCFABRIC', - ARPHRD_IEEE802_TR : 'ARPHRD_IEEE802_TR', - ARPHRD_IEEE80211 : 'ARPHRD_IEEE80211', - ARPHRD_IEEE80211_PRISM : 'ARPHRD_IEEE80211_PRISM', - ARPHRD_IEEE80211_RADIOTAP : 'ARPHRD_IEEE80211_RADIOTAP', - ARPHRD_IEEE802154 : 'ARPHRD_IEEE802154', - ARPHRD_PHONET : 'ARPHRD_PHONET', - ARPHRD_PHONET_PIPE : 'ARPHRD_PHONET_PIPE', - ARPHRD_CAIF : 'ARPHRD_CAIF', - ARPHRD_VOID : 'ARPHRD_VOID', - ARPHRD_NONE : 'ARPHRD_NONE' - } - - # ========================================= - # IFLA_LINKINFO attributes - # ========================================= - IFLA_INFO_UNSPEC = 0 - IFLA_INFO_KIND = 1 - IFLA_INFO_DATA = 2 - IFLA_INFO_XSTATS = 3 - IFLA_INFO_SLAVE_KIND = 4 - IFLA_INFO_SLAVE_DATA = 5 - IFLA_INFO_MAX = 6 - - ifla_info_to_string = { - IFLA_INFO_UNSPEC : 'IFLA_INFO_UNSPEC', - IFLA_INFO_KIND : 'IFLA_INFO_KIND', - IFLA_INFO_DATA : 'IFLA_INFO_DATA', - IFLA_INFO_XSTATS : 'IFLA_INFO_XSTATS', - IFLA_INFO_SLAVE_KIND : 'IFLA_INFO_SLAVE_KIND', - IFLA_INFO_SLAVE_DATA : 'IFLA_INFO_SLAVE_DATA', - IFLA_INFO_MAX : 'IFLA_INFO_MAX' - } - - # ========================================= - # IFLA_INFO_DATA attributes for vlan - # ========================================= - IFLA_VLAN_UNSPEC = 0 - IFLA_VLAN_ID = 1 - IFLA_VLAN_FLAGS = 2 - IFLA_VLAN_EGRESS_QOS = 3 - IFLA_VLAN_INGRESS_QOS = 4 - IFLA_VLAN_PROTOCOL = 5 - - ifla_vlan_to_string = { - IFLA_VLAN_UNSPEC : 'IFLA_VLAN_UNSPEC', - IFLA_VLAN_ID : 'IFLA_VLAN_ID', - IFLA_VLAN_FLAGS : 'IFLA_VLAN_FLAGS', - IFLA_VLAN_EGRESS_QOS : 'IFLA_VLAN_EGRESS_QOS', - IFLA_VLAN_INGRESS_QOS : 'IFLA_VLAN_INGRESS_QOS', - IFLA_VLAN_PROTOCOL : 'IFLA_VLAN_PROTOCOL' - } - - ifla_vlan_protocol_dict = { - '802.1Q': 0x8100, - '802.1q': 0x8100, - - '802.1ad': 0x88A8, - '802.1AD': 0x88A8, - '802.1Ad': 0x88A8, - '802.1aD': 0x88A8, - - 0x8100: '802.1Q', - 0x88A8: '802.1ad' - } - - # ========================================= - # IFLA_INFO_DATA attributes for macvlan - # ========================================= - IFLA_MACVLAN_UNSPEC = 0 - IFLA_MACVLAN_MODE = 1 - - ifla_macvlan_to_string = { - IFLA_MACVLAN_UNSPEC : 'IFLA_MACVLAN_UNSPEC', - IFLA_MACVLAN_MODE : 'IFLA_MACVLAN_MODE' - } - - # macvlan modes - MACVLAN_MODE_PRIVATE = 1 - MACVLAN_MODE_VEPA = 2 - MACVLAN_MODE_BRIDGE = 3 - MACVLAN_MODE_PASSTHRU = 4 - - macvlan_mode_to_string = { - MACVLAN_MODE_PRIVATE : 'MACVLAN_MODE_PRIVATE', - MACVLAN_MODE_VEPA : 'MACVLAN_MODE_VEPA', - MACVLAN_MODE_BRIDGE : 'MACVLAN_MODE_BRIDGE', - MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU' - } - - # ========================================= - # IFLA_INFO_DATA attributes for xfrm - # ========================================= - IFLA_XFRM_UNSPEC = 0 - IFLA_XFRM_LINK = 1 - IFLA_XFRM_IF_ID = 2 - - ifla_xfrm_to_string = { - IFLA_XFRM_UNSPEC : 'IFLA_XFRM_UNSPEC', - IFLA_XFRM_LINK : 'IFLA_XFRM_LINK', - IFLA_XFRM_IF_ID : 'IFLA_XFRM_IF_ID' - } - - # ========================================= - # IFLA_INFO_DATA attributes for vxlan - # ========================================= - IFLA_VXLAN_UNSPEC = 0 - IFLA_VXLAN_ID = 1 - IFLA_VXLAN_GROUP = 2 - IFLA_VXLAN_LINK = 3 - IFLA_VXLAN_LOCAL = 4 - IFLA_VXLAN_TTL = 5 - IFLA_VXLAN_TOS = 6 - IFLA_VXLAN_LEARNING = 7 - IFLA_VXLAN_AGEING = 8 - IFLA_VXLAN_LIMIT = 9 - IFLA_VXLAN_PORT_RANGE = 10 - IFLA_VXLAN_PROXY = 11 - IFLA_VXLAN_RSC = 12 - IFLA_VXLAN_L2MISS = 13 - IFLA_VXLAN_L3MISS = 14 - IFLA_VXLAN_PORT = 15 - IFLA_VXLAN_GROUP6 = 16 - IFLA_VXLAN_LOCAL6 = 17 - IFLA_VXLAN_UDP_CSUM = 18 - IFLA_VXLAN_UDP_ZERO_CSUM6_TX = 19 - IFLA_VXLAN_UDP_ZERO_CSUM6_RX = 20 - IFLA_VXLAN_REMCSUM_TX = 21 - IFLA_VXLAN_REMCSUM_RX = 22 - IFLA_VXLAN_GBP = 23 - IFLA_VXLAN_REMCSUM_NOPARTIAL = 24 - IFLA_VXLAN_COLLECT_METADATA = 25 - IFLA_VXLAN_REPLICATION_NODE = 253 - IFLA_VXLAN_REPLICATION_TYPE = 254 - - ifla_vxlan_to_string = { - IFLA_VXLAN_UNSPEC : 'IFLA_VXLAN_UNSPEC', - IFLA_VXLAN_ID : 'IFLA_VXLAN_ID', - IFLA_VXLAN_GROUP : 'IFLA_VXLAN_GROUP', - IFLA_VXLAN_LINK : 'IFLA_VXLAN_LINK', - IFLA_VXLAN_LOCAL : 'IFLA_VXLAN_LOCAL', - IFLA_VXLAN_TTL : 'IFLA_VXLAN_TTL', - IFLA_VXLAN_TOS : 'IFLA_VXLAN_TOS', - IFLA_VXLAN_LEARNING : 'IFLA_VXLAN_LEARNING', - IFLA_VXLAN_AGEING : 'IFLA_VXLAN_AGEING', - IFLA_VXLAN_LIMIT : 'IFLA_VXLAN_LIMIT', - IFLA_VXLAN_PORT_RANGE : 'IFLA_VXLAN_PORT_RANGE', - IFLA_VXLAN_PROXY : 'IFLA_VXLAN_PROXY', - IFLA_VXLAN_RSC : 'IFLA_VXLAN_RSC', - IFLA_VXLAN_L2MISS : 'IFLA_VXLAN_L2MISS', - IFLA_VXLAN_L3MISS : 'IFLA_VXLAN_L3MISS', - IFLA_VXLAN_PORT : 'IFLA_VXLAN_PORT', - IFLA_VXLAN_GROUP6 : 'IFLA_VXLAN_GROUP6', - IFLA_VXLAN_LOCAL6 : 'IFLA_VXLAN_LOCAL6', - IFLA_VXLAN_UDP_CSUM : 'IFLA_VXLAN_UDP_CSUM', - IFLA_VXLAN_UDP_ZERO_CSUM6_TX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_TX', - IFLA_VXLAN_UDP_ZERO_CSUM6_RX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_RX', - IFLA_VXLAN_REMCSUM_TX : 'IFLA_VXLAN_REMCSUM_TX', - IFLA_VXLAN_REMCSUM_RX : 'IFLA_VXLAN_REMCSUM_RX', - IFLA_VXLAN_GBP : 'IFLA_VXLAN_GBP', - IFLA_VXLAN_REMCSUM_NOPARTIAL : 'IFLA_VXLAN_REMCSUM_NOPARTIAL', - IFLA_VXLAN_COLLECT_METADATA : 'IFLA_VXLAN_COLLECT_METADATA', - IFLA_VXLAN_REPLICATION_NODE : 'IFLA_VXLAN_REPLICATION_NODE', - IFLA_VXLAN_REPLICATION_TYPE : 'IFLA_VXLAN_REPLICATION_TYPE' - } - - # ========================================= - # IFLA_INFO_DATA attributes for bonds - # ========================================= - IFLA_BOND_UNSPEC = 0 - IFLA_BOND_MODE = 1 - IFLA_BOND_ACTIVE_SLAVE = 2 - IFLA_BOND_MIIMON = 3 - IFLA_BOND_UPDELAY = 4 - IFLA_BOND_DOWNDELAY = 5 - IFLA_BOND_USE_CARRIER = 6 - IFLA_BOND_ARP_INTERVAL = 7 - IFLA_BOND_ARP_IP_TARGET = 8 - IFLA_BOND_ARP_VALIDATE = 9 - IFLA_BOND_ARP_ALL_TARGETS = 10 - IFLA_BOND_PRIMARY = 11 - IFLA_BOND_PRIMARY_RESELECT = 12 - IFLA_BOND_FAIL_OVER_MAC = 13 - IFLA_BOND_XMIT_HASH_POLICY = 14 - IFLA_BOND_RESEND_IGMP = 15 - IFLA_BOND_NUM_PEER_NOTIF = 16 - IFLA_BOND_ALL_SLAVES_ACTIVE = 17 - IFLA_BOND_MIN_LINKS = 18 - IFLA_BOND_LP_INTERVAL = 19 - IFLA_BOND_PACKETS_PER_SLAVE = 20 - IFLA_BOND_AD_LACP_RATE = 21 - IFLA_BOND_AD_SELECT = 22 - IFLA_BOND_AD_INFO = 23 - IFLA_BOND_AD_ACTOR_SYS_PRIO = 24 - IFLA_BOND_AD_USER_PORT_KEY = 25 - IFLA_BOND_AD_ACTOR_SYSTEM = 26 - IFLA_BOND_AD_LACP_BYPASS = 100 - - ifla_bond_to_string = { - IFLA_BOND_UNSPEC : 'IFLA_BOND_UNSPEC', - IFLA_BOND_MODE : 'IFLA_BOND_MODE', - IFLA_BOND_ACTIVE_SLAVE : 'IFLA_BOND_ACTIVE_SLAVE', - IFLA_BOND_MIIMON : 'IFLA_BOND_MIIMON', - IFLA_BOND_UPDELAY : 'IFLA_BOND_UPDELAY', - IFLA_BOND_DOWNDELAY : 'IFLA_BOND_DOWNDELAY', - IFLA_BOND_USE_CARRIER : 'IFLA_BOND_USE_CARRIER', - IFLA_BOND_ARP_INTERVAL : 'IFLA_BOND_ARP_INTERVAL', - IFLA_BOND_ARP_IP_TARGET : 'IFLA_BOND_ARP_IP_TARGET', - IFLA_BOND_ARP_VALIDATE : 'IFLA_BOND_ARP_VALIDATE', - IFLA_BOND_ARP_ALL_TARGETS : 'IFLA_BOND_ARP_ALL_TARGETS', - IFLA_BOND_PRIMARY : 'IFLA_BOND_PRIMARY', - IFLA_BOND_PRIMARY_RESELECT : 'IFLA_BOND_PRIMARY_RESELECT', - IFLA_BOND_FAIL_OVER_MAC : 'IFLA_BOND_FAIL_OVER_MAC', - IFLA_BOND_XMIT_HASH_POLICY : 'IFLA_BOND_XMIT_HASH_POLICY', - IFLA_BOND_RESEND_IGMP : 'IFLA_BOND_RESEND_IGMP', - IFLA_BOND_NUM_PEER_NOTIF : 'IFLA_BOND_NUM_PEER_NOTIF', - IFLA_BOND_ALL_SLAVES_ACTIVE : 'IFLA_BOND_ALL_SLAVES_ACTIVE', - IFLA_BOND_MIN_LINKS : 'IFLA_BOND_MIN_LINKS', - IFLA_BOND_LP_INTERVAL : 'IFLA_BOND_LP_INTERVAL', - IFLA_BOND_PACKETS_PER_SLAVE : 'IFLA_BOND_PACKETS_PER_SLAVE', - IFLA_BOND_AD_LACP_RATE : 'IFLA_BOND_AD_LACP_RATE', - IFLA_BOND_AD_SELECT : 'IFLA_BOND_AD_SELECT', - IFLA_BOND_AD_INFO : 'IFLA_BOND_AD_INFO', - IFLA_BOND_AD_ACTOR_SYS_PRIO : 'IFLA_BOND_AD_ACTOR_SYS_PRIO', - IFLA_BOND_AD_USER_PORT_KEY : 'IFLA_BOND_AD_USER_PORT_KEY', - IFLA_BOND_AD_ACTOR_SYSTEM : 'IFLA_BOND_AD_ACTOR_SYSTEM', - IFLA_BOND_AD_LACP_BYPASS : 'IFLA_BOND_AD_LACP_BYPASS' - } - - IFLA_BOND_AD_INFO_UNSPEC = 0 - IFLA_BOND_AD_INFO_AGGREGATOR = 1 - IFLA_BOND_AD_INFO_NUM_PORTS = 2 - IFLA_BOND_AD_INFO_ACTOR_KEY = 3 - IFLA_BOND_AD_INFO_PARTNER_KEY = 4 - IFLA_BOND_AD_INFO_PARTNER_MAC = 5 - - ifla_bond_ad_to_string = { - IFLA_BOND_AD_INFO_UNSPEC : 'IFLA_BOND_AD_INFO_UNSPEC', - IFLA_BOND_AD_INFO_AGGREGATOR : 'IFLA_BOND_AD_INFO_AGGREGATOR', - IFLA_BOND_AD_INFO_NUM_PORTS : 'IFLA_BOND_AD_INFO_NUM_PORTS', - IFLA_BOND_AD_INFO_ACTOR_KEY : 'IFLA_BOND_AD_INFO_ACTOR_KEY', - IFLA_BOND_AD_INFO_PARTNER_KEY : 'IFLA_BOND_AD_INFO_PARTNER_KEY', - IFLA_BOND_AD_INFO_PARTNER_MAC : 'IFLA_BOND_AD_INFO_PARTNER_MAC' - } - - ifla_bond_mode_tbl = { - 'balance-rr': 0, - 'active-backup': 1, - 'balance-xor': 2, - 'broadcast': 3, - '802.3ad': 4, - 'balance-tlb': 5, - 'balance-alb': 6, - '0': 0, - '1': 1, - '2': 2, - '3': 3, - '4': 4, - '5': 5, - '6': 6, - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6 - } - - ifla_bond_mode_pretty_tbl = { - 0: 'balance-rr', - 1: 'active-backup', - 2: 'balance-xor', - 3: 'broadcast', - 4: '802.3ad', - 5: 'balance-tlb', - 6: 'balance-alb' - } - - ifla_bond_xmit_hash_policy_tbl = { - 'layer2': 0, - 'layer3+4': 1, - 'layer2+3': 2, - 'encap2+3': 3, - 'encap3+4': 4, - '0': 0, - '1': 1, - '2': 2, - '3': 3, - '4': 4, - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4 - } - - ifla_bond_xmit_hash_policy_pretty_tbl = { - 0: 'layer2', - 1: 'layer3+4', - 2: 'layer2+3', - 3: 'encap2+3', - 4: 'encap3+4', - } - - # ========================================= - # IFLA_INFO_SLAVE_DATA attributes for bonds - # ========================================= - IFLA_BOND_SLAVE_UNSPEC = 0 - IFLA_BOND_SLAVE_STATE = 1 - IFLA_BOND_SLAVE_MII_STATUS = 2 - IFLA_BOND_SLAVE_LINK_FAILURE_COUNT = 3 - IFLA_BOND_SLAVE_PERM_HWADDR = 4 - IFLA_BOND_SLAVE_QUEUE_ID = 5 - IFLA_BOND_SLAVE_AD_AGGREGATOR_ID = 6 - IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE = 7 - IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE = 8 - IFLA_BOND_SLAVE_CL_START = 50 - IFLA_BOND_SLAVE_AD_RX_BYPASS = IFLA_BOND_SLAVE_CL_START - - ifla_bond_slave_to_string = { - IFLA_BOND_SLAVE_UNSPEC : 'IFLA_BOND_SLAVE_UNSPEC', - IFLA_BOND_SLAVE_STATE : 'IFLA_BOND_SLAVE_STATE', - IFLA_BOND_SLAVE_MII_STATUS : 'IFLA_BOND_SLAVE_MII_STATUS', - IFLA_BOND_SLAVE_LINK_FAILURE_COUNT : 'IFLA_BOND_SLAVE_LINK_FAILURE_COUNT', - IFLA_BOND_SLAVE_PERM_HWADDR : 'IFLA_BOND_SLAVE_PERM_HWADDR', - IFLA_BOND_SLAVE_QUEUE_ID : 'IFLA_BOND_SLAVE_QUEUE_ID', - IFLA_BOND_SLAVE_AD_AGGREGATOR_ID : 'IFLA_BOND_SLAVE_AD_AGGREGATOR_ID', - IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE : 'IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE', - IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE : 'IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE', - IFLA_BOND_SLAVE_CL_START : 'IFLA_BOND_SLAVE_CL_START', - IFLA_BOND_SLAVE_AD_RX_BYPASS : 'IFLA_BOND_SLAVE_AD_RX_BYPASS' - } - - # ========================================= - # IFLA_PROTINFO attributes for bridge ports - # ========================================= - IFLA_BRPORT_UNSPEC = 0 - IFLA_BRPORT_STATE = 1 - IFLA_BRPORT_PRIORITY = 2 - IFLA_BRPORT_COST = 3 - IFLA_BRPORT_MODE = 4 - IFLA_BRPORT_GUARD = 5 - IFLA_BRPORT_PROTECT = 6 - IFLA_BRPORT_FAST_LEAVE = 7 - IFLA_BRPORT_LEARNING = 8 - IFLA_BRPORT_UNICAST_FLOOD = 9 - IFLA_BRPORT_PROXYARP = 10 - IFLA_BRPORT_LEARNING_SYNC = 11 - IFLA_BRPORT_PROXYARP_WIFI = 12 - IFLA_BRPORT_ROOT_ID = 13 - IFLA_BRPORT_BRIDGE_ID = 14 - IFLA_BRPORT_DESIGNATED_PORT = 15 - IFLA_BRPORT_DESIGNATED_COST = 16 - IFLA_BRPORT_ID = 17 - IFLA_BRPORT_NO = 18 - IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19 - IFLA_BRPORT_CONFIG_PENDING = 20 - IFLA_BRPORT_MESSAGE_AGE_TIMER = 21 - IFLA_BRPORT_FORWARD_DELAY_TIMER = 22 - IFLA_BRPORT_HOLD_TIMER = 23 - IFLA_BRPORT_FLUSH = 24 - IFLA_BRPORT_MULTICAST_ROUTER = 25 - IFLA_BRPORT_PAD = 26 - IFLA_BRPORT_MCAST_FLOOD = 27 - IFLA_BRPORT_MCAST_TO_UCAST = 28 - IFLA_BRPORT_VLAN_TUNNEL = 29 - IFLA_BRPORT_BCAST_FLOOD = 30 - IFLA_BRPORT_GROUP_FWD_MASK = 31 - IFLA_BRPORT_ARP_SUPPRESS = 32 - IFLA_BRPORT_PEER_LINK = 150 - IFLA_BRPORT_DUAL_LINK = 151 - IFLA_BRPORT_GROUP_FWD_MASKHI = 153 - IFLA_BRPORT_DOWN_PEERLINK_REDIRECT = 154 - - ifla_brport_to_string = { - IFLA_BRPORT_UNSPEC : 'IFLA_BRPORT_UNSPEC', - IFLA_BRPORT_STATE : 'IFLA_BRPORT_STATE', - IFLA_BRPORT_PRIORITY : 'IFLA_BRPORT_PRIORITY', - IFLA_BRPORT_COST : 'IFLA_BRPORT_COST', - IFLA_BRPORT_MODE : 'IFLA_BRPORT_MODE', - IFLA_BRPORT_GUARD : 'IFLA_BRPORT_GUARD', - IFLA_BRPORT_PROTECT : 'IFLA_BRPORT_PROTECT', - IFLA_BRPORT_FAST_LEAVE : 'IFLA_BRPORT_FAST_LEAVE', - IFLA_BRPORT_LEARNING : 'IFLA_BRPORT_LEARNING', - IFLA_BRPORT_UNICAST_FLOOD : 'IFLA_BRPORT_UNICAST_FLOOD', - IFLA_BRPORT_PROXYARP : 'IFLA_BRPORT_PROXYARP', - IFLA_BRPORT_LEARNING_SYNC : 'IFLA_BRPORT_LEARNING_SYNC', - IFLA_BRPORT_PROXYARP_WIFI : 'IFLA_BRPORT_PROXYARP_WIFI', - IFLA_BRPORT_ROOT_ID : 'IFLA_BRPORT_ROOT_ID', - IFLA_BRPORT_BRIDGE_ID : 'IFLA_BRPORT_BRIDGE_ID', - IFLA_BRPORT_DESIGNATED_PORT : 'IFLA_BRPORT_DESIGNATED_PORT', - IFLA_BRPORT_DESIGNATED_COST : 'IFLA_BRPORT_DESIGNATED_COST', - IFLA_BRPORT_ID : 'IFLA_BRPORT_ID', - IFLA_BRPORT_NO : 'IFLA_BRPORT_NO', - IFLA_BRPORT_TOPOLOGY_CHANGE_ACK : 'IFLA_BRPORT_TOPOLOGY_CHANGE_ACK', - IFLA_BRPORT_CONFIG_PENDING : 'IFLA_BRPORT_CONFIG_PENDING', - IFLA_BRPORT_MESSAGE_AGE_TIMER : 'IFLA_BRPORT_MESSAGE_AGE_TIMER', - IFLA_BRPORT_FORWARD_DELAY_TIMER : 'IFLA_BRPORT_FORWARD_DELAY_TIMER', - IFLA_BRPORT_HOLD_TIMER : 'IFLA_BRPORT_HOLD_TIMER', - IFLA_BRPORT_FLUSH : 'IFLA_BRPORT_FLUSH', - IFLA_BRPORT_MULTICAST_ROUTER : 'IFLA_BRPORT_MULTICAST_ROUTER', - IFLA_BRPORT_PAD : 'IFLA_BRPORT_PAD', - IFLA_BRPORT_MCAST_FLOOD : 'IFLA_BRPORT_MCAST_FLOOD', - IFLA_BRPORT_MCAST_TO_UCAST : 'IFLA_BRPORT_MCAST_TO_UCAST', - IFLA_BRPORT_VLAN_TUNNEL : 'IFLA_BRPORT_VLAN_TUNNEL', - IFLA_BRPORT_BCAST_FLOOD : 'IFLA_BRPORT_BCAST_FLOOD', - IFLA_BRPORT_GROUP_FWD_MASK : 'IFLA_BRPORT_GROUP_FWD_MASK', - IFLA_BRPORT_PEER_LINK : 'IFLA_BRPORT_PEER_LINK', - IFLA_BRPORT_DUAL_LINK : 'IFLA_BRPORT_DUAL_LINK', - IFLA_BRPORT_ARP_SUPPRESS : 'IFLA_BRPORT_ARP_SUPPRESS', - IFLA_BRPORT_GROUP_FWD_MASKHI : 'IFLA_BRPORT_GROUP_FWD_MASKHI', - IFLA_BRPORT_DOWN_PEERLINK_REDIRECT : 'IFLA_BRPORT_DOWN_PEERLINK_REDIRECT' + ARPHRD_NETROM : 'ARPHRD_NETROM', + ARPHRD_ETHER : 'ARPHRD_ETHER', + ARPHRD_EETHER : 'ARPHRD_EETHER', + ARPHRD_AX25 : 'ARPHRD_AX25', + ARPHRD_PRONET : 'ARPHRD_PRONET', + ARPHRD_CHAOS : 'ARPHRD_CHAOS', + ARPHRD_IEEE802 : 'ARPHRD_IEEE802', + ARPHRD_ARCNET : 'ARPHRD_ARCNET', + ARPHRD_APPLETLK : 'ARPHRD_APPLETLK', + ARPHRD_DLCI : 'ARPHRD_DLCI', + ARPHRD_ATM : 'ARPHRD_ATM', + ARPHRD_METRICOM : 'ARPHRD_METRICOM', + ARPHRD_IEEE1394 : 'ARPHRD_IEEE1394', + ARPHRD_EUI64 : 'ARPHRD_EUI64', + ARPHRD_INFINIBAND : 'ARPHRD_INFINIBAND', + ARPHRD_SLIP : 'ARPHRD_SLIP', + ARPHRD_CSLIP : 'ARPHRD_CSLIP', + ARPHRD_SLIP6 : 'ARPHRD_SLIP6', + ARPHRD_CSLIP6 : 'ARPHRD_CSLIP6', + ARPHRD_RSRVD : 'ARPHRD_RSRVD', + ARPHRD_ADAPT : 'ARPHRD_ADAPT', + ARPHRD_ROSE : 'ARPHRD_ROSE', + ARPHRD_X25 : 'ARPHRD_X25', + ARPHRD_HWX25 : 'ARPHRD_HWX25', + ARPHRD_CAN : 'ARPHRD_CAN', + ARPHRD_PPP : 'ARPHRD_PPP', + ARPHRD_CISCO : 'ARPHRD_CISCO', + ARPHRD_HDLC : 'ARPHRD_HDLC', + ARPHRD_LAPB : 'ARPHRD_LAPB', + ARPHRD_DDCMP : 'ARPHRD_DDCMP', + ARPHRD_RAWHDLC : 'ARPHRD_RAWHDLC', + ARPHRD_TUNNEL : 'ARPHRD_TUNNEL', + ARPHRD_TUNNEL6 : 'ARPHRD_TUNNEL6', + ARPHRD_FRAD : 'ARPHRD_FRAD', + ARPHRD_SKIP : 'ARPHRD_SKIP', + ARPHRD_LOOPBACK : 'ARPHRD_LOOPBACK', + ARPHRD_LOCALTLK : 'ARPHRD_LOCALTLK', + ARPHRD_FDDI : 'ARPHRD_FDDI', + ARPHRD_BIF : 'ARPHRD_BIF', + ARPHRD_SIT : 'ARPHRD_SIT', + ARPHRD_IPDDP : 'ARPHRD_IPDDP', + ARPHRD_IPGRE : 'ARPHRD_IPGRE', + ARPHRD_PIMREG : 'ARPHRD_PIMREG', + ARPHRD_HIPPI : 'ARPHRD_HIPPI', + ARPHRD_ASH : 'ARPHRD_ASH', + ARPHRD_ECONET : 'ARPHRD_ECONET', + ARPHRD_IRDA : 'ARPHRD_IRDA', + ARPHRD_FCPP : 'ARPHRD_FCPP', + ARPHRD_FCAL : 'ARPHRD_FCAL', + ARPHRD_FCPL : 'ARPHRD_FCPL', + ARPHRD_FCFABRIC : 'ARPHRD_FCFABRIC', + ARPHRD_IEEE802_TR : 'ARPHRD_IEEE802_TR', + ARPHRD_IEEE80211 : 'ARPHRD_IEEE80211', + ARPHRD_IEEE80211_PRISM : 'ARPHRD_IEEE80211_PRISM', + ARPHRD_IEEE80211_RADIOTAP : 'ARPHRD_IEEE80211_RADIOTAP', + ARPHRD_IEEE802154 : 'ARPHRD_IEEE802154', + ARPHRD_PHONET : 'ARPHRD_PHONET', + ARPHRD_PHONET_PIPE : 'ARPHRD_PHONET_PIPE', + ARPHRD_CAIF : 'ARPHRD_CAIF', + ARPHRD_VOID : 'ARPHRD_VOID', + ARPHRD_NONE : 'ARPHRD_NONE' } # Subtype attributes for IFLA_AF_SPEC @@ -3615,298 +4266,66 @@ class Link(NetlinkPacket): IN6_ADDR_GEN_MODE_NONE: "none", IN6_ADDR_GEN_MODE_STABLE_PRIVACY: "stable_secret", IN6_ADDR_GEN_MODE_RANDOM: "random" - } - - # Subtype attrbutes AF_INET - IFLA_INET_UNSPEC = 0 - IFLA_INET_CONF = 1 - __IFLA_INET_MAX = 2 - - ifla_inet_af_spec_to_string = { - IFLA_INET_UNSPEC : 'IFLA_INET_UNSPEC', - IFLA_INET_CONF : 'IFLA_INET_CONF', - } - - # BRIDGE IFLA_AF_SPEC attributes - IFLA_BRIDGE_FLAGS = 0 - IFLA_BRIDGE_MODE = 1 - IFLA_BRIDGE_VLAN_INFO = 2 - - ifla_bridge_af_spec_to_string = { - IFLA_BRIDGE_FLAGS : 'IFLA_BRIDGE_FLAGS', - IFLA_BRIDGE_MODE : 'IFLA_BRIDGE_MODE', - IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO' - } - - # BRIDGE_VLAN_INFO flags - BRIDGE_VLAN_INFO_MASTER = 1 << 0 # Operate on Bridge device as well - BRIDGE_VLAN_INFO_PVID = 1 << 1 # VLAN is PVID, ingress untagged - BRIDGE_VLAN_INFO_UNTAGGED = 1 << 2 # VLAN egresses untagged - BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3 # VLAN is start of vlan range - BRIDGE_VLAN_INFO_RANGE_END = 1 << 4 # VLAN is end of vlan range - BRIDGE_VLAN_INFO_BRENTRY = 1 << 5 # Global bridge VLAN entry - - bridge_vlan_to_string = { - BRIDGE_VLAN_INFO_MASTER : 'BRIDGE_VLAN_INFO_MASTER', - BRIDGE_VLAN_INFO_PVID : 'BRIDGE_VLAN_INFO_PVID', - BRIDGE_VLAN_INFO_UNTAGGED : 'BRIDGE_VLAN_INFO_UNTAGGED', - BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN', - BRIDGE_VLAN_INFO_RANGE_END : 'BRIDGE_VLAN_INFO_RANGE_END', - BRIDGE_VLAN_INFO_BRENTRY : 'BRIDGE_VLAN_INFO_BRENTRY' - } - - # Bridge flags - BRIDGE_FLAGS_MASTER = 1 - BRIDGE_FLAGS_SELF = 2 - - bridge_flags_to_string = { - BRIDGE_FLAGS_MASTER : 'BRIDGE_FLAGS_MASTER', - BRIDGE_FLAGS_SELF : 'BRIDGE_FLAGS_SELF' - } - - # filters for IFLA_EXT_MASK - RTEXT_FILTER_VF = 1 << 0 - RTEXT_FILTER_BRVLAN = 1 << 1 - RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2 - RTEXT_FILTER_SKIP_STATS = 1 << 3 - - rtext_to_string = { - RTEXT_FILTER_VF : 'RTEXT_FILTER_VF', - RTEXT_FILTER_BRVLAN : 'RTEXT_FILTER_BRVLAN', - RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED', - RTEXT_FILTER_SKIP_STATS : 'RTEXT_FILTER_SKIP_STATS' - } - - IFLA_BR_UNSPEC = 0 - IFLA_BR_FORWARD_DELAY = 1 - IFLA_BR_HELLO_TIME = 2 - IFLA_BR_MAX_AGE = 3 - IFLA_BR_AGEING_TIME = 4 - IFLA_BR_STP_STATE = 5 - IFLA_BR_PRIORITY = 6 - IFLA_BR_VLAN_FILTERING = 7 - IFLA_BR_VLAN_PROTOCOL = 8 - IFLA_BR_GROUP_FWD_MASK = 9 - IFLA_BR_ROOT_ID = 10 - IFLA_BR_BRIDGE_ID = 11 - IFLA_BR_ROOT_PORT = 12 - IFLA_BR_ROOT_PATH_COST = 13 - IFLA_BR_TOPOLOGY_CHANGE = 14 - IFLA_BR_TOPOLOGY_CHANGE_DETECTED = 15 - IFLA_BR_HELLO_TIMER = 16 - IFLA_BR_TCN_TIMER = 17 - IFLA_BR_TOPOLOGY_CHANGE_TIMER = 18 - IFLA_BR_GC_TIMER = 19 - IFLA_BR_GROUP_ADDR = 20 - IFLA_BR_FDB_FLUSH = 21 - IFLA_BR_MCAST_ROUTER = 22 - IFLA_BR_MCAST_SNOOPING = 23 - IFLA_BR_MCAST_QUERY_USE_IFADDR = 24 - IFLA_BR_MCAST_QUERIER = 25 - IFLA_BR_MCAST_HASH_ELASTICITY = 26 - IFLA_BR_MCAST_HASH_MAX = 27 - IFLA_BR_MCAST_LAST_MEMBER_CNT = 28 - IFLA_BR_MCAST_STARTUP_QUERY_CNT = 29 - IFLA_BR_MCAST_LAST_MEMBER_INTVL = 30 - IFLA_BR_MCAST_MEMBERSHIP_INTVL = 31 - IFLA_BR_MCAST_QUERIER_INTVL = 32 - IFLA_BR_MCAST_QUERY_INTVL = 33 - IFLA_BR_MCAST_QUERY_RESPONSE_INTVL = 34 - IFLA_BR_MCAST_STARTUP_QUERY_INTVL = 35 - IFLA_BR_NF_CALL_IPTABLES = 36 - IFLA_BR_NF_CALL_IP6TABLES = 37 - IFLA_BR_NF_CALL_ARPTABLES = 38 - IFLA_BR_VLAN_DEFAULT_PVID = 39 - IFLA_BR_PAD = 40 - IFLA_BR_VLAN_STATS_ENABLED = 41 - IFLA_BR_MCAST_STATS_ENABLED = 42 - IFLA_BR_MCAST_IGMP_VERSION = 43 - IFLA_BR_MCAST_MLD_VERSION = 44 - - ifla_br_to_string = { - IFLA_BR_UNSPEC : 'IFLA_BR_UNSPEC', - IFLA_BR_FORWARD_DELAY : 'IFLA_BR_FORWARD_DELAY', - IFLA_BR_HELLO_TIME : 'IFLA_BR_HELLO_TIME', - IFLA_BR_MAX_AGE : 'IFLA_BR_MAX_AGE', - IFLA_BR_AGEING_TIME : 'IFLA_BR_AGEING_TIME', - IFLA_BR_STP_STATE : 'IFLA_BR_STP_STATE', - IFLA_BR_PRIORITY : 'IFLA_BR_PRIORITY', - IFLA_BR_VLAN_FILTERING : 'IFLA_BR_VLAN_FILTERING', - IFLA_BR_VLAN_PROTOCOL : 'IFLA_BR_VLAN_PROTOCOL', - IFLA_BR_GROUP_FWD_MASK : 'IFLA_BR_GROUP_FWD_MASK', - IFLA_BR_ROOT_ID : 'IFLA_BR_ROOT_ID', - IFLA_BR_BRIDGE_ID : 'IFLA_BR_BRIDGE_ID', - IFLA_BR_ROOT_PORT : 'IFLA_BR_ROOT_PORT', - IFLA_BR_ROOT_PATH_COST : 'IFLA_BR_ROOT_PATH_COST', - IFLA_BR_TOPOLOGY_CHANGE : 'IFLA_BR_TOPOLOGY_CHANGE', - IFLA_BR_TOPOLOGY_CHANGE_DETECTED : 'IFLA_BR_TOPOLOGY_CHANGE_DETECTED', - IFLA_BR_HELLO_TIMER : 'IFLA_BR_HELLO_TIMER', - IFLA_BR_TCN_TIMER : 'IFLA_BR_TCN_TIMER', - IFLA_BR_TOPOLOGY_CHANGE_TIMER : 'IFLA_BR_TOPOLOGY_CHANGE_TIMER', - IFLA_BR_GC_TIMER : 'IFLA_BR_GC_TIMER', - IFLA_BR_GROUP_ADDR : 'IFLA_BR_GROUP_ADDR', - IFLA_BR_FDB_FLUSH : 'IFLA_BR_FDB_FLUSH', - IFLA_BR_MCAST_ROUTER : 'IFLA_BR_MCAST_ROUTER', - IFLA_BR_MCAST_SNOOPING : 'IFLA_BR_MCAST_SNOOPING', - IFLA_BR_MCAST_QUERY_USE_IFADDR : 'IFLA_BR_MCAST_QUERY_USE_IFADDR', - IFLA_BR_MCAST_QUERIER : 'IFLA_BR_MCAST_QUERIER', - IFLA_BR_MCAST_HASH_ELASTICITY : 'IFLA_BR_MCAST_HASH_ELASTICITY', - IFLA_BR_MCAST_HASH_MAX : 'IFLA_BR_MCAST_HASH_MAX', - IFLA_BR_MCAST_LAST_MEMBER_CNT : 'IFLA_BR_MCAST_LAST_MEMBER_CNT', - IFLA_BR_MCAST_STARTUP_QUERY_CNT : 'IFLA_BR_MCAST_STARTUP_QUERY_CNT', - IFLA_BR_MCAST_LAST_MEMBER_INTVL : 'IFLA_BR_MCAST_LAST_MEMBER_INTVL', - IFLA_BR_MCAST_MEMBERSHIP_INTVL : 'IFLA_BR_MCAST_MEMBERSHIP_INTVL', - IFLA_BR_MCAST_QUERIER_INTVL : 'IFLA_BR_MCAST_QUERIER_INTVL', - IFLA_BR_MCAST_QUERY_INTVL : 'IFLA_BR_MCAST_QUERY_INTVL', - IFLA_BR_MCAST_QUERY_RESPONSE_INTVL : 'IFLA_BR_MCAST_QUERY_RESPONSE_INTVL', - IFLA_BR_MCAST_STARTUP_QUERY_INTVL : 'IFLA_BR_MCAST_STARTUP_QUERY_INTVL', - IFLA_BR_NF_CALL_IPTABLES : 'IFLA_BR_NF_CALL_IPTABLES', - IFLA_BR_NF_CALL_IP6TABLES : 'IFLA_BR_NF_CALL_IP6TABLES', - IFLA_BR_NF_CALL_ARPTABLES : 'IFLA_BR_NF_CALL_ARPTABLES', - IFLA_BR_VLAN_DEFAULT_PVID : 'IFLA_BR_VLAN_DEFAULT_PVID', - IFLA_BR_PAD : 'IFLA_BR_PAD', - IFLA_BR_VLAN_STATS_ENABLED : 'IFLA_BR_VLAN_STATS_ENABLED', - IFLA_BR_MCAST_STATS_ENABLED : 'IFLA_BR_MCAST_STATS_ENABLED', - IFLA_BR_MCAST_IGMP_VERSION : 'IFLA_BR_MCAST_IGMP_VERSION', - IFLA_BR_MCAST_MLD_VERSION : 'IFLA_BR_MCAST_MLD_VERSION' - } - - # ========================================= - # IFLA_INFO_DATA attributes for vrfs - # ========================================= - IFLA_VRF_UNSPEC = 0 - IFLA_VRF_TABLE = 1 - - ifla_vrf_to_string = { - IFLA_VRF_UNSPEC : 'IFLA_VRF_UNSPEC', - IFLA_VRF_TABLE : 'IFLA_VRF_TABLE' - } - - # ================================================================ - # IFLA_INFO_DATA attributes for (ip6)gre, (ip6)gretap, (ip6)erspan - # ================================================================ - IFLA_GRE_UNSPEC = 0 - IFLA_GRE_LINK = 1 - IFLA_GRE_IFLAGS = 2 - IFLA_GRE_OFLAGS = 3 - IFLA_GRE_IKEY = 4 - IFLA_GRE_OKEY = 5 - IFLA_GRE_LOCAL = 6 - IFLA_GRE_REMOTE = 7 - IFLA_GRE_TTL = 8 - IFLA_GRE_TOS = 9 - IFLA_GRE_PMTUDISC = 10 - IFLA_GRE_ENCAP_LIMIT = 11 - IFLA_GRE_FLOWINFO = 12 - IFLA_GRE_FLAGS = 13 - IFLA_GRE_ENCAP_TYPE = 14 - IFLA_GRE_ENCAP_FLAGS = 15 - IFLA_GRE_ENCAP_SPORT = 16 - IFLA_GRE_ENCAP_DPORT = 17 - IFLA_GRE_COLLECT_METADATA = 18 - IFLA_GRE_IGNORE_DF = 19 - IFLA_GRE_FWMARK = 20 - IFLA_GRE_ERSPAN_INDEX = 21 - IFLA_GRE_ERSPAN_VER = 22 - IFLA_GRE_ERSPAN_DIR = 23 - IFLA_GRE_ERSPAN_HWID = 24 + } - ifla_gre_to_string = { - IFLA_GRE_UNSPEC : "IFLA_GRE_UNSPEC", - IFLA_GRE_LINK : "IFLA_GRE_LINK", - IFLA_GRE_IFLAGS : "IFLA_GRE_IFLAGS", - IFLA_GRE_OFLAGS : "IFLA_GRE_OFLAGS", - IFLA_GRE_IKEY : "IFLA_GRE_IKEY", - IFLA_GRE_OKEY : "IFLA_GRE_OKEY", - IFLA_GRE_LOCAL : "IFLA_GRE_LOCAL", - IFLA_GRE_REMOTE : "IFLA_GRE_REMOTE", - IFLA_GRE_TTL : "IFLA_GRE_TTL", - IFLA_GRE_TOS : "IFLA_GRE_TOS", - IFLA_GRE_PMTUDISC : "IFLA_GRE_PMTUDISC", - IFLA_GRE_ENCAP_LIMIT : "IFLA_GRE_ENCAP_LIMIT", - IFLA_GRE_FLOWINFO : "IFLA_GRE_FLOWINFO", - IFLA_GRE_FLAGS : "IFLA_GRE_FLAGS", - IFLA_GRE_ENCAP_TYPE : "IFLA_GRE_ENCAP_TYPE", - IFLA_GRE_ENCAP_FLAGS : "IFLA_GRE_ENCAP_FLAGS", - IFLA_GRE_ENCAP_SPORT : "IFLA_GRE_ENCAP_SPORT", - IFLA_GRE_ENCAP_DPORT : "IFLA_GRE_ENCAP_DPORT", - IFLA_GRE_COLLECT_METADATA : "IFLA_GRE_COLLECT_METADATA", - IFLA_GRE_IGNORE_DF : "IFLA_GRE_IGNORE_DF", - IFLA_GRE_FWMARK : "IFLA_GRE_FWMARK", - IFLA_GRE_ERSPAN_INDEX : "IFLA_GRE_ERSPAN_INDEX", - IFLA_GRE_ERSPAN_VER : "IFLA_GRE_ERSPAN_VER", - IFLA_GRE_ERSPAN_DIR : "IFLA_GRE_ERSPAN_DIR", - IFLA_GRE_ERSPAN_HWID : "IFLA_GRE_ERSPAN_HWID", + # Subtype attrbutes AF_INET + IFLA_INET_UNSPEC = 0 + IFLA_INET_CONF = 1 + __IFLA_INET_MAX = 2 + + ifla_inet_af_spec_to_string = { + IFLA_INET_UNSPEC : 'IFLA_INET_UNSPEC', + IFLA_INET_CONF : 'IFLA_INET_CONF', } - # =============================================== - # IFLA_INFO_DATA attributes for ipip, sit, ip6tnl - # =============================================== - IFLA_IPTUN_UNSPEC = 0 - IFLA_IPTUN_LINK = 1 - IFLA_IPTUN_LOCAL = 2 - IFLA_IPTUN_REMOTE = 3 - IFLA_IPTUN_TTL = 4 - IFLA_IPTUN_TOS = 5 - IFLA_IPTUN_ENCAP_LIMIT = 6 - IFLA_IPTUN_FLOWINFO = 7 - IFLA_IPTUN_FLAGS = 8 - IFLA_IPTUN_PROTO = 9 - IFLA_IPTUN_PMTUDISC = 10 - IFLA_IPTUN_6RD_PREFIX = 11 - IFLA_IPTUN_6RD_RELAY_PREFIX = 12 - IFLA_IPTUN_6RD_PREFIXLEN = 13 - IFLA_IPTUN_6RD_RELAY_PREFIXLEN = 14 - IFLA_IPTUN_ENCAP_TYPE = 15 - IFLA_IPTUN_ENCAP_FLAGS = 16 - IFLA_IPTUN_ENCAP_SPORT = 17 - IFLA_IPTUN_ENCAP_DPORT = 18 - IFLA_IPTUN_COLLECT_METADATA = 19 - IFLA_IPTUN_FWMARK = 20 + # BRIDGE IFLA_AF_SPEC attributes + IFLA_BRIDGE_FLAGS = 0 + IFLA_BRIDGE_MODE = 1 + IFLA_BRIDGE_VLAN_INFO = 2 - ifla_iptun_to_string = { - IFLA_IPTUN_UNSPEC : "IFLA_IPTUN_UNSPEC", - IFLA_IPTUN_LINK : "IFLA_IPTUN_LINK", - IFLA_IPTUN_LOCAL : "IFLA_IPTUN_LOCAL", - IFLA_IPTUN_REMOTE : "IFLA_IPTUN_REMOTE", - IFLA_IPTUN_TTL : "IFLA_IPTUN_TTL", - IFLA_IPTUN_TOS : "IFLA_IPTUN_TOS", - IFLA_IPTUN_ENCAP_LIMIT : "IFLA_IPTUN_ENCAP_LIMIT", - IFLA_IPTUN_FLOWINFO : "IFLA_IPTUN_FLOWINFO", - IFLA_IPTUN_FLAGS : "IFLA_IPTUN_FLAGS", - IFLA_IPTUN_PROTO : "IFLA_IPTUN_PROTO", - IFLA_IPTUN_PMTUDISC : "IFLA_IPTUN_PMTUDISC", - IFLA_IPTUN_6RD_PREFIX : "IFLA_IPTUN_6RD_PREFIX", - IFLA_IPTUN_6RD_RELAY_PREFIX : "IFLA_IPTUN_6RD_RELAY_PREFIX", - IFLA_IPTUN_6RD_PREFIXLEN : "IFLA_IPTUN_6RD_PREFIXLEN", - IFLA_IPTUN_6RD_RELAY_PREFIXLEN : "IFLA_IPTUN_6RD_RELAY_PREFIXLEN", - IFLA_IPTUN_ENCAP_TYPE : "IFLA_IPTUN_ENCAP_TYPE", - IFLA_IPTUN_ENCAP_FLAGS : "IFLA_IPTUN_ENCAP_FLAGS", - IFLA_IPTUN_ENCAP_SPORT : "IFLA_IPTUN_ENCAP_SPORT", - IFLA_IPTUN_ENCAP_DPORT : "IFLA_IPTUN_ENCAP_DPORT", - IFLA_IPTUN_COLLECT_METADATA : "IFLA_IPTUN_COLLECT_METADATA", - IFLA_IPTUN_FWMARK : "IFLA_IPTUN_FWMARK", + ifla_bridge_af_spec_to_string = { + IFLA_BRIDGE_FLAGS : 'IFLA_BRIDGE_FLAGS', + IFLA_BRIDGE_MODE : 'IFLA_BRIDGE_MODE', + IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO' } - # ========================================= - # IFLA_INFO_DATA attributes for vti, vti6 - # ========================================= - IFLA_VTI_UNSPEC = 0 - IFLA_VTI_LINK = 1 - IFLA_VTI_IKEY = 2 - IFLA_VTI_OKEY = 3 - IFLA_VTI_LOCAL = 4 - IFLA_VTI_REMOTE = 5 - IFLA_VTI_FWMARK = 6 + # BRIDGE_VLAN_INFO flags + BRIDGE_VLAN_INFO_MASTER = 1 << 0 # Operate on Bridge device as well + BRIDGE_VLAN_INFO_PVID = 1 << 1 # VLAN is PVID, ingress untagged + BRIDGE_VLAN_INFO_UNTAGGED = 1 << 2 # VLAN egresses untagged + BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3 # VLAN is start of vlan range + BRIDGE_VLAN_INFO_RANGE_END = 1 << 4 # VLAN is end of vlan range + BRIDGE_VLAN_INFO_BRENTRY = 1 << 5 # Global bridge VLAN entry - ifla_vti_to_string = { - IFLA_VTI_UNSPEC : "IFLA_VTI_UNSPEC", - IFLA_VTI_LINK : "IFLA_VTI_LINK", - IFLA_VTI_IKEY : "IFLA_VTI_IKEY", - IFLA_VTI_OKEY : "IFLA_VTI_OKEY", - IFLA_VTI_LOCAL : "IFLA_VTI_LOCAL", - IFLA_VTI_REMOTE : "IFLA_VTI_REMOTE", - IFLA_VTI_FWMARK : "IFLA_VTI_FWMARK", + bridge_vlan_to_string = { + BRIDGE_VLAN_INFO_MASTER : 'BRIDGE_VLAN_INFO_MASTER', + BRIDGE_VLAN_INFO_PVID : 'BRIDGE_VLAN_INFO_PVID', + BRIDGE_VLAN_INFO_UNTAGGED : 'BRIDGE_VLAN_INFO_UNTAGGED', + BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN', + BRIDGE_VLAN_INFO_RANGE_END : 'BRIDGE_VLAN_INFO_RANGE_END', + BRIDGE_VLAN_INFO_BRENTRY : 'BRIDGE_VLAN_INFO_BRENTRY' + } + + # Bridge flags + BRIDGE_FLAGS_MASTER = 1 + BRIDGE_FLAGS_SELF = 2 + + bridge_flags_to_string = { + BRIDGE_FLAGS_MASTER : 'BRIDGE_FLAGS_MASTER', + BRIDGE_FLAGS_SELF : 'BRIDGE_FLAGS_SELF' + } + + # filters for IFLA_EXT_MASK + RTEXT_FILTER_VF = 1 << 0 + RTEXT_FILTER_BRVLAN = 1 << 1 + RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2 + RTEXT_FILTER_SKIP_STATS = 1 << 3 + + rtext_to_string = { + RTEXT_FILTER_VF : 'RTEXT_FILTER_VF', + RTEXT_FILTER_BRVLAN : 'RTEXT_FILTER_BRVLAN', + RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED', + RTEXT_FILTER_SKIP_STATS : 'RTEXT_FILTER_SKIP_STATS' } def __init__(self, msgtype, debug=False, logger=None, use_color=True): @@ -3935,12 +4354,15 @@ def get_ifla_vlan_string(self, index): def get_ifla_vxlan_string(self, index): return self.get_string(self.ifla_vxlan_to_string, index) + def get_ifla_vrf_string(self, index): + return self.get_string(self.ifla_vrf_to_string, index) + + def get_ifla_bond_slave_string(self, index): + return self.get_string(self.ifla_bond_slave_to_string, index) + def get_ifla_macvlan_string(self, index): return self.get_string(self.ifla_macvlan_to_string, index) - def get_ifla_xfrm_string(self, index): - return self.get_string(self.ifla_xfrm_to_string, index) - def get_macvlan_mode_string(self, index): return self.get_string(self.macvlan_mode_to_string, index) @@ -4014,6 +4436,386 @@ def is_up(self): return False +class AttributeMDBA_MDB(Attribute): + """ + /* Bridge multicast database attributes + * [MDBA_MDB] = { + * [MDBA_MDB_ENTRY] = { + * [MDBA_MDB_ENTRY_INFO] { + * struct br_mdb_entry + * [MDBA_MDB_EATTR attributes] + * } + * } + * } + """ + """ + Current we support only MDB Dump and no MDB_GET. + The code has been written to handle multiple entries in a single msg. + data -- alignment + MDBA_MDB ===> data[0:4] + MDBA_MDB_ENTRY ===> data[4:8] + MDBA_MDB_ENTRY_INFO ===> data[8:12] + br_mdb_entry -- ===> ifindex data[12:16] + -- ===> state,flags,vide data[16:20] + -- ===> ip_addr data[20:36] + -- ===> proto data[36:40] + -- ===> MDB_MDB_EATTR_TIMER data[40:44] + -- Timer Value data[44:48] + """ + + def __init__(self, atype, string, family, logger): + Attribute.__init__(self, atype, string, logger) + + def decode(self, parent_msg, data): + self.decode_length_type(data) + + data = self.data[4:] + if parent_msg.msgtype == RTM_GETMDB: + self.value = [] + while data: + (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4]) + sub_attr_end = padded_length(sub_attr_length) + sub_attr_data = data[4:sub_attr_end] + + mdb_entry = {} + mdb_entry[MDB.MDBA_MDB_ENTRY] = [] + if not sub_attr_length: + self.log.error('parsed a zero length sub-attr') + return + + if sub_attr_type == MDB.MDBA_MDB_ENTRY: + while sub_attr_data: + nested_mdb_entry = {} + (nested_attr_length,nested_attr_type) = unpack('=HH',sub_attr_data[:4]) + nested_attr_end = padded_length(nested_attr_length) + if nested_attr_type == MDB.MDBA_MDB_ENTRY_INFO: + (ifindex, state, flags, vid) = unpack('=LBBH',sub_attr_data[4:12]) + info = [ifindex,state,flags,vid] + proto = unpack('=H',sub_attr_data[28:30])[0] + if proto == htons(ETH_P_IP): + ip_addr = IPv4Address(unpack('>L', sub_attr_data[12:16])[0]) + else: + (data1, data2) = unpack('>QQ',sub_attr_data[12:28]) + ip_addr = IPv6Address(data1 << 64 | data2) + + info.append(ip_addr) + + try: + (timer_attr_length,timer_attr_type) = unpack('=HH',sub_attr_data[32:36]) + if(timer_attr_type ) == MDB.MDBA_MDB_EATTR_TIMER: + info.append({MDB.MDBA_MDB_EATTR_TIMER: (unpack('=I',sub_attr_data[36:40])[0])*0.01}) + except struct.error: + self.log.error('No TimerAttribute') + nested_mdb_entry[MDB.MDBA_MDB_ENTRY] = info + mdb_entry[MDB.MDBA_MDB_ENTRY].append(nested_mdb_entry) + sub_attr_data = sub_attr_data[nested_attr_end:] + self.value.append(mdb_entry) + data = data[sub_attr_end:] + + else: + self.value = {} + while data: + (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4]) + sub_attr_end = padded_length(sub_attr_length) + sub_attr_data = data[4:] + if not sub_attr_length: + self.log.error('parsed a zero length sub-attr') + return + + if sub_attr_type == MDB.MDBA_MDB_ENTRY: + (nested_attr_length,nested_attr_type) = unpack('=HH',sub_attr_data[:4]) + if nested_attr_type == MDB.MDBA_MDB_ENTRY_INFO: + self.value[MDB.MDBA_MDB_ENTRY] = {} + (ifindex,state,flags,vid) = unpack('=LBBH',sub_attr_data[4:12]) + info = (ifindex,state,flags,vid) + info = list(info) + proto = unpack('=H',sub_attr_data[28:30])[0] + if proto == 8: + ip_addr = IPv4Address(unpack('>L', sub_attr_data[12:16])[0]) + else: + (data1, data2) = unpack('>QQ',sub_attr_data[12:28]) + ip_addr = IPv6Address(data1 << 64 | data2) + + info.append(ip_addr) + self.value[MDB.MDBA_MDB_ENTRY][MDB.MDBA_MDB_ENTRY_INFO] = info + data = data[sub_attr_end:] + + def dump_lines(self, dump_buffer, line_number, color): + line_number = self.dump_first_line(dump_buffer, line_number, color) + + dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], self.value)) + return line_number + 1 + + + +class AttributeMDBA_ROUTER(Attribute): + """ + /* + * [MDBA_ROUTER] = { + * [MDBA_ROUTER_PORT] = { + * u32 ifindex + * [MDBA_ROUTER_PATTR attributes] + * } + * } + */ + """ + + def __init__(self, atype, string, family, logger): + Attribute.__init__(self, atype, string, logger) + + def decode(self, parent_msg, data): + self.decode_length_type(data) + data = self.data[4:] + if parent_msg.msgtype == RTM_GETMDB: + self.value = [] + while data: + (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4]) + sub_attr_end = padded_length(sub_attr_length) + sub_attr_data = data[4:sub_attr_end] + router_entry = {} + + if not sub_attr_length: + self.log.error('parsed a zero length sub-attr') + return + + if sub_attr_type == MDB.MDBA_ROUTER_PORT: + ifindex = unpack('=I',sub_attr_data[:4])[0] + sub_attr_data = sub_attr_data[4:] + timer_info = {} + type_info = {} + while sub_attr_data: + (nested_sub_attr_length, nested_sub_attr_type) = unpack('=HH',sub_attr_data[:4]) + nested_sub_attr_data = sub_attr_data[4:] + nested_sub_attr_end = padded_length(nested_sub_attr_length) + if nested_sub_attr_type == MDB.MDBA_ROUTER_PATTR_TIMER: + timer_info[MDB.MDBA_ROUTER_PATTR_TIMER] = (unpack('=L',nested_sub_attr_data[ :4])[0])*0.01 + elif nested_sub_attr_type == MDB.MDBA_ROUTER_PATTR_TYPE: + type_info[MDB.MDBA_ROUTER_PATTR_TYPE] = unpack('=B',nested_sub_attr_data[:1])[0] + else: + raise Exception("Invalid Router Port Attribute") + sub_attr_data = sub_attr_data[nested_sub_attr_end:] + router_entry[MDB.MDBA_ROUTER_PORT] = [ifindex,timer_info,type_info] + + self.value.append(router_entry) + data = data[sub_attr_end:] + + else: + self.value = {} + while data: + (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4]) + sub_attr_end = padded_length(sub_attr_length) + sub_attr_data = data[4:] + + if not sub_attr_length: + self.log.error('parsed a zero length sub-attr') + return + + if sub_attr_type == MDB.MDBA_ROUTER_PORT: + ifindex = unpack('=L',sub_attr_data[:4])[0] + self.value[MDB.MDBA_ROUTER_PORT] = ifindex + data = data[sub_attr_end:] + + def dump_lines(self, dump_buffer, line_number, color): + line_number = self.dump_first_line(dump_buffer, line_number, color) + + dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], self.value)) + return line_number + 1 + + +class AttributeMDBA_SET_ENTRY(Attribute): + + def __init__(self, atype, string, family, logger): + Attribute.__init__(self, atype, string, logger) + self.PACK = None + self.LEN = None + + def set_value(self, value): + self.value = value + + + def encode(self): + if self.value: + (ifindex, flags, state,vid, ip, proto) = self.value + if proto == htons(ETH_P_IP): + self.PACK = '=IBBHLxxxxxxxxxxxxHxx' + reorder = unpack(' 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) diff --git a/ifupdown2/sbin/start-networking b/ifupdown2/sbin/start-networking deleted file mode 100755 index da6f9d03..00000000 --- a/ifupdown2/sbin/start-networking +++ /dev/null @@ -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 diff --git a/setup.py b/setup.py index c3d61b3f..c25e0a16 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,6 @@ ] 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 @@ 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 @@ def build_deb_package(): } 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 @@ def build_deb_package(): 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,