mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
ifupdown2 2.0.0-1
Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,7 +16,7 @@ dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
/lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
20
debian/changelog
vendored
20
debian/changelog
vendored
@@ -1,8 +1,22 @@
|
||||
ifupdown2 (1.2.9-1) unstable; urgency=medium
|
||||
ifupdown2 (2.0.0-1) unstable; urgency=medium
|
||||
|
||||
*
|
||||
* Introduction of the live netlink cache
|
||||
* Refactoring and PEP8 fixes
|
||||
* Install systemd ifupdown2.netowrking.service and ifup.service
|
||||
* Addons: bond: bond-primary attributes (closes: #9)
|
||||
* Addons: address-virtual: vrrp support
|
||||
* Addons: address: add arp-accept option
|
||||
* Addons: tunnel: adding "tunnel-" prefix to every attributes
|
||||
* Loopback interface won't go down (unless link-down yes)
|
||||
* Macvlans (address-virtual) can now be configured without ips
|
||||
* Add support for vxlan multicast group (vxlan-mcastgrp)
|
||||
* New sets of poliicies:
|
||||
- bridge polcy for vxlan port: bridge-vxlan-arp-nd-suppres (ON/off)
|
||||
- bridge policy for vxlan port: bridge_vxlan_port_learning (ON/off)
|
||||
- bridge policy for vxlan port:
|
||||
vxlan_bridge_igmp_snooping_enable_port_mcrouter (1/0)
|
||||
|
||||
-- Julien Fortin <julien@cumulusnetworks.com> Thu, 11 Jul 2019 23:42:42 +0200
|
||||
-- Julien Fortin <julien@cumulusnetworks.com> Tue, 01 Oct 2019 23:42:42 +0200
|
||||
|
||||
ifupdown2 (1.2.8-1) unstable; urgency=medium
|
||||
|
||||
|
6
debian/control
vendored
6
debian/control
vendored
@@ -2,13 +2,15 @@ Source: ifupdown2
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: Julien Fortin <julien@cumulusnetworks.com>
|
||||
Build-Depends: debhelper (>= 9.20160709),
|
||||
Build-Depends: debhelper (>=9),
|
||||
dh-systemd,
|
||||
dh-python,
|
||||
python-all,
|
||||
python-setuptools,
|
||||
python-docutils
|
||||
Standards-Version: 4.2.1
|
||||
Homepage: https://github.com/cumulusnetworks/ifupdown2
|
||||
X-Python-Version: >= 2.7
|
||||
|
||||
Package: ifupdown2
|
||||
Architecture: all
|
||||
@@ -16,7 +18,7 @@ Provides: ifupdown
|
||||
Conflicts: ifupdown
|
||||
Replaces: ifupdown
|
||||
Depends: ${python:Depends}, ${misc:Depends}, iproute2, python-argcomplete, python-ipaddr
|
||||
Suggests: isc-dhcp-client, bridge-utils, ethtool, python-gvgen, python-mako, python-pkg-resources
|
||||
Suggests: isc-dhcp-client, bridge-utils, ethtool, python-gvgen, python-mako
|
||||
Description: Network Interface Management tool similar to ifupdown
|
||||
ifupdown2 is ifupdown re-written in Python. It replaces ifupdown and provides
|
||||
the same user interface as ifupdown for network interface configuration.
|
||||
|
17
debian/ifup@.service
vendored
Normal file
17
debian/ifup@.service
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=ifup for %I
|
||||
After=local-fs.target network-pre.target networking.service systemd-sysctl.service
|
||||
Before=network.target shutdown.target network-online.target
|
||||
Conflicts=shutdown.target
|
||||
BindsTo=sys-subsystem-net-devices-%i.device
|
||||
After=sys-subsystem-net-devices-%i.device
|
||||
DefaultDependencies=no
|
||||
IgnoreOnIsolate=yes
|
||||
|
||||
[Service]
|
||||
# avoid stopping on shutdown via stopping system-ifup.slice
|
||||
Slice=system.slice
|
||||
ExecStart=/sbin/ifup --allow=hotplug %I
|
||||
ExecStop=/sbin/ifdown %I
|
||||
RemainAfterExit=true
|
||||
TimeoutStartSec=2min
|
1
debian/ifupdown2.install
vendored
1
debian/ifupdown2.install
vendored
@@ -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/
|
||||
|
8
debian/ifupdown2.networking.service
vendored
8
debian/ifupdown2.networking.service
vendored
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=ifupdown2 networking initialization
|
||||
Description=Network initialization
|
||||
Documentation=man:interfaces(5) man:ifup(8) man:ifdown(8)
|
||||
DefaultDependencies=no
|
||||
Before=shutdown.target
|
||||
@@ -10,9 +10,9 @@ Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
SyslogIdentifier=networking
|
||||
TimeoutStopSec=30s
|
||||
ExecStart=/usr/share/ifupdown2/sbin/start-networking start
|
||||
ExecStop=/usr/share/ifupdown2/sbin/start-networking stop
|
||||
ExecReload=/usr/share/ifupdown2/sbin/start-networking reload
|
||||
ExecStart=/sbin/ifup -a
|
||||
ExecStop=/sbin/ifdown -a
|
||||
ExecReload=/sbin/ifreload -a
|
||||
|
||||
[Install]
|
||||
WantedBy=basic.target network.target shutdown.target
|
||||
|
9
debian/rules
vendored
9
debian/rules
vendored
@@ -11,14 +11,17 @@ override_dh_installman:
|
||||
./ifupdown2/man/genmanpages.sh ./ifupdown2/man ./man
|
||||
dh_installman
|
||||
|
||||
override_dh_install:
|
||||
dh_install
|
||||
mkdir -p debian/ifupdown2/lib/systemd/system/
|
||||
install --mode=644 debian/ifup@.service debian/ifupdown2/lib/systemd/system/
|
||||
|
||||
|
||||
override_dh_systemd_start:
|
||||
dh_systemd_start --name=networking --no-start
|
||||
|
||||
override_dh_systemd_enable:
|
||||
dh_systemd_enable --name=networking
|
||||
|
||||
override_dh_installinit:
|
||||
dh_installinit --name=networking --no-start
|
||||
|
||||
override_dh_compress:
|
||||
dh_compress -X.py
|
||||
|
1
debian/watch
vendored
Normal file
1
debian/watch
vendored
Normal file
@@ -0,0 +1 @@
|
||||
version=3
|
@@ -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"
|
@@ -12,7 +12,9 @@ pre-up,bridgevlan
|
||||
pre-up,mstpctl
|
||||
pre-up,tunnel
|
||||
pre-up,vrf
|
||||
pre-up,tunnel
|
||||
pre-up,ethtool
|
||||
pre-up,address
|
||||
up,dhcp
|
||||
up,address
|
||||
up,addressvirtual
|
||||
@@ -40,4 +42,4 @@ post-down,batman_adv
|
||||
post-down,usercmds
|
||||
post-down,link
|
||||
post-down,tunnel
|
||||
post-down,xfrm
|
||||
post-down,xfrm
|
||||
|
@@ -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/
|
||||
|
@@ -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
|
||||
|
@@ -1,205 +1,128 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (C) 2016, 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# Copyright 2016 Cumulus Networks, Inc. All rights reserved.
|
||||
# Author: Julien Fortin, julien@cumulusnetworks.com
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# ifupdown2 - Network Manager
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import errno
|
||||
import struct
|
||||
import select
|
||||
import socket
|
||||
import signal
|
||||
|
||||
try:
|
||||
import ifupdown2.ifupdown.config as core_config
|
||||
from ifupdown2.ifupdown.log import log
|
||||
from ifupdown2.lib.log import LogManager, root_logger
|
||||
from ifupdown2.lib.status import Status
|
||||
except:
|
||||
from lib.log import LogManager, root_logger
|
||||
from lib.status import Status
|
||||
|
||||
# first thing first, setup the logging infra
|
||||
LogManager.get_instance()
|
||||
|
||||
try:
|
||||
import ifupdown2.ifupdown.config as config
|
||||
|
||||
from ifupdown2 import __version__
|
||||
|
||||
core_config.__version__ = __version__
|
||||
config.__version__ = __version__
|
||||
|
||||
from ifupdown2.lib.exceptions import ExitWithStatus, ExitWithStatusAndError
|
||||
|
||||
from ifupdown2.ifupdown.client import Client
|
||||
from ifupdown2.ifupdown.exceptions import ArgvParseHelp
|
||||
except:
|
||||
import ifupdown.config as core_config
|
||||
from ifupdown.log import log
|
||||
import ifupdown.config as config
|
||||
|
||||
core_config.__version__ = __import__('__init__').__version__
|
||||
config.__version__ = __import__("__init__").__version__
|
||||
|
||||
from lib.exceptions import ExitWithStatus, ExitWithStatusAndError
|
||||
|
||||
from ifupdown.client import Client
|
||||
from ifupdown.exceptions import ArgvParseHelp
|
||||
|
||||
|
||||
class Ifupdown2Complete(Exception):
|
||||
def __init__(self, status):
|
||||
self.status = status
|
||||
|
||||
|
||||
class Ifupdown2Client:
|
||||
def __init__(self, argv):
|
||||
|
||||
self.stdin = None
|
||||
self.argv = argv
|
||||
self.data = ''
|
||||
self.HEADER_SIZE = 4
|
||||
self.daemon_pid = -1
|
||||
|
||||
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
try:
|
||||
self.socket.connect('/var/run/ifupdown2d/uds')
|
||||
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
signal.signal(signal.SIGTERM, self.signal_handler)
|
||||
signal.signal(signal.SIGQUIT, self.signal_handler)
|
||||
|
||||
try:
|
||||
self.SO_PEERCRED = socket.SO_PEERCRED
|
||||
except AttributeError:
|
||||
# powerpc is the only non-generic we care about. alpha, mips,
|
||||
# sparc, and parisc also have non-generic values.
|
||||
machine = os.uname()[4]
|
||||
if re.search(r'^(ppc|powerpc)', machine):
|
||||
self.SO_PASSCRED = 20
|
||||
self.SO_PEERCRED = 21
|
||||
else:
|
||||
self.SO_PASSCRED = 16
|
||||
self.SO_PEERCRED = 17
|
||||
try:
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
|
||||
except Exception as e:
|
||||
raise Exception('setsockopt: %s' % str(e))
|
||||
|
||||
except socket.error:
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
sys.stderr.write("""
|
||||
ERROR: %s could not connect to ifupdown2d
|
||||
|
||||
Try starting ifupdown2d with:
|
||||
sudo systemctl start ifupdown2d
|
||||
|
||||
To configure ifupdown2d to start when the box boots:
|
||||
sudo systemctl enable ifupdown2d
|
||||
""" % argv[0])
|
||||
|
||||
def __del__(self):
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
|
||||
def signal_handler(self, sig, frame):
|
||||
if self.daemon_pid > 0:
|
||||
os.kill(self.daemon_pid, sig)
|
||||
|
||||
def read_data(self):
|
||||
ready = select.select([self.socket], [], [])
|
||||
if ready and ready[0] and ready[0][0] == self.socket:
|
||||
d = self.socket.recv(65536)
|
||||
if self.data:
|
||||
self.data += d
|
||||
else:
|
||||
self.data = d
|
||||
return True
|
||||
def daemon_mode():
|
||||
""" Check ifupdown2 config to see if we should start the client """
|
||||
try:
|
||||
with open(config.IFUPDOWN2_CONF_PATH) as f:
|
||||
return "use_daemon=yes" in f.read()
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_packets(self):
|
||||
"""
|
||||
ifupdown2 output is divided into "json packets"
|
||||
the first 4 bytes is the size of the next json
|
||||
object to etract
|
||||
|
||||
"""
|
||||
data_size = len(self.data)
|
||||
if not data_size:
|
||||
raise Ifupdown2Complete(status=1)
|
||||
|
||||
packets = []
|
||||
try:
|
||||
while data_size > 0:
|
||||
packet_len = struct.unpack('=I', self.data[:self.HEADER_SIZE])[0]
|
||||
packet_data = self.data[self.HEADER_SIZE:packet_len + self.HEADER_SIZE]
|
||||
|
||||
fmt = "=%ds" % packet_len
|
||||
|
||||
packets.append(json.loads(struct.unpack(fmt, packet_data)[0]))
|
||||
|
||||
self.data = self.data[self.HEADER_SIZE + packet_len:]
|
||||
data_size -= self.HEADER_SIZE + packet_len
|
||||
except:
|
||||
pass
|
||||
return packets
|
||||
|
||||
def process_packets(self, packets):
|
||||
for packet in packets:
|
||||
if 'pid' in packet:
|
||||
self.daemon_pid = packet['pid']
|
||||
if 'stdout' in packet:
|
||||
sys.stdout.write(packet['stdout'])
|
||||
if 'stderr' in packet:
|
||||
sys.stderr.write(packet['stderr'])
|
||||
if 'status' in packet:
|
||||
raise Ifupdown2Complete(packet['status'])
|
||||
|
||||
def run(self):
|
||||
status = 1
|
||||
if self.socket:
|
||||
for arg in ['-i', '--interfaces']:
|
||||
try:
|
||||
if self.argv[self.argv.index(arg) + 1] == '-':
|
||||
self.stdin = sys.stdin.read()
|
||||
continue
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
self.socket.send(json.dumps({
|
||||
'argv': self.argv,
|
||||
'stdin': self.stdin
|
||||
}))
|
||||
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
self.read_data()
|
||||
self.process_packets(self.get_packets())
|
||||
except Ifupdown2Complete as e:
|
||||
status = e.status
|
||||
break
|
||||
except Exception as e:
|
||||
if ((isinstance(e.args, tuple) and e[0] == 4)
|
||||
or (hasattr(e, 'errno') and e.errno == errno.EINTR)):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
except Exception as e:
|
||||
sys.stderr.write(str(e))
|
||||
finally:
|
||||
self.socket.close()
|
||||
return status if status != None else 1
|
||||
|
||||
|
||||
def ifupdown2_standalone():
|
||||
def client():
|
||||
try:
|
||||
import ifupdown2.ifupdown.main as main_ifupdown2
|
||||
status = Client(sys.argv).run()
|
||||
except ExitWithStatusAndError as e:
|
||||
root_logger.error(e.message)
|
||||
status = e.status
|
||||
except ExitWithStatus as e:
|
||||
status = e.status
|
||||
return status
|
||||
|
||||
|
||||
def stand_alone():
|
||||
if not sys.argv[0].endswith("query") and os.geteuid() != 0:
|
||||
sys.stderr.write('must be root to run this command\n')
|
||||
return 1
|
||||
try:
|
||||
from ifupdown2.ifupdown.main import Ifupdown2
|
||||
from ifupdown2.lib.nlcache import NetlinkListenerWithCache, NetlinkListenerWithCacheErrorNotInitialized
|
||||
except:
|
||||
import ifupdown.main as main_ifupdown2
|
||||
ifupdown2 = main_ifupdown2.Ifupdown2(daemon=False, uid=os.geteuid())
|
||||
ifupdown2.parse_argv(sys.argv)
|
||||
ifupdown2.update_logger()
|
||||
return ifupdown2.main()
|
||||
from ifupdown.main import Ifupdown2
|
||||
from lib.nlcache import NetlinkListenerWithCache, NetlinkListenerWithCacheErrorNotInitialized
|
||||
ifupdown2 = Ifupdown2(daemon=False, uid=os.geteuid())
|
||||
try:
|
||||
ifupdown2.parse_argv(sys.argv)
|
||||
LogManager.get_instance().start_standalone_logging(ifupdown2.args)
|
||||
except ArgvParseHelp:
|
||||
# on --help parse_args raises SystemExit, we catch it and raise a
|
||||
# custom exception ArgvParseHelp to return 0
|
||||
return 0
|
||||
try:
|
||||
status = ifupdown2.main()
|
||||
finally:
|
||||
try:
|
||||
NetlinkListenerWithCache.get_instance().cleanup()
|
||||
except NetlinkListenerWithCacheErrorNotInitialized:
|
||||
status = Status.Client.STATUS_NLERROR
|
||||
return status
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
if 'use_daemon=yes' in open(core_config.IFUPDOWN2_CONF_PATH).read():
|
||||
return Ifupdown2Client(sys.argv).run()
|
||||
if daemon_mode():
|
||||
return client()
|
||||
else:
|
||||
return ifupdown2_standalone()
|
||||
return stand_alone()
|
||||
except ArgvParseHelp:
|
||||
return Status.Client.STATUS_SUCCESS
|
||||
except KeyboardInterrupt:
|
||||
return 1
|
||||
return Status.Client.STATUS_KEYBOARD_INTERRUPT
|
||||
except Exception as e:
|
||||
log.error(str(e))
|
||||
return 1
|
||||
root_logger.exception("main: %s" % str(e))
|
||||
return Status.Client.STATUS_EXCEPTION_MAIN
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
sys.exit(main())
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
sys.exit(Status.Client.STATUS_KEYBOARD_INTERRUPT)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -6,19 +6,18 @@
|
||||
|
||||
import os
|
||||
import glob
|
||||
import subprocess
|
||||
|
||||
from collections import deque
|
||||
from ipaddr import IPNetwork, IPv6Network
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
|
||||
from ifupdown2.nlmanager.nlpacket import Link
|
||||
|
||||
from ifupdown2.ifupdownaddons.cache import *
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown2.ifupdown.statemanager as statemanager
|
||||
@@ -26,14 +25,12 @@ try:
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.netlink import netlink
|
||||
|
||||
from nlmanager.nlpacket import Link
|
||||
|
||||
from ifupdownaddons.cache import *
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown.statemanager as statemanager
|
||||
@@ -42,42 +39,45 @@ except ImportError:
|
||||
import ifupdown.ifupdownconfig as ifupdownconfig
|
||||
|
||||
|
||||
class addressvirtual(moduleBase):
|
||||
class addressvirtual(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure virtual addresses """
|
||||
|
||||
_modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
|
||||
'interfaces. It creates a macvlan interface for ' +
|
||||
'every mac ip address-virtual line',
|
||||
'attrs' : {
|
||||
'address-virtual' :
|
||||
{ 'help' : 'bridge router virtual mac and ips',
|
||||
'multivalue' : True,
|
||||
'validvals' : ['<mac-ip/prefixlen-list>',],
|
||||
'example': ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']
|
||||
},
|
||||
'address-virtual-ipv6-addrgen': {
|
||||
'help': 'enable disable ipv6 link addrgenmode',
|
||||
'validvals': ['on', 'off'],
|
||||
'default': 'on',
|
||||
'example': [
|
||||
'address-virtual-ipv6-addrgen on',
|
||||
'address-virtual-ipv6-addrgen off'
|
||||
]
|
||||
},
|
||||
"vrrp": {
|
||||
"help": "VRRP support",
|
||||
"multivalue": True,
|
||||
"example": [
|
||||
"vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
|
||||
"vrrp 42 10.0.0.42/24"
|
||||
]
|
||||
}
|
||||
}}
|
||||
_modinfo = {
|
||||
"mhelp": "address module configures virtual addresses for interfaces. "
|
||||
"It creates a macvlan interface for every mac ip address-virtual line",
|
||||
"attrs": {
|
||||
"address-virtual": {
|
||||
"help": "bridge router virtual mac and ips",
|
||||
"multivalue": True,
|
||||
"validvals": ["<mac-ip/prefixlen-list>", ],
|
||||
"example": ["address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24"]
|
||||
},
|
||||
"address-virtual-ipv6-addrgen": {
|
||||
"help": "enable disable ipv6 link addrgenmode",
|
||||
"validvals": ["on", "off"],
|
||||
"default": "on",
|
||||
"example": [
|
||||
"address-virtual-ipv6-addrgen on",
|
||||
"address-virtual-ipv6-addrgen off"
|
||||
]
|
||||
},
|
||||
"vrrp": {
|
||||
"help": "VRRP support",
|
||||
"multivalue": True,
|
||||
"example": [
|
||||
"vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
|
||||
"vrrp 42 10.0.0.42/24"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_IP_METRIC = 1024
|
||||
ADDR_METRIC_SUPPORT = None
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.ipcmd = None
|
||||
self._bridge_fdb_query_cache = {}
|
||||
self.addressvirtual_with_route_metric = utils.get_boolean_from_string(
|
||||
policymanager.policymanager_api.get_module_globals(
|
||||
@@ -89,6 +89,27 @@ class addressvirtual(moduleBase):
|
||||
|
||||
self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
|
||||
|
||||
if addressvirtual.ADDR_METRIC_SUPPORT is None:
|
||||
try:
|
||||
cmd = [utils.ip_cmd, 'addr', 'help']
|
||||
self.logger.info('executing %s addr help' % utils.ip_cmd)
|
||||
|
||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = process.communicate()
|
||||
addressvirtual.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or ''
|
||||
self.logger.info('address metric support: %s' % ('OK' if addressvirtual.ADDR_METRIC_SUPPORT else 'KO'))
|
||||
except Exception:
|
||||
addressvirtual.ADDR_METRIC_SUPPORT = False
|
||||
self.logger.info('address metric support: KO')
|
||||
|
||||
@classmethod
|
||||
def addr_metric_support(cls):
|
||||
return cls.ADDR_METRIC_SUPPORT
|
||||
|
||||
@classmethod
|
||||
def get_default_ip_metric(cls):
|
||||
return cls.DEFAULT_IP_METRIC
|
||||
|
||||
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
|
||||
if ifaceobj.get_attr_value('address-virtual') or ifaceobj.get_attr_value("vrrp"):
|
||||
ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
|
||||
@@ -96,20 +117,19 @@ class addressvirtual(moduleBase):
|
||||
def _get_macvlan_prefix(self, ifaceobj):
|
||||
return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
|
||||
|
||||
@staticmethod
|
||||
def get_vrrp_prefix(ifname, family):
|
||||
return "vrrp%s-%s-" % (family, netlink.get_iface_index(ifname))
|
||||
def get_vrrp_prefix(self, ifname, family):
|
||||
return "vrrp%s-%s-" % (family, self.cache.get_ifindex(ifname))
|
||||
|
||||
def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
|
||||
# XXX: batch the addresses
|
||||
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
|
||||
bridgename = ifaceobj.lowerifaces[0]
|
||||
vlan = self._get_vlan_id(ifaceobj)
|
||||
if self.ipcmd.bridge_is_vlan_aware(bridgename):
|
||||
[self.ipcmd.bridge_fdb_add(bridgename, addr,
|
||||
if self.cache.bridge_is_vlan_aware(bridgename):
|
||||
[self.iproute2.bridge_fdb_add(bridgename, addr,
|
||||
vlan) for addr in hwaddress]
|
||||
elif self.ipcmd.is_bridge(ifaceobj.name):
|
||||
[self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
|
||||
elif self.cache.link_is_bridge(ifaceobj.name):
|
||||
[self.iproute2.bridge_fdb_add(ifaceobj.name, addr)
|
||||
for addr in hwaddress]
|
||||
|
||||
def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
|
||||
@@ -117,17 +137,17 @@ class addressvirtual(moduleBase):
|
||||
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
|
||||
bridgename = ifaceobj.lowerifaces[0]
|
||||
vlan = self._get_vlan_id(ifaceobj)
|
||||
if self.ipcmd.bridge_is_vlan_aware(bridgename):
|
||||
if self.cache.bridge_is_vlan_aware(bridgename):
|
||||
for addr in hwaddress:
|
||||
try:
|
||||
self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
|
||||
self.iproute2.bridge_fdb_del(bridgename, addr, vlan)
|
||||
except Exception, e:
|
||||
self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
|
||||
pass
|
||||
elif self.ipcmd.is_bridge(ifaceobj.name):
|
||||
elif self.cache.link_is_bridge(ifaceobj.name):
|
||||
for addr in hwaddress:
|
||||
try:
|
||||
self.ipcmd.bridge_fdb_del(ifaceobj.name, addr)
|
||||
self.iproute2.bridge_fdb_del(ifaceobj.name, addr)
|
||||
except Exception, e:
|
||||
self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
|
||||
pass
|
||||
@@ -135,7 +155,7 @@ class addressvirtual(moduleBase):
|
||||
def _get_bridge_fdbs(self, bridgename, vlan):
|
||||
fdbs = self._bridge_fdb_query_cache.get(bridgename)
|
||||
if not fdbs:
|
||||
fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
|
||||
fdbs = self.iproute2.bridge_fdb_show_dev(bridgename)
|
||||
if not fdbs:
|
||||
return
|
||||
self._bridge_fdb_query_cache[bridgename] = fdbs
|
||||
@@ -147,13 +167,13 @@ class addressvirtual(moduleBase):
|
||||
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
|
||||
bridgename = ifaceobj.lowerifaces[0]
|
||||
vlan = self._get_vlan_id(ifaceobj)
|
||||
if self.ipcmd.bridge_is_vlan_aware(bridgename):
|
||||
if self.cache.bridge_is_vlan_aware(bridgename):
|
||||
fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
|
||||
if not fdb_addrs:
|
||||
return False
|
||||
hwaddress_int = self.ipcmd.mac_str_to_int(hwaddress)
|
||||
hwaddress_int = utils.mac_str_to_int(hwaddress)
|
||||
for mac in fdb_addrs:
|
||||
if self.ipcmd.mac_str_to_int(mac) == hwaddress_int:
|
||||
if utils.mac_str_to_int(mac) == hwaddress_int:
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
@@ -182,29 +202,24 @@ class addressvirtual(moduleBase):
|
||||
route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
|
||||
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
|
||||
vrf_master = self.ipcmd.link_get_master(ifaceobj.name)
|
||||
vrf_master = self.cache.get_master(ifaceobj.name)
|
||||
else:
|
||||
vrf_master = None
|
||||
|
||||
dev = self.ipcmd.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
|
||||
dev = self.iproute2.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
|
||||
|
||||
if dev and dev != ifaceobj.name:
|
||||
self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
|
||||
'seems to be of the macvlan dev %s'
|
||||
%vifacename +
|
||||
' .. flapping macvlan dev to fix entry.')
|
||||
self.ipcmd.link_down(vifacename)
|
||||
self.ipcmd.link_up(vifacename)
|
||||
self.iproute2.link_down(vifacename)
|
||||
self.iproute2.link_up(vifacename)
|
||||
except Exception, e:
|
||||
self.logger.debug('%s: fixing route entry failed (%s)'
|
||||
% (ifaceobj.name, str(e)))
|
||||
pass
|
||||
|
||||
def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
|
||||
vrfname = self.ipcmd.link_get_master(ifaceobj.name)
|
||||
if vrfname:
|
||||
self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
|
||||
|
||||
def _get_macs_from_old_config(self, ifaceobj=None):
|
||||
""" This method returns a list of the mac addresses
|
||||
in the address-virtual attribute for the bridge. """
|
||||
@@ -251,10 +266,10 @@ class addressvirtual(moduleBase):
|
||||
return False, None
|
||||
|
||||
def _remove_running_address_config(self, ifaceobj):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
hwaddress = []
|
||||
self.ipcmd.batch_start()
|
||||
|
||||
for macvlan_prefix in [
|
||||
self._get_macvlan_prefix(ifaceobj),
|
||||
self.get_vrrp_prefix(ifaceobj.name, "4"),
|
||||
@@ -262,13 +277,13 @@ class addressvirtual(moduleBase):
|
||||
]:
|
||||
for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % macvlan_prefix):
|
||||
macvlan_ifacename = os.path.basename(macvlan_ifacename)
|
||||
if not self.ipcmd.link_exists(macvlan_ifacename):
|
||||
if not self.cache.link_exists(macvlan_ifacename):
|
||||
continue
|
||||
hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
|
||||
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
|
||||
hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
|
||||
self.netlink.link_del(os.path.basename(macvlan_ifacename))
|
||||
# XXX: Also delete any fdb addresses. This requires, checking mac address
|
||||
# on individual macvlan interfaces and deleting the vlan from that.
|
||||
self.ipcmd.batch_commit()
|
||||
|
||||
if any(hwaddress):
|
||||
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
|
||||
|
||||
@@ -277,28 +292,20 @@ class addressvirtual(moduleBase):
|
||||
self._remove_running_address_config(ifaceobj)
|
||||
return
|
||||
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
hwaddress = []
|
||||
self.ipcmd.batch_start()
|
||||
av_idx = 0
|
||||
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
|
||||
for av in address_virtual_list:
|
||||
av_attrs = av.split()
|
||||
if len(av_attrs) < 2:
|
||||
self.log_error("%s: incorrect address-virtual attrs '%s'"
|
||||
%(ifaceobj.name, av), ifaceobj,
|
||||
raise_error=False)
|
||||
av_idx += 1
|
||||
continue
|
||||
|
||||
# Delete the macvlan device on this device
|
||||
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
|
||||
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
|
||||
self.netlink.link_del(os.path.basename(macvlan_ifacename))
|
||||
if av_attrs[0] != 'None':
|
||||
hwaddress.append(av_attrs[0])
|
||||
av_idx += 1
|
||||
self.ipcmd.batch_commit()
|
||||
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
|
||||
|
||||
def check_mac_address(self, ifaceobj, mac):
|
||||
@@ -325,12 +332,12 @@ class addressvirtual(moduleBase):
|
||||
if not ifaceobj_getfunc:
|
||||
return
|
||||
if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
|
||||
self.ipcmd.link_exists(ifaceobj.name)):
|
||||
self.cache.link_exists(ifaceobj.name)):
|
||||
# if I am a vrf device and I have slaves
|
||||
# that have address virtual config,
|
||||
# enslave the slaves 'address virtual
|
||||
# interfaces (macvlans)' to myself:
|
||||
running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
|
||||
running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
|
||||
if running_slaves:
|
||||
# pick up any existing slaves of a vrf device and
|
||||
# look for their upperdevices and enslave them to the
|
||||
@@ -341,7 +348,7 @@ class addressvirtual(moduleBase):
|
||||
(sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
|
||||
# enslave all its upper devices to
|
||||
# the vrf device
|
||||
upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name)
|
||||
upperdevs = self.sysfs.link_get_uppers(sobjs[0].name)
|
||||
if not upperdevs:
|
||||
continue
|
||||
for u in upperdevs:
|
||||
@@ -350,18 +357,18 @@ class addressvirtual(moduleBase):
|
||||
# upper device list
|
||||
if u == ifaceobj.name:
|
||||
continue
|
||||
self.ipcmd.link_set(u, 'master', ifaceobj.name,
|
||||
state='up')
|
||||
self.netlink.link_set_master(u, ifaceobj.name)
|
||||
self.netlink.link_up(u)
|
||||
elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
|
||||
(ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
|
||||
self.ipcmd.link_exists(ifaceobj.name)):
|
||||
self.cache.link_exists(ifaceobj.name)):
|
||||
# If I am a vrf slave and I have 'address virtual'
|
||||
# config, make sure my addrress virtual interfaces
|
||||
# (macvlans) are also enslaved to the vrf device
|
||||
vrfname = ifaceobj.get_attr_value_first('vrf')
|
||||
if not vrfname or not self.ipcmd.link_exists(vrfname):
|
||||
if not vrfname or not self.cache.link_exists(vrfname):
|
||||
return
|
||||
running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name)
|
||||
running_uppers = self.sysfs.link_get_uppers(ifaceobj.name)
|
||||
if not running_uppers:
|
||||
return
|
||||
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
|
||||
@@ -371,10 +378,11 @@ class addressvirtual(moduleBase):
|
||||
if u == vrfname:
|
||||
continue
|
||||
if u.startswith(macvlan_prefix):
|
||||
self.ipcmd.link_set(u, 'master', vrfname,
|
||||
state='up')
|
||||
self.netlink.link_set_master(u, vrfname)
|
||||
self.netlink.link_up(u)
|
||||
|
||||
def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False):
|
||||
|
||||
"""
|
||||
intf_config_list = [
|
||||
{
|
||||
@@ -384,17 +392,25 @@ class addressvirtual(moduleBase):
|
||||
},
|
||||
]
|
||||
"""
|
||||
hw_address_list = []
|
||||
|
||||
if not intf_config_list:
|
||||
return hw_address_list
|
||||
|
||||
user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj)
|
||||
purge_existing = False if ifupdownflags.flags.PERFMODE else True
|
||||
hw_address_list = []
|
||||
ifname = ifaceobj.name
|
||||
|
||||
lower_iface_mtu = update_mtu = None
|
||||
update_mtu = lower_iface_mtu = lower_iface_mtu_str = None
|
||||
if ifupdownconfig.config.get("adjust_logical_dev_mtu", "1") != "0":
|
||||
if ifaceobj.lowerifaces and intf_config_list:
|
||||
update_mtu = True
|
||||
|
||||
self.ipcmd.batch_start()
|
||||
if update_mtu:
|
||||
lower_iface_mtu = self.cache.get_link_mtu(ifaceobj.name)
|
||||
lower_iface_mtu_str = str(lower_iface_mtu)
|
||||
|
||||
self.iproute2.batch_start() # TODO: make sure we only do 1 ip link set down and set up (only one flap in the batch)
|
||||
|
||||
for intf_config_dict in intf_config_list:
|
||||
link_created = False
|
||||
@@ -403,19 +419,27 @@ class addressvirtual(moduleBase):
|
||||
macvlan_mode = intf_config_dict.get("mode")
|
||||
ips = intf_config_dict.get("ips")
|
||||
|
||||
if not self.ipcmd.link_exists(macvlan_ifname):
|
||||
self.ipcmd.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
|
||||
if not self.cache.link_exists(macvlan_ifname):
|
||||
# When creating VRRP macvlan with bridge mode, the kernel
|
||||
# return an error: 'Invalid argument' (22)
|
||||
# so for now we should only use the iproute2 API.
|
||||
# try:
|
||||
# self.netlink.link_add_macvlan(ifname, macvlan_ifname)
|
||||
# except:
|
||||
self.iproute2.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
|
||||
link_created = True
|
||||
|
||||
# first thing we need to handle vrf enslavement
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
|
||||
self._handle_vrf_slaves(macvlan_ifname, ifaceobj)
|
||||
vrf_ifname = self.cache.get_master(ifaceobj.name)
|
||||
if vrf_ifname:
|
||||
self.iproute2.link_set_master(macvlan_ifname, vrf_ifname)
|
||||
|
||||
# if we are dealing with a VRRP macvlan we need to set addrgenmode
|
||||
# to RANDOM, and protodown on
|
||||
if vrrp:
|
||||
try:
|
||||
self.ipcmd.ipv6_addrgen(
|
||||
self.iproute2.link_set_ipv6_addrgen(
|
||||
macvlan_ifname,
|
||||
Link.IN6_ADDR_GEN_MODE_RANDOM,
|
||||
link_created
|
||||
@@ -425,22 +449,26 @@ class addressvirtual(moduleBase):
|
||||
"operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
|
||||
try:
|
||||
if link_created:
|
||||
netlink.link_set_protodown(macvlan_ifname, "on")
|
||||
self.netlink.link_set_protodown_on(macvlan_ifname)
|
||||
except Exception as e:
|
||||
self.logger.warning("%s: %s: ip link set dev %s protodown on: operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
|
||||
elif user_configured_ipv6_addrgenmode:
|
||||
self.ipcmd.ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
|
||||
self.iproute2.link_set_ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
|
||||
|
||||
if macvlan_hwaddr:
|
||||
self.ipcmd.link_set_hwaddress(macvlan_ifname, macvlan_hwaddr, keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN)
|
||||
self.iproute2.link_set_address_and_keep_down(
|
||||
macvlan_ifname,
|
||||
macvlan_hwaddr,
|
||||
keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN
|
||||
)
|
||||
hw_address_list.append(macvlan_hwaddr)
|
||||
|
||||
if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support():
|
||||
metric = self.ipcmd.get_default_ip_metric()
|
||||
if self.addressvirtual_with_route_metric and self.addr_metric_support():
|
||||
metric = self.get_default_ip_metric()
|
||||
else:
|
||||
metric = None
|
||||
|
||||
self.ipcmd.addr_add_multiple(
|
||||
self.iproute2.add_addresses(
|
||||
ifaceobj,
|
||||
macvlan_ifname,
|
||||
ips,
|
||||
@@ -450,53 +478,58 @@ class addressvirtual(moduleBase):
|
||||
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
|
||||
self.logger.info("%s: keeping macvlan down - link-down yes on lower device %s" % (macvlan_ifname, ifname))
|
||||
netlink.link_set_updown(macvlan_ifname, "down")
|
||||
self.netlink.link_down(macvlan_ifname)
|
||||
|
||||
# If link existed before, flap the link
|
||||
if not link_created:
|
||||
|
||||
if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
|
||||
if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
|
||||
# if the system doesn't support ip addr set METRIC
|
||||
# we need to do manually check the ordering of the ip4 routes
|
||||
self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
|
||||
|
||||
if update_mtu:
|
||||
lower_iface_mtu = self.ipcmd.link_get_mtu(ifname, refresh=True)
|
||||
update_mtu = False
|
||||
|
||||
if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifname, refresh=True):
|
||||
try:
|
||||
self.ipcmd.link_set_mtu(macvlan_ifname,
|
||||
lower_iface_mtu)
|
||||
self.sysfs.link_set_mtu(macvlan_ifname, mtu_str=lower_iface_mtu_str, mtu_int=lower_iface_mtu)
|
||||
except Exception as e:
|
||||
self.logger.info('%s: failed to set mtu %s: %s' %
|
||||
(macvlan_ifname, lower_iface_mtu, e))
|
||||
self.logger.info('%s: failed to set mtu %s: %s' % (macvlan_ifname, lower_iface_mtu, e))
|
||||
|
||||
# set macvlan device to up in anycase.
|
||||
# since we auto create them here..we are responsible
|
||||
# to bring them up here in the case they were brought down
|
||||
# by some other entity in the system.
|
||||
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
|
||||
netlink.link_set_updown(macvlan_ifname, "up")
|
||||
self.netlink.link_up(macvlan_ifname)
|
||||
else:
|
||||
try:
|
||||
if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
|
||||
if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
|
||||
# if the system doesn't support ip addr set METRIC
|
||||
# we need to do manually check the ordering of the ip6 routes
|
||||
self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
|
||||
self.iproute2.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
|
||||
except Exception as e:
|
||||
self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
|
||||
|
||||
# Disable IPv6 duplicate address detection on VRR interfaces
|
||||
sysctl_prefix = "net.ipv6.conf.%s" % macvlan_ifname
|
||||
|
||||
try:
|
||||
syskey = "%s.%s" % (sysctl_prefix, "enhanced_dad")
|
||||
if self.sysctl_get(syskey) != "0":
|
||||
self.sysctl_set(syskey, "0")
|
||||
except Exception as e:
|
||||
self.logger.info("sysctl failure: operation not supported: %s" % str(e))
|
||||
|
||||
for key, sysval in {
|
||||
"accept_dad": "0",
|
||||
"dad_transmits": "0"
|
||||
}.iteritems():
|
||||
syskey = "net.ipv6.conf.%s.%s" % (macvlan_ifname, key)
|
||||
syskey = "%s.%s" % (sysctl_prefix, key)
|
||||
if self.sysctl_get(syskey) != sysval:
|
||||
self.sysctl_set(syskey, sysval)
|
||||
|
||||
self.ipcmd.batch_commit()
|
||||
self.iproute2.batch_commit()
|
||||
return hw_address_list
|
||||
|
||||
def _up(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
@@ -518,7 +551,7 @@ class addressvirtual(moduleBase):
|
||||
"with no upper interfaces or parent interfaces)"
|
||||
% ifaceobj.name, ifaceobj)
|
||||
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
|
||||
addr_virtual_macs = self.create_macvlan_and_apply_config(
|
||||
@@ -597,7 +630,7 @@ class addressvirtual(moduleBase):
|
||||
if ip4 or ifquery:
|
||||
merged_with_existing_obj = False
|
||||
macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
|
||||
macvlan_ip4_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip4_mac)
|
||||
macvlan_ip4_mac_int = utils.mac_str_to_int(macvlan_ip4_mac)
|
||||
# if the vrr config is defined in different lines for the same ID
|
||||
# we need to save the ip4 and ip6 in the objects we previously
|
||||
# created, example:
|
||||
@@ -622,13 +655,13 @@ class addressvirtual(moduleBase):
|
||||
elif not ip4 and not ifquery:
|
||||
# special check to see if all ipv4 were removed from the vrrp
|
||||
# configuration, if so we need to remove the associated macvlan
|
||||
if self.ipcmd.link_exists(macvlan_ip4_ifname):
|
||||
netlink.link_del(macvlan_ip4_ifname)
|
||||
if self.cache.link_exists(macvlan_ip4_ifname):
|
||||
self.netlink.link_del(macvlan_ip4_ifname)
|
||||
|
||||
if ip6 or ifquery:
|
||||
merged_with_existing_obj = False
|
||||
macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
|
||||
macvlan_ip6_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip6_mac)
|
||||
macvlan_ip6_mac_int = utils.mac_str_to_int(macvlan_ip6_mac)
|
||||
# if the vrr config is defined in different lines for the same ID
|
||||
# we need to save the ip4 and ip6 in the objects we previously
|
||||
# created, example:
|
||||
@@ -654,8 +687,8 @@ class addressvirtual(moduleBase):
|
||||
elif not ip6 and not ifquery:
|
||||
# special check to see if all ipv6 were removed from the vrrp
|
||||
# configuration, if so we need to remove the associated macvlan
|
||||
if self.ipcmd.link_exists(macvlan_ip6_ifname):
|
||||
netlink.link_del(macvlan_ip6_ifname)
|
||||
if self.cache.link_exists(macvlan_ip6_ifname):
|
||||
self.netlink.link_del(macvlan_ip6_ifname)
|
||||
|
||||
if not ifquery:
|
||||
# check if vrrp attribute was removed/re-assigned
|
||||
@@ -681,11 +714,11 @@ class addressvirtual(moduleBase):
|
||||
macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), id_to_remove)
|
||||
macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), id_to_remove)
|
||||
|
||||
if self.ipcmd.link_exists(macvlan_ip4_ifname):
|
||||
netlink.link_del(macvlan_ip4_ifname)
|
||||
if self.cache.link_exists(macvlan_ip4_ifname):
|
||||
self.netlink.link_del(macvlan_ip4_ifname)
|
||||
|
||||
if self.ipcmd.link_exists(macvlan_ip6_ifname):
|
||||
netlink.link_del(macvlan_ip6_ifname)
|
||||
if self.cache.link_exists(macvlan_ip6_ifname):
|
||||
self.netlink.link_del(macvlan_ip6_ifname)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname)
|
||||
@@ -714,13 +747,6 @@ class addressvirtual(moduleBase):
|
||||
|
||||
for index, addr_virtual in enumerate(address_virtual_list):
|
||||
av_attrs = addr_virtual.split()
|
||||
|
||||
if len(av_attrs) < 2:
|
||||
self.log_error("%s: incorrect address-virtual attrs '%s'"
|
||||
% (ifaceobj.name, addr_virtual), ifaceobj,
|
||||
raise_error=False)
|
||||
continue
|
||||
|
||||
mac = av_attrs[0]
|
||||
if mac:
|
||||
mac = mac.lower()
|
||||
@@ -735,7 +761,7 @@ class addressvirtual(moduleBase):
|
||||
|
||||
if mac != "none":
|
||||
config["hwaddress"] = mac
|
||||
config["hwaddress_int"] = self.ipcmd.mac_str_to_int(mac)
|
||||
config["hwaddress_int"] = utils.mac_str_to_int(mac)
|
||||
|
||||
ip_network_obj_list = []
|
||||
for ip in av_attrs[1:]:
|
||||
@@ -746,9 +772,6 @@ class addressvirtual(moduleBase):
|
||||
|
||||
return user_config_list
|
||||
|
||||
def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
|
||||
return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
|
||||
|
||||
def _down(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
try:
|
||||
self._remove_address_config(ifaceobj,
|
||||
@@ -756,17 +779,15 @@ class addressvirtual(moduleBase):
|
||||
|
||||
#### VRR
|
||||
hwaddress = []
|
||||
self.ipcmd.batch_start()
|
||||
for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
|
||||
for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
|
||||
macvlan_ifacename = os.path.basename(macvlan_ifacename)
|
||||
if not self.ipcmd.link_exists(macvlan_ifacename):
|
||||
if not self.cache.link_exists(macvlan_ifacename):
|
||||
continue
|
||||
hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
|
||||
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
|
||||
hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
|
||||
self.netlink.link_del(macvlan_ifacename)
|
||||
# XXX: Also delete any fdb addresses. This requires, checking mac address
|
||||
# on individual macvlan interfaces and deleting the vlan from that.
|
||||
self.ipcmd.batch_commit()
|
||||
if any(hwaddress):
|
||||
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
|
||||
except Exception, e:
|
||||
@@ -776,7 +797,7 @@ class addressvirtual(moduleBase):
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
|
||||
user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
|
||||
@@ -822,6 +843,25 @@ class addressvirtual(moduleBase):
|
||||
return
|
||||
ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
|
||||
|
||||
@staticmethod
|
||||
def compare_user_config_vs_running_state(running_addrs, user_addrs):
|
||||
ip4 = []
|
||||
ip6 = []
|
||||
|
||||
for ip in user_addrs or []:
|
||||
obj = IPNetwork(ip)
|
||||
|
||||
if type(obj) == IPv6Network:
|
||||
ip6.append(obj)
|
||||
else:
|
||||
ip4.append(obj)
|
||||
|
||||
running_ipobj = []
|
||||
for ip in running_addrs or []:
|
||||
running_ipobj.append(IPNetwork(ip))
|
||||
|
||||
return running_ipobj == (ip4 + ip6)
|
||||
|
||||
def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
|
||||
"""
|
||||
macvlan_config_list = [
|
||||
@@ -853,7 +893,7 @@ class addressvirtual(moduleBase):
|
||||
|
||||
macvlan_ifacename = config.get("ifname")
|
||||
|
||||
if not self.ipcmd.link_exists(macvlan_ifacename):
|
||||
if not self.cache.link_exists(macvlan_ifacename):
|
||||
ifaceobjcurr.update_config_with_status(attr_name, "", 1)
|
||||
continue
|
||||
|
||||
@@ -861,26 +901,26 @@ class addressvirtual(moduleBase):
|
||||
macvlan_hwaddress_int = config.get("hwaddress_int")
|
||||
|
||||
if user_config_address_virtual_ipv6_addr:
|
||||
macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
|
||||
macvlans_running_ipv6_addr.append(self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename))
|
||||
|
||||
# Check mac and ip address
|
||||
rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
|
||||
raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
|
||||
rhwaddress = ip4_macvlan_hwaddress = self.cache.get_link_address(macvlan_ifacename)
|
||||
raddrs = ip4_running_addrs =[str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
|
||||
ifname=macvlan_ifacename,
|
||||
details=False,
|
||||
addr_virtual_ifaceobj=ifaceobj
|
||||
)
|
||||
ifaceobj_list=[ifaceobj],
|
||||
with_address_virtual=True
|
||||
)]
|
||||
|
||||
if not is_vrr:
|
||||
ips = config.get("ips")
|
||||
|
||||
if not raddrs or not rhwaddress:
|
||||
if not rhwaddress:
|
||||
ifaceobjcurr.update_config_with_status(attr_name, "", 1)
|
||||
continue
|
||||
|
||||
try:
|
||||
if self.ipcmd.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
|
||||
and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
|
||||
if utils.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
|
||||
and self.compare_user_config_vs_running_state(raddrs, ips) \
|
||||
and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
|
||||
ifaceobjcurr.update_config_with_status(
|
||||
attr_name,
|
||||
@@ -909,24 +949,25 @@ class addressvirtual(moduleBase):
|
||||
ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
|
||||
|
||||
# check macvlan ip6 hwaddress (only if ip6 were provided by the user)
|
||||
if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
|
||||
if not ip6_config.get("ips") or self.cache.get_link_address_raw(ip6_macvlan_ifname) == ip6_config.get("hwaddress_int"):
|
||||
|
||||
# check all ip4
|
||||
if self.ipcmd.compare_user_config_vs_running_state(
|
||||
if self.compare_user_config_vs_running_state(
|
||||
ip4_running_addrs,
|
||||
ip4_config.get("ips")
|
||||
) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
|
||||
ip6_running_addrs = self.ipcmd.get_running_addrs(
|
||||
ip6_running_addrs = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
|
||||
ifname=ip6_macvlan_ifname,
|
||||
details=False,
|
||||
addr_virtual_ifaceobj=ifaceobj
|
||||
)
|
||||
ifaceobj_list=[ifaceobj],
|
||||
with_address_virtual=True
|
||||
)]
|
||||
|
||||
# check all ip6
|
||||
if self.ipcmd.compare_user_config_vs_running_state(
|
||||
if self.compare_user_config_vs_running_state(
|
||||
ip6_running_addrs,
|
||||
ip6_config.get("ips")
|
||||
) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
|
||||
) and self._check_addresses_in_bridge(ifaceobj, ip6_macvlan_hwaddress):
|
||||
ifaceobjcurr.update_config_with_status(
|
||||
attr_name,
|
||||
"%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
|
||||
@@ -945,19 +986,16 @@ class addressvirtual(moduleBase):
|
||||
|
||||
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
|
||||
macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
|
||||
address_virtuals = []
|
||||
for av in linkCache.links:
|
||||
if av.startswith(macvlan_prefix):
|
||||
address_virtuals.append(av)
|
||||
|
||||
address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
|
||||
macvlans_ipv6_addrgen_list = []
|
||||
for av in address_virtuals:
|
||||
macvlan_ifacename = os.path.basename(av)
|
||||
rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
|
||||
|
||||
raddress = []
|
||||
for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
|
||||
raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
|
||||
rhwaddress = self.cache.get_link_address(macvlan_ifacename)
|
||||
raddress = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
|
||||
ifaceobj_list=ifaceobj_getfunc(ifaceobjrunning.name) or [],
|
||||
ifname=ifaceobjrunning.name,
|
||||
with_address_virtual=True
|
||||
)]
|
||||
|
||||
raddress = list(set(raddress))
|
||||
|
||||
@@ -968,7 +1006,7 @@ class addressvirtual(moduleBase):
|
||||
ifaceobjrunning.update_config('address-virtual',
|
||||
'%s %s' %(rhwaddress, ' '.join(raddress)))
|
||||
|
||||
macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
|
||||
macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename)))
|
||||
|
||||
macvlan_count = len(address_virtuals)
|
||||
if not macvlan_count:
|
||||
@@ -983,19 +1021,17 @@ class addressvirtual(moduleBase):
|
||||
return
|
||||
ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
|
||||
|
||||
|
||||
_run_ops = {'up' : _up,
|
||||
'down' : _down,
|
||||
'query-checkcurr' : _query_check,
|
||||
'query-running' : _query_running}
|
||||
_run_ops = {
|
||||
'up': _up,
|
||||
'down': _down,
|
||||
'query-checkcurr': _query_check,
|
||||
'query-running': _query_running
|
||||
}
|
||||
|
||||
def get_ops(self):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None,
|
||||
ifaceobj_getfunc=None, **extra_args):
|
||||
@@ -1019,7 +1055,7 @@ class addressvirtual(moduleBase):
|
||||
op_handler = self._run_ops.get(operation)
|
||||
if not op_handler:
|
||||
return
|
||||
self._init_command_handlers()
|
||||
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -5,20 +5,20 @@
|
||||
#
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
from ifupdown2.ifupdown.exceptions import moduleNotSupported
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
except:
|
||||
from lib.addon import Addon
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown.netlink import netlink
|
||||
from ifupdown.exceptions import moduleNotSupported
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
@@ -27,62 +27,56 @@ import re
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class batman_adv (moduleBase):
|
||||
|
||||
class batman_adv(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure B.A.T.M.A.N. advanced interfaces """
|
||||
|
||||
_modinfo = {
|
||||
'mhelp' : 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
|
||||
'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
|
||||
'interface to be creatable. You can specify a space separated list' +
|
||||
'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
|
||||
'is set for an interfaces this module will do the magic.',
|
||||
|
||||
'attrs' : {
|
||||
'batman-ifaces' : {
|
||||
'help' : 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
|
||||
'validvals' : [ '<interface-list>' ],
|
||||
'required' : True,
|
||||
'mhelp': 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
|
||||
'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
|
||||
'interface to be creatable. You can specify a space separated list' +
|
||||
'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
|
||||
'is set for an interfaces this module will do the magic.',
|
||||
'attrs': {
|
||||
'batman-ifaces': {
|
||||
'help': 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
|
||||
'validvals': ['<interface-list>'],
|
||||
'required': True,
|
||||
},
|
||||
|
||||
'batman-ifaces-ignore-regex' : {
|
||||
'help' : 'Interfaces to ignore when verifying configuration (regexp)',
|
||||
'required' : False,
|
||||
'batman-ifaces-ignore-regex': {
|
||||
'help': 'Interfaces to ignore when verifying configuration (regexp)',
|
||||
'required': False,
|
||||
},
|
||||
|
||||
'batman-distributed-arp-table' : {
|
||||
'help' : 'B.A.T.M.A.N. distributed ARP table',
|
||||
'validvals' : [ 'enabled', 'disabled' ],
|
||||
'required' : False,
|
||||
'batman-attr' : True,
|
||||
'batman-distributed-arp-table': {
|
||||
'help': 'B.A.T.M.A.N. distributed ARP table',
|
||||
'validvals': ['enabled', 'disabled'],
|
||||
'required': False,
|
||||
'batman-attr': True,
|
||||
},
|
||||
|
||||
'batman-gw-mode' : {
|
||||
'help' : 'B.A.T.M.A.N. gateway mode',
|
||||
'validvals' : [ 'off', 'client', 'server' ],
|
||||
'required' : False,
|
||||
'example' : [ 'batman-gw-mode client' ],
|
||||
'batman-attr' : True,
|
||||
'batman-gw-mode': {
|
||||
'help': 'B.A.T.M.A.N. gateway mode',
|
||||
'validvals': ['off', 'client', 'server'],
|
||||
'required': False,
|
||||
'example': ['batman-gw-mode client'],
|
||||
'batman-attr': True,
|
||||
},
|
||||
|
||||
'batman-hop-penalty' : {
|
||||
'help' : 'B.A.T.M.A.N. hop penalty',
|
||||
'validvals' : [ '<number>' ],
|
||||
'required' : False,
|
||||
'batman-attr' : True,
|
||||
'batman-hop-penalty': {
|
||||
'help': 'B.A.T.M.A.N. hop penalty',
|
||||
'validvals': ['<number>'],
|
||||
'required': False,
|
||||
'batman-attr': True,
|
||||
},
|
||||
|
||||
'batman-multicast-mode' : {
|
||||
'help' : 'B.A.T.M.A.N. multicast mode',
|
||||
'validvals' : [ 'enabled', 'disabled' ],
|
||||
'required' : False,
|
||||
'batman-attr' : True,
|
||||
'batman-multicast-mode': {
|
||||
'help': 'B.A.T.M.A.N. multicast mode',
|
||||
'validvals': ['enabled', 'disabled'],
|
||||
'required': False,
|
||||
'batman-attr': True,
|
||||
},
|
||||
|
||||
'batman-routing-algo' : {
|
||||
'help' : 'B.A.T.M.A.N. routing algo',
|
||||
'validvals' : [ 'BATMAN_IV', 'BATMAN_V' ],
|
||||
'required' : False,
|
||||
'batman-attr' : False,
|
||||
'batman-routing-algo': {
|
||||
'help': 'B.A.T.M.A.N. routing algo',
|
||||
'validvals': ['BATMAN_IV', 'BATMAN_V'],
|
||||
'required': False,
|
||||
'batman-attr': False,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -90,12 +84,11 @@ class batman_adv (moduleBase):
|
||||
_batman_attrs = {
|
||||
}
|
||||
|
||||
|
||||
def __init__ (self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__ (self, *args, **kargs)
|
||||
if not os.path.exists('/usr/sbin/batctl'):
|
||||
raise moduleNotSupported('module init failed: no /usr/sbin/batctl found')
|
||||
self.ipcmd = None
|
||||
|
||||
for longname, entry in self._modinfo['attrs'].items ():
|
||||
if entry.get ('batman-attr', False) == False:
|
||||
@@ -106,27 +99,23 @@ class batman_adv (moduleBase):
|
||||
'filename' : attr.replace ("-", "_"),
|
||||
}
|
||||
|
||||
|
||||
def _is_batman_device (self, ifaceobj):
|
||||
if ifaceobj.get_attr_value_first ('batman-ifaces'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _get_batman_ifaces (self, ifaceobj ):
|
||||
batman_ifaces = ifaceobj.get_attr_value_first ('batman-ifaces')
|
||||
if batman_ifaces:
|
||||
return sorted (batman_ifaces.split ())
|
||||
return None
|
||||
|
||||
|
||||
def _get_batman_ifaces_ignore_regex (self, ifaceobj):
|
||||
ifaces_ignore_regex = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
|
||||
if ifaces_ignore_regex:
|
||||
return re.compile (r"%s" % ifaces_ignore_regex)
|
||||
return None
|
||||
|
||||
|
||||
def _get_batman_attr (self, ifaceobj, attr):
|
||||
if attr not in self._batman_attrs:
|
||||
raise ValueError ("_get_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
|
||||
@@ -137,7 +126,6 @@ class batman_adv (moduleBase):
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _read_current_batman_attr (self, ifaceobj, attr, dont_map = False):
|
||||
# 'routing_algo' needs special handling, D'oh.
|
||||
if dont_map:
|
||||
@@ -156,7 +144,6 @@ class batman_adv (moduleBase):
|
||||
except ValueError:
|
||||
raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value)
|
||||
|
||||
|
||||
def _set_batman_attr (self, ifaceobj, attr, value):
|
||||
if attr not in self._batman_attrs:
|
||||
raise ValueError ("_set_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
|
||||
@@ -168,7 +155,6 @@ class batman_adv (moduleBase):
|
||||
except IOError as i:
|
||||
raise Exception ("_set_batman_attr (%s): %s" % (attr, i))
|
||||
|
||||
|
||||
def _batctl_if (self, bat_iface, mesh_iface, op):
|
||||
if op not in [ 'add', 'del' ]:
|
||||
raise Exception ("_batctl_if() called with invalid \"op\" value: %s" % op)
|
||||
@@ -193,7 +179,6 @@ class batman_adv (moduleBase):
|
||||
except Exception as e:
|
||||
raise Exception ("_set_routing_algo: %s" % e)
|
||||
|
||||
|
||||
def _find_member_ifaces (self, ifaceobj, ignore = True):
|
||||
members = []
|
||||
iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
|
||||
@@ -208,7 +193,6 @@ class batman_adv (moduleBase):
|
||||
|
||||
return sorted (members)
|
||||
|
||||
|
||||
def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
|
||||
if not self._is_batman_device (ifaceobj):
|
||||
return None
|
||||
@@ -220,7 +204,6 @@ class batman_adv (moduleBase):
|
||||
|
||||
return [None]
|
||||
|
||||
|
||||
def _up (self, ifaceobj):
|
||||
if self._get_batman_ifaces (ifaceobj) == None:
|
||||
raise Exception ('could not determine batman interfacaes')
|
||||
@@ -228,7 +211,7 @@ class batman_adv (moduleBase):
|
||||
# Verify existance of batman interfaces (should be present already)
|
||||
batman_ifaces = []
|
||||
for iface in self._get_batman_ifaces (ifaceobj):
|
||||
if not self.ipcmd.link_exists (iface):
|
||||
if not self.cache.link_exists (iface):
|
||||
self.logger.warn ('batman iface %s not present' % iface)
|
||||
continue
|
||||
|
||||
@@ -241,10 +224,9 @@ class batman_adv (moduleBase):
|
||||
if routing_algo:
|
||||
self._set_routing_algo (routing_algo)
|
||||
|
||||
|
||||
if_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
|
||||
# Is the batman main interface already present?
|
||||
if self.ipcmd.link_exists (ifaceobj.name):
|
||||
if self.cache.link_exists (ifaceobj.name):
|
||||
# Verify which member interfaces are present
|
||||
members = self._find_member_ifaces (ifaceobj)
|
||||
for iface in members:
|
||||
@@ -269,9 +251,8 @@ class batman_adv (moduleBase):
|
||||
netlink.link_set_updown(ifaceobj.name, "up")
|
||||
|
||||
|
||||
|
||||
def _down (self, ifaceobj):
|
||||
if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
|
||||
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists (ifaceobj.name):
|
||||
return
|
||||
|
||||
members = self._find_member_ifaces (ifaceobj)
|
||||
@@ -283,7 +264,7 @@ class batman_adv (moduleBase):
|
||||
|
||||
|
||||
def _query_check (self, ifaceobj, ifaceobjcurr):
|
||||
if not self.ipcmd.link_exists (ifaceobj.name):
|
||||
if not self.cache.link_exists (ifaceobj.name):
|
||||
return
|
||||
|
||||
batman_ifaces_cfg = self._get_batman_ifaces (ifaceobj)
|
||||
@@ -342,30 +323,16 @@ class batman_adv (moduleBase):
|
||||
|
||||
ifaceobjcurr.update_config_with_status ('batman-routing-algo', value_curr, value_ok)
|
||||
|
||||
|
||||
def _query_running (self, ifaceobjrunning):
|
||||
if not self.ipcmd.link_exists (ifaceobjrunning.name):
|
||||
return
|
||||
|
||||
# XXX Now what?
|
||||
|
||||
|
||||
_run_ops = {'pre-up' : _up,
|
||||
'post-down' : _down,
|
||||
'query-checkcurr' : _query_check}
|
||||
# XXX 'query-running' : _query_running}
|
||||
|
||||
_run_ops = {
|
||||
'pre-up': _up,
|
||||
'post-down': _down,
|
||||
'query-checkcurr': _query_check
|
||||
}
|
||||
|
||||
def get_ops (self):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys ()
|
||||
|
||||
|
||||
def _init_command_handlers (self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
|
||||
def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
|
||||
""" run B.A.T.M.A.N. configuration on the interface object passed as argument
|
||||
|
||||
@@ -389,8 +356,6 @@ class batman_adv (moduleBase):
|
||||
if (operation != 'query-running' and not self._is_batman_device (ifaceobj)):
|
||||
return
|
||||
|
||||
self._init_command_handlers ()
|
||||
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler (self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -11,136 +11,162 @@ import os
|
||||
from sets import Set
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
from ifupdown2.nlmanager.nlmanager import Link
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
|
||||
|
||||
import ifupdown2.ifupdown.policymanager as policymanager
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
from nlmanager.nlmanager import Link
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.netlink import netlink
|
||||
from ifupdown.statemanager import statemanager_api as statemanager
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown.policymanager as policymanager
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
class bond(moduleBase):
|
||||
class bond(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure bond interfaces """
|
||||
|
||||
overrides_ifupdown_scripts = ['ifenslave', ]
|
||||
|
||||
_modinfo = { 'mhelp' : 'bond configuration module',
|
||||
'attrs' : {
|
||||
'bond-use-carrier':
|
||||
{'help' : 'bond use carrier',
|
||||
'validvals' : ['yes', 'no', '0', '1'],
|
||||
'default' : 'yes',
|
||||
'example': ['bond-use-carrier yes']},
|
||||
'bond-num-grat-arp':
|
||||
{'help' : 'bond use carrier',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '1',
|
||||
'example' : ['bond-num-grat-arp 1']},
|
||||
'bond-num-unsol-na' :
|
||||
{'help' : 'bond slave devices',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '1',
|
||||
'example' : ['bond-num-unsol-na 1']},
|
||||
'bond-xmit-hash-policy' :
|
||||
{'help' : 'bond slave devices',
|
||||
'validvals' : ['0', 'layer2',
|
||||
'1', 'layer3+4',
|
||||
'2', 'layer2+3',
|
||||
'3', 'encap2+3',
|
||||
'4', 'encap3+4'],
|
||||
'default' : 'layer2',
|
||||
'example' : ['bond-xmit-hash-policy layer2']},
|
||||
'bond-miimon' :
|
||||
{'help' : 'bond miimon',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '0',
|
||||
'example' : ['bond-miimon 0']},
|
||||
'bond-mode' :
|
||||
{'help': 'bond mode',
|
||||
'validvals': ['0', 'balance-rr',
|
||||
'1', 'active-backup',
|
||||
'2', 'balance-xor',
|
||||
'3', 'broadcast',
|
||||
'4', '802.3ad',
|
||||
'5', 'balance-tlb',
|
||||
'6', 'balance-alb'],
|
||||
'default': 'balance-rr',
|
||||
'example': ['bond-mode 802.3ad']},
|
||||
'bond-lacp-rate':
|
||||
{'help' : 'bond lacp rate',
|
||||
'validvals' : ['0', 'slow', '1', 'fast'],
|
||||
'default' : '0',
|
||||
'example' : ['bond-lacp-rate 0']},
|
||||
'bond-min-links':
|
||||
{'help' : 'bond min links',
|
||||
'default' : '0',
|
||||
'validrange' : ['0', '255'],
|
||||
'example' : ['bond-min-links 0']},
|
||||
'bond-ad-sys-priority':
|
||||
{'help' : '802.3ad system priority',
|
||||
'default' : '65535',
|
||||
'validrange' : ['0', '65535'],
|
||||
'example' : ['bond-ad-sys-priority 65535'],
|
||||
'deprecated' : True,
|
||||
'new-attribute' : 'bond-ad-actor-sys-prio'},
|
||||
'bond-ad-actor-sys-prio':
|
||||
{'help' : '802.3ad system priority',
|
||||
'default' : '65535',
|
||||
'validrange' : ['0', '65535'],
|
||||
'example' : ['bond-ad-actor-sys-prio 65535']},
|
||||
'bond-ad-sys-mac-addr':
|
||||
{'help' : '802.3ad system mac address',
|
||||
'validvals': ['<mac>', ],
|
||||
'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00'],
|
||||
'deprecated' : True,
|
||||
'new-attribute' : 'bond-ad-actor-system'},
|
||||
'bond-ad-actor-system':
|
||||
{'help' : '802.3ad system mac address',
|
||||
'validvals': ['<mac>', ],
|
||||
'example' : ['bond-ad-actor-system 00:00:00:00:00:00'],},
|
||||
'bond-lacp-bypass-allow':
|
||||
{'help' : 'allow lacp bypass',
|
||||
'validvals' : ['yes', 'no', '0', '1'],
|
||||
'default' : 'no',
|
||||
'example' : ['bond-lacp-bypass-allow no']},
|
||||
'bond-slaves' :
|
||||
{'help' : 'bond slaves',
|
||||
'required' : True,
|
||||
'multivalue' : True,
|
||||
'validvals': ['<interface-list>'],
|
||||
'example' : ['bond-slaves swp1 swp2',
|
||||
'bond-slaves glob swp1-2',
|
||||
'bond-slaves regex (swp[1|2)'],
|
||||
'aliases': ['bond-ports']},
|
||||
'bond-updelay' :
|
||||
{'help' : 'bond updelay',
|
||||
'default' : '0',
|
||||
'validrange' : ['0', '65535'],
|
||||
'example' : ['bond-updelay 100']},
|
||||
'bond-downdelay':
|
||||
{'help' : 'bond downdelay',
|
||||
'default' : '0',
|
||||
'validrange' : ['0', '65535'],
|
||||
'example' : ['bond-downdelay 100']}
|
||||
}}
|
||||
_modinfo = {
|
||||
"mhelp": "bond configuration module",
|
||||
"attrs": {
|
||||
"bond-use-carrier": {
|
||||
"help": "bond use carrier",
|
||||
"validvals": ["yes", "no", "0", "1"],
|
||||
"default": "yes",
|
||||
"example": ["bond-use-carrier yes"]},
|
||||
"bond-num-grat-arp": {
|
||||
"help": "bond use carrier",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "1",
|
||||
"example": ["bond-num-grat-arp 1"]
|
||||
},
|
||||
"bond-num-unsol-na": {
|
||||
"help": "bond slave devices",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "1",
|
||||
"example": ["bond-num-unsol-na 1"]
|
||||
},
|
||||
"bond-xmit-hash-policy": {
|
||||
"help": "bond slave devices",
|
||||
"validvals": [
|
||||
"0", "layer2",
|
||||
"1", "layer3+4",
|
||||
"2", "layer2+3",
|
||||
"3", "encap2+3",
|
||||
"4", "encap3+4"
|
||||
],
|
||||
"default": "layer2",
|
||||
"example": ["bond-xmit-hash-policy layer2"]
|
||||
},
|
||||
"bond-miimon": {
|
||||
"help": "bond miimon",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "0",
|
||||
"example": ["bond-miimon 0"]
|
||||
},
|
||||
"bond-mode": {
|
||||
"help": "bond mode",
|
||||
"validvals": [
|
||||
"0", "balance-rr",
|
||||
"1", "active-backup",
|
||||
"2", "balance-xor",
|
||||
"3", "broadcast",
|
||||
"4", "802.3ad",
|
||||
"5", "balance-tlb",
|
||||
"6", "balance-alb"
|
||||
],
|
||||
"default": "balance-rr",
|
||||
"example": ["bond-mode 802.3ad"]
|
||||
},
|
||||
"bond-lacp-rate": {
|
||||
"help": "bond lacp rate",
|
||||
"validvals": ["0", "slow", "1", "fast"],
|
||||
"default": "0",
|
||||
"example": ["bond-lacp-rate 0"]
|
||||
},
|
||||
"bond-min-links": {
|
||||
"help": "bond min links",
|
||||
"default": "0",
|
||||
"validrange": ["0", "255"],
|
||||
"example": ["bond-min-links 0"]
|
||||
},
|
||||
"bond-ad-sys-priority": {
|
||||
"help": "802.3ad system priority",
|
||||
"default": "65535",
|
||||
"validrange": ["0", "65535"],
|
||||
"example": ["bond-ad-sys-priority 65535"],
|
||||
"deprecated": True,
|
||||
"new-attribute": "bond-ad-actor-sys-prio"
|
||||
},
|
||||
"bond-ad-actor-sys-prio": {
|
||||
"help": "802.3ad system priority",
|
||||
"default": "65535",
|
||||
"validrange": ["0", "65535"],
|
||||
"example": ["bond-ad-actor-sys-prio 65535"]
|
||||
},
|
||||
"bond-ad-sys-mac-addr": {
|
||||
"help": "802.3ad system mac address",
|
||||
"validvals": ["<mac>", ],
|
||||
"example": ["bond-ad-sys-mac-addr 00:00:00:00:00:00"],
|
||||
"deprecated": True,
|
||||
"new-attribute": "bond-ad-actor-system"
|
||||
},
|
||||
"bond-ad-actor-system": {
|
||||
"help": "802.3ad system mac address",
|
||||
"validvals": ["<mac>", ],
|
||||
"example": ["bond-ad-actor-system 00:00:00:00:00:00"],
|
||||
},
|
||||
"bond-lacp-bypass-allow": {
|
||||
"help": "allow lacp bypass",
|
||||
"validvals": ["yes", "no", "0", "1"],
|
||||
"default": "no",
|
||||
"example": ["bond-lacp-bypass-allow no"]
|
||||
},
|
||||
"bond-slaves": {
|
||||
"help": "bond slaves",
|
||||
"required": True,
|
||||
"multivalue": True,
|
||||
"validvals": ["<interface-list>"],
|
||||
"example": [
|
||||
"bond-slaves swp1 swp2",
|
||||
"bond-slaves glob swp1-2",
|
||||
"bond-slaves regex (swp[1|2)"
|
||||
],
|
||||
"aliases": ["bond-ports"]
|
||||
},
|
||||
"bond-updelay": {
|
||||
"help": "bond updelay",
|
||||
"default": "0",
|
||||
"validrange": ["0", "65535"],
|
||||
"example": ["bond-updelay 100"]
|
||||
},
|
||||
"bond-downdelay": {
|
||||
"help": "bond downdelay",
|
||||
"default": "0",
|
||||
"validrange": ["0", "65535"],
|
||||
"example": ["bond-downdelay 100"]
|
||||
},
|
||||
"bond-primary": {
|
||||
"help": "Control which slave interface is "
|
||||
"preferred active member",
|
||||
"example": ["bond-primary swp1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_bond_attr_netlink_map = {
|
||||
'bond-mode': Link.IFLA_BOND_MODE,
|
||||
@@ -157,7 +183,8 @@ class bond(moduleBase):
|
||||
'bond-ad-actor-sys-prio': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
|
||||
'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
|
||||
'bond-updelay': Link.IFLA_BOND_UPDELAY,
|
||||
'bond-downdelay': Link.IFLA_BOND_DOWNDELAY
|
||||
'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
|
||||
'bond-primary': Link.IFLA_BOND_PRIMARY
|
||||
}
|
||||
|
||||
# ifquery-check attr dictionary with callable object to translate user data to netlink format
|
||||
@@ -173,7 +200,8 @@ class bond(moduleBase):
|
||||
Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: int,
|
||||
Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
|
||||
Link.IFLA_BOND_UPDELAY: int,
|
||||
Link.IFLA_BOND_DOWNDELAY: int
|
||||
Link.IFLA_BOND_DOWNDELAY: int,
|
||||
# Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
|
||||
}
|
||||
|
||||
# ifup attr list with callable object to translate user data to netlink format
|
||||
@@ -194,13 +222,13 @@ class bond(moduleBase):
|
||||
('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
|
||||
('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
|
||||
('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
|
||||
('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
|
||||
('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str)
|
||||
# ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.ipcmd = None
|
||||
self.bondcmd = None
|
||||
|
||||
if not os.path.exists('/sys/class/net/bonding_masters'):
|
||||
try:
|
||||
@@ -208,6 +236,9 @@ class bond(moduleBase):
|
||||
except Exception as e:
|
||||
self.logger.info("bond: error while loading bonding module: %s" % str(e))
|
||||
|
||||
self._bond_attr_ifquery_check_translate_func[Link.IFLA_BOND_PRIMARY] = self.cache.get_ifindex
|
||||
self._bond_attr_set_list = self._bond_attr_set_list + (('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex),)
|
||||
|
||||
@staticmethod
|
||||
def get_bond_slaves(ifaceobj):
|
||||
slaves = ifaceobj.get_attr_value_first('bond-slaves')
|
||||
@@ -218,9 +249,7 @@ class bond(moduleBase):
|
||||
def _is_bond(self, ifaceobj):
|
||||
# at first link_kind is not set but once ifupdownmain
|
||||
# calls get_dependent_ifacenames link_kind is set to BOND
|
||||
if ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj):
|
||||
return True
|
||||
return False
|
||||
return ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj)
|
||||
|
||||
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
|
||||
""" Returns list of interfaces dependent on ifaceobj """
|
||||
@@ -244,8 +273,7 @@ class bond(moduleBase):
|
||||
return self.syntax_check_updown_delay(ifaceobj)
|
||||
|
||||
def get_dependent_ifacenames_running(self, ifaceobj):
|
||||
self._init_command_handlers()
|
||||
return self.bondcmd.bond_get_slaves(ifaceobj.name)
|
||||
return self.cache.get_slaves(ifaceobj.name)
|
||||
|
||||
def _get_slave_list(self, ifaceobj):
|
||||
""" Returns slave list present in ifaceobj config """
|
||||
@@ -264,8 +292,10 @@ class bond(moduleBase):
|
||||
If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
|
||||
"""
|
||||
try:
|
||||
if os.path.exists("/sys/class/net/%s/brport" % ifname):
|
||||
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
|
||||
for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
|
||||
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
|
||||
return
|
||||
except Exception, e:
|
||||
self.logger.info(str(e))
|
||||
|
||||
@@ -276,46 +306,44 @@ class bond(moduleBase):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _add_slaves(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
runningslaves = []
|
||||
|
||||
def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None):
|
||||
slaves = self._get_slave_list(ifaceobj)
|
||||
if not slaves:
|
||||
self.logger.debug('%s: no slaves found' %ifaceobj.name)
|
||||
return
|
||||
|
||||
if not ifupdownflags.flags.PERFMODE:
|
||||
runningslaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
|
||||
|
||||
clag_bond = self._is_clag_bond(ifaceobj)
|
||||
|
||||
for slave in Set(slaves).difference(Set(runningslaves)):
|
||||
if (not ifupdownflags.flags.PERFMODE and
|
||||
not self.ipcmd.link_exists(slave)):
|
||||
not self.cache.link_exists(slave)):
|
||||
self.log_error('%s: skipping slave %s, does not exist'
|
||||
%(ifaceobj.name, slave), ifaceobj,
|
||||
raise_error=False)
|
||||
continue
|
||||
link_up = False
|
||||
if self.ipcmd.is_link_up(slave):
|
||||
netlink.link_set_updown(slave, "down")
|
||||
if self.cache.link_is_up(slave):
|
||||
self.netlink.link_down_force(slave)
|
||||
link_up = True
|
||||
# If clag bond place the slave in a protodown state; clagd
|
||||
# will protoup it when it is ready
|
||||
if clag_bond:
|
||||
try:
|
||||
netlink.link_set_protodown(slave, "on")
|
||||
self.netlink.link_set_protodown_on(slave)
|
||||
except Exception, e:
|
||||
self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
|
||||
|
||||
self.enable_ipv6_if_prev_brport(slave)
|
||||
netlink.link_set_master(slave, ifaceobj.name)
|
||||
self.netlink.link_set_master(slave, ifaceobj.name)
|
||||
# TODO: if this fail we should switch to iproute2
|
||||
# start a batch: down - set master - up
|
||||
if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
|
||||
try:
|
||||
if (ifaceobj_getfunc(slave)[0].link_privflags &
|
||||
ifaceLinkPrivFlags.KEEP_LINK_DOWN):
|
||||
netlink.link_set_updown(slave, "down")
|
||||
self.netlink.link_down_force(slave)
|
||||
else:
|
||||
netlink.link_set_updown(slave, "up")
|
||||
self.netlink.link_up(slave)
|
||||
except Exception, e:
|
||||
self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
|
||||
pass
|
||||
@@ -323,22 +351,22 @@ class bond(moduleBase):
|
||||
if runningslaves:
|
||||
for s in runningslaves:
|
||||
if s not in slaves:
|
||||
self.bondcmd.bond_remove_slave(ifaceobj.name, s)
|
||||
self.sysfs.bond_remove_slave(ifaceobj.name, s)
|
||||
if clag_bond:
|
||||
try:
|
||||
netlink.link_set_protodown(s, "off")
|
||||
self.netlink.link_set_protodown_off(s)
|
||||
except Exception, e:
|
||||
self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
|
||||
else:
|
||||
# apply link-down config changes on running slaves
|
||||
try:
|
||||
link_up = self.ipcmd.is_link_up(s)
|
||||
link_up = self.cache.link_is_up(s)
|
||||
config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
|
||||
ifaceLinkPrivFlags.KEEP_LINK_DOWN)
|
||||
if (config_link_down and link_up):
|
||||
netlink.link_set_updown(s, "down")
|
||||
self.netlink.link_down_force(s)
|
||||
elif (not config_link_down and not link_up):
|
||||
netlink.link_set_updown(s, "up")
|
||||
self.netlink.link_up_force(s)
|
||||
except Exception, e:
|
||||
self.logger.warn('%s: %s' % (ifaceobj.name, str(e)))
|
||||
|
||||
@@ -396,7 +424,7 @@ class bond(moduleBase):
|
||||
"""
|
||||
ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
|
||||
if link_exists and ifla_bond_miimon is None:
|
||||
ifla_bond_miimon = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', Link.IFLA_BOND_MIIMON])
|
||||
ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
|
||||
|
||||
if ifla_bond_miimon == 0:
|
||||
for nl_attr, attr_name in self._bond_updown_delay_nl_list:
|
||||
@@ -417,7 +445,7 @@ class bond(moduleBase):
|
||||
def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
|
||||
ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
|
||||
if ifla_bond_mode is None and link_exists:
|
||||
ifla_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
|
||||
ifla_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
|
||||
# in this case the link already exists (we have a cached value):
|
||||
# if IFLA_BOND_MODE is not present in ifla_info_data it means:
|
||||
# - that bond-mode was present in the user config and didn't change
|
||||
@@ -430,7 +458,7 @@ class bond(moduleBase):
|
||||
if ifla_bond_mode == 4: # 802.3ad
|
||||
min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
|
||||
if min_links is None:
|
||||
min_links = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
|
||||
min_links = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MIN_LINKS)
|
||||
# get_min_links_nl may return None so we need to strictly check 0
|
||||
if min_links == 0:
|
||||
self.logger.warn('%s: attribute bond-min-links is set to \'0\'' % ifname)
|
||||
@@ -495,7 +523,7 @@ class bond(moduleBase):
|
||||
nl_value = func_ptr(user_config.lower())
|
||||
|
||||
if link_exists:
|
||||
cached_value = self.bondcmd.link_cache_get([ifname, 'linkinfo', netlink_attr])
|
||||
cached_value = self.cache.get_link_info_data_attribute(ifname, netlink_attr)
|
||||
|
||||
if link_exists and cached_value is None:
|
||||
# the link already exists but we don't have any value
|
||||
@@ -539,10 +567,10 @@ class bond(moduleBase):
|
||||
return True
|
||||
return False
|
||||
|
||||
def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data):
|
||||
def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data, bond_slaves):
|
||||
# if bond-mode was changed the bond needs to be brought
|
||||
# down and slaves un-slaved before bond mode is changed.
|
||||
cached_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
|
||||
cached_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
|
||||
ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
|
||||
|
||||
# bond-mode was changed or is not specified
|
||||
@@ -551,24 +579,26 @@ class bond(moduleBase):
|
||||
self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
|
||||
% (ifname, ifla_bond_mode))
|
||||
if is_link_up:
|
||||
netlink.link_set_updown(ifname, 'down')
|
||||
self.netlink.link_down(ifname)
|
||||
is_link_up = False
|
||||
|
||||
for lower_dev in ifaceobj.lowerifaces:
|
||||
netlink.link_set_nomaster(lower_dev)
|
||||
self.netlink.link_set_nomaster(lower_dev)
|
||||
try:
|
||||
bond_slaves.remove(lower_dev)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.bondcmd.cache_delete([ifname, 'linkinfo', 'slaves'])
|
||||
else:
|
||||
# bond-mode user config value is the current running(cached) value
|
||||
# no need to reset it again we can ignore this attribute
|
||||
del ifla_info_data[Link.IFLA_BOND_MODE]
|
||||
|
||||
return is_link_up
|
||||
return is_link_up, bond_slaves
|
||||
|
||||
def create_or_set_bond_config(self, ifaceobj):
|
||||
ifname = ifaceobj.name
|
||||
link_exists = self.ipcmd.link_exists(ifname)
|
||||
is_link_up = self.ipcmd.is_link_up(ifname) if link_exists else False
|
||||
link_exists, is_link_up = self.cache.link_exists_and_up(ifname)
|
||||
ifla_info_data = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
|
||||
|
||||
remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
|
||||
@@ -576,13 +606,21 @@ class bond(moduleBase):
|
||||
# if link exists: down link if specific attributes are specified
|
||||
if link_exists:
|
||||
# did bond-mode changed?
|
||||
is_link_up = self.should_update_bond_mode(ifaceobj, ifname, is_link_up, ifla_info_data)
|
||||
is_link_up, bond_slaves = self.should_update_bond_mode(
|
||||
ifaceobj,
|
||||
ifname,
|
||||
is_link_up,
|
||||
ifla_info_data,
|
||||
self.cache.get_slaves(ifname)
|
||||
)
|
||||
|
||||
# if specific attributes need to be set we need to down the bond first
|
||||
if ifla_info_data and is_link_up:
|
||||
if self._should_down_bond(ifla_info_data):
|
||||
netlink.link_set_updown(ifname, 'down')
|
||||
self.netlink.link_down_force(ifname)
|
||||
is_link_up = False
|
||||
else:
|
||||
bond_slaves = []
|
||||
|
||||
if link_exists and not ifla_info_data:
|
||||
# if the bond already exists and no attrs need to be set
|
||||
@@ -590,7 +628,7 @@ class bond(moduleBase):
|
||||
self.logger.info('%s: already exists, no change detected' % ifname)
|
||||
else:
|
||||
try:
|
||||
netlink.link_add_set(kind='bond', ifname=ifname, ifla_info_data=ifla_info_data)
|
||||
self.netlink.link_add_bond_with_info_data(ifname, ifla_info_data)
|
||||
except Exception as e:
|
||||
# defensive code
|
||||
# if anything happens, we try to set up the bond with the sysfs api
|
||||
@@ -603,30 +641,30 @@ class bond(moduleBase):
|
||||
ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
|
||||
ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
|
||||
|
||||
# if link_add doesn't raise we can update the cache, the future
|
||||
# netlink listener will update the cache based on the kernel response
|
||||
for key, value in ifla_info_data.items():
|
||||
self.bondcmd.cache_update([ifname, 'linkinfo', key], value)
|
||||
|
||||
if link_exists and ifla_info_data and not is_link_up:
|
||||
netlink.link_set_updown(ifname, 'up')
|
||||
self.netlink.link_up_force(ifname)
|
||||
|
||||
return bond_slaves
|
||||
|
||||
def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
self.bondcmd.create_bond(ifaceobj.name)
|
||||
self.bondcmd.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
self.sysfs.bond_create(ifaceobj.name)
|
||||
self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
|
||||
|
||||
def _up(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
try:
|
||||
self.create_or_set_bond_config(ifaceobj)
|
||||
self._add_slaves(ifaceobj, ifaceobj_getfunc)
|
||||
bond_slaves = self.create_or_set_bond_config(ifaceobj)
|
||||
self._add_slaves(
|
||||
ifaceobj,
|
||||
bond_slaves,
|
||||
ifaceobj_getfunc,
|
||||
)
|
||||
except Exception, e:
|
||||
self.log_error(str(e), ifaceobj)
|
||||
|
||||
def _down(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
try:
|
||||
netlink.link_del(ifaceobj.name)
|
||||
self.bondcmd.cache_delete([ifaceobj.name])
|
||||
self.netlink.link_del(ifaceobj.name)
|
||||
except Exception as e:
|
||||
self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
|
||||
|
||||
@@ -651,7 +689,7 @@ class bond(moduleBase):
|
||||
ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
|
||||
if not self.bondcmd.bond_exists(ifaceobj.name):
|
||||
if not self.cache.bond_exists(ifaceobj.name):
|
||||
self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
|
||||
return
|
||||
|
||||
@@ -673,7 +711,7 @@ class bond(moduleBase):
|
||||
query_slaves = True
|
||||
if not user_bond_slaves:
|
||||
user_bond_slaves = self._get_slave_list(ifaceobj)
|
||||
running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
|
||||
running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
|
||||
|
||||
self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
|
||||
except:
|
||||
@@ -684,7 +722,7 @@ class bond(moduleBase):
|
||||
# if user specified bond-ports we need to display it
|
||||
if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
|
||||
user_bond_slaves = self._get_slave_list(ifaceobj)
|
||||
running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
|
||||
running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
|
||||
|
||||
self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
|
||||
except:
|
||||
@@ -693,7 +731,7 @@ class bond(moduleBase):
|
||||
for attr in iface_attrs:
|
||||
nl_attr = self._bond_attr_netlink_map[attr]
|
||||
translate_func = self._bond_attr_ifquery_check_translate_func[nl_attr]
|
||||
current_config = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', nl_attr])
|
||||
current_config = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
|
||||
user_config = ifaceobj.get_attr_value_first(attr)
|
||||
|
||||
if current_config == translate_func(user_config):
|
||||
@@ -710,28 +748,35 @@ class bond(moduleBase):
|
||||
return 'fast' if value else 'slow'
|
||||
|
||||
def _query_running_attrs(self, bondname):
|
||||
cached_vxlan_ifla_info_data = self.cache.get_link_info_data(bondname)
|
||||
|
||||
bond_attrs = {
|
||||
'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])),
|
||||
'bond-miimon': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON]),
|
||||
'bond-use-carrier': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])),
|
||||
'bond-lacp-rate': self.translate_nl_value_slowfast(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])),
|
||||
'bond-min-links': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS]),
|
||||
'bond-ad-actor-system': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM]),
|
||||
'bond-ad-actor-sys-prio': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO]),
|
||||
'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])),
|
||||
'bond-lacp-bypass-allow': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])),
|
||||
'bond-num-unsol-na': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
|
||||
'bond-num-grat-arp': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
|
||||
'bond-updelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY]),
|
||||
'bond-downdelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
|
||||
'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MODE)),
|
||||
'bond-miimon': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIIMON),
|
||||
'bond-use-carrier': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_USE_CARRIER)),
|
||||
'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
|
||||
'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
|
||||
'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
|
||||
'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
|
||||
'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_XMIT_HASH_POLICY)),
|
||||
'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),
|
||||
'bond-num-unsol-na': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
|
||||
'bond-num-grat-arp': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
|
||||
'bond-updelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_UPDELAY),
|
||||
'bond-downdelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_DOWNDELAY)
|
||||
}
|
||||
slaves = self.bondcmd.bond_get_slaves(bondname)
|
||||
|
||||
cached_bond_primary = cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_PRIMARY)
|
||||
if cached_bond_primary:
|
||||
bond_attrs['bond-primary'] = self.cache.get_ifname(cached_bond_primary)
|
||||
|
||||
slaves = self.cache.get_slaves(bondname)
|
||||
if slaves:
|
||||
bond_attrs['bond-slaves'] = slaves
|
||||
return bond_attrs
|
||||
|
||||
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
|
||||
if not self.bondcmd.bond_exists(ifaceobjrunning.name):
|
||||
if not self.cache.bond_exists(ifaceobjrunning.name):
|
||||
return
|
||||
bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
|
||||
if bond_attrs.get('bond-slaves'):
|
||||
@@ -752,10 +797,6 @@ class bond(moduleBase):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = self.bondcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None,
|
||||
ifaceobj_getfunc=None):
|
||||
""" run bond configuration on the interface object passed as argument
|
||||
@@ -779,7 +820,6 @@ class bond(moduleBase):
|
||||
return
|
||||
if operation != 'query-running' and not self._is_bond(ifaceobj):
|
||||
return
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj,
|
||||
ifaceobj_getfunc=ifaceobj_getfunc)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -5,54 +5,59 @@
|
||||
#
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
|
||||
from ifupdown.iface import *
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
|
||||
class bridgevlan(moduleBase):
|
||||
class bridgevlan(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure vlan attributes on a vlan
|
||||
aware bridge """
|
||||
|
||||
_modinfo = {'mhelp' : 'bridgevlan module configures vlan attributes ' +
|
||||
'on a vlan aware bridge. This module only ' +
|
||||
'understands vlan interface name ' +
|
||||
'with dot notations. eg br0.100. where br0 is the ' +
|
||||
'vlan aware bridge this config is for',
|
||||
'attrs' : {
|
||||
'bridge-igmp-querier-src' :
|
||||
{ 'help' : 'bridge igmp querier src. Must be ' +
|
||||
'specified under the vlan interface',
|
||||
'validvals' : ['<ipv4>', ],
|
||||
'example' : ['bridge-igmp-querier-src 172.16.101.1']}}}
|
||||
_modinfo = {
|
||||
"mhelp": "bridgevlan module configures vlan attributes on a vlan aware "
|
||||
"bridge. This module only understands vlan interface name "
|
||||
"with dot notations. eg br0.100. where br0 is the vlan aware "
|
||||
"bridge this config is for",
|
||||
"attrs": {
|
||||
"bridge-igmp-querier-src": {
|
||||
"help": "bridge igmp querier src. Must be specified under "
|
||||
"the vlan interface",
|
||||
"validvals": ["<ipv4>"],
|
||||
"example": ["bridge-igmp-querier-src 172.16.101.1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.brctlcmd = None
|
||||
self.ipcmd = None
|
||||
|
||||
def _is_bridge_vlan_device(self, ifaceobj):
|
||||
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
|
||||
return True
|
||||
return False
|
||||
@staticmethod
|
||||
def _is_bridge_vlan_device(ifaceobj):
|
||||
return ifaceobj.type == ifaceType.BRIDGE_VLAN
|
||||
|
||||
def _get_bridge_n_vlan(self, ifaceobj):
|
||||
@staticmethod
|
||||
def _get_bridge_n_vlan(ifaceobj):
|
||||
vlist = ifaceobj.name.split('.', 1)
|
||||
if len(vlist) == 2:
|
||||
return (vlist[0], vlist[1])
|
||||
return vlist[0], vlist[1]
|
||||
return None
|
||||
|
||||
def _get_bridgename(self, ifaceobj):
|
||||
@staticmethod
|
||||
def _get_bridgename(ifaceobj):
|
||||
vlist = ifaceobj.name.split('.', 1)
|
||||
if len(vlist) == 2:
|
||||
return vlist[0]
|
||||
@@ -68,18 +73,18 @@ class bridgevlan(moduleBase):
|
||||
(bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
|
||||
vlanid = int(vlan, 10)
|
||||
except:
|
||||
self.log_error('%s: bridge vlan interface name ' %ifaceobj.name +
|
||||
'does not correspond to format (eg. br0.100)', ifaceobj)
|
||||
self.log_error("%s: bridge vlan interface name does not correspond "
|
||||
"to format (eg. br0.100)" % ifaceobj.name, ifaceobj)
|
||||
raise
|
||||
|
||||
if not self.ipcmd.link_exists(bridgename):
|
||||
if not self.cache.link_exists(bridgename):
|
||||
#self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
|
||||
# bridgename))
|
||||
return
|
||||
|
||||
running_mcqv4src = {}
|
||||
if not ifupdownflags.flags.PERFMODE:
|
||||
running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(bridgename)
|
||||
running_mcqv4src = self.sysfs.bridge_get_mcqv4src(bridgename)
|
||||
if running_mcqv4src:
|
||||
r_mcqv4src = running_mcqv4src.get(vlan)
|
||||
else:
|
||||
@@ -87,37 +92,37 @@ class bridgevlan(moduleBase):
|
||||
mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
|
||||
if not mcqv4src:
|
||||
if r_mcqv4src:
|
||||
self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
|
||||
self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
|
||||
return
|
||||
|
||||
if r_mcqv4src and r_mcqv4src != mcqv4src:
|
||||
self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
|
||||
self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
|
||||
self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
|
||||
self.iproute2.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
|
||||
else:
|
||||
self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
|
||||
self.iproute2.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
|
||||
|
||||
def _down(self, ifaceobj):
|
||||
try:
|
||||
(bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
|
||||
vlanid = int(vlan, 10)
|
||||
except:
|
||||
self.logger.warn('%s: bridge vlan interface name ' %ifaceobj.name +
|
||||
'does not correspond to format (eg. br0.100)')
|
||||
self.logger.warn("%s: bridge vlan interface name does not "
|
||||
"correspond to format (eg. br0.100)" % ifaceobj.name)
|
||||
raise
|
||||
|
||||
if not self.ipcmd.link_exists(bridgename):
|
||||
if not self.cache.link_exists(bridgename):
|
||||
#self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
|
||||
# bridgename))
|
||||
return
|
||||
mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
|
||||
if mcqv4src:
|
||||
self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
|
||||
self.iproute2.bridge_del_mcqv4src(bridgename, vlanid)
|
||||
|
||||
def _query_running_bridge_igmp_querier_src(self, ifaceobj):
|
||||
(bridgename, vlanid) = ifaceobj.name.split('.')
|
||||
running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(bridgename)
|
||||
running_mcqv4src = self.sysfs.bridge_get_mcqv4src(bridgename)
|
||||
if running_mcqv4src:
|
||||
return running_mcqv4src.get(vlanid)
|
||||
return running_mcqv4src.get(vlanid)
|
||||
return None
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
@@ -133,32 +138,24 @@ class bridgevlan(moduleBase):
|
||||
ifaceobjcurr.status = ifaceStatus.SUCCESS
|
||||
return
|
||||
|
||||
def _query_running(self, ifaceobjrunning):
|
||||
# XXX not supported
|
||||
return
|
||||
|
||||
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
|
||||
ret = True
|
||||
bvlan_intf = self._is_bridge_vlan_device(ifaceobj)
|
||||
if (ifaceobj.get_attr_value_first('bridge-igmp-querier-src') and
|
||||
not bvlan_intf):
|
||||
self.logger.error('%s: bridge-igmp-querier-src only allowed under vlan stanza' %ifaceobj.name)
|
||||
if (ifaceobj.get_attr_value_first('bridge-igmp-querier-src') and not bvlan_intf):
|
||||
self.logger.error('%s: bridge-igmp-querier-src only allowed under vlan stanza' % ifaceobj.name)
|
||||
ret = False
|
||||
return ret
|
||||
|
||||
_run_ops = {'pre-up' : _up,
|
||||
'post-down' : _down,
|
||||
'query-checkcurr' : _query_check,
|
||||
'query-running' : _query_running}
|
||||
_run_ops = {
|
||||
"pre-up": _up,
|
||||
"post-down": _down,
|
||||
"query-checkcurr": _query_check,
|
||||
}
|
||||
|
||||
def get_ops(self):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = self.brctlcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
""" run vlan configuration on the interface object passed as argument
|
||||
|
||||
@@ -178,18 +175,15 @@ class bridgevlan(moduleBase):
|
||||
op_handler = self._run_ops.get(operation)
|
||||
if not op_handler:
|
||||
return
|
||||
if (operation != 'query-running' and
|
||||
not self._is_bridge_vlan_device(ifaceobj)):
|
||||
if (operation != 'query-running' and not self._is_bridge_vlan_device(ifaceobj)):
|
||||
# most common problem is people specify BRIDGE_VLAN
|
||||
# attribute on a bridge or a vlan device, which
|
||||
# is incorrect. So, catch them here and warn before
|
||||
# giving up processing the interface
|
||||
if ((ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
|
||||
ifaceobj.link_kind & ifaceLinkKind.VLAN) and
|
||||
not self.syntax_check(ifaceobj, None)):
|
||||
if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or ifaceobj.link_kind & ifaceLinkKind.VLAN) \
|
||||
and not self.syntax_check(ifaceobj, None):
|
||||
ifaceobj.status = ifaceStatus.ERROR
|
||||
return
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -6,8 +6,11 @@
|
||||
|
||||
import re
|
||||
import time
|
||||
import socket
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
|
||||
import ifupdown2.ifupdown.policymanager as policymanager
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
@@ -15,9 +18,10 @@ try:
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
|
||||
from ifupdown2.ifupdownaddons.dhclient import dhclient
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
|
||||
import ifupdown.policymanager as policymanager
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
@@ -25,46 +29,16 @@ except ImportError:
|
||||
from ifupdown.utils import utils
|
||||
|
||||
from ifupdownaddons.dhclient import dhclient
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
|
||||
class dhcp(moduleBase):
|
||||
class dhcp(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure dhcp on interface """
|
||||
|
||||
_modinfo = {
|
||||
"mhelp": "Configure dhcp",
|
||||
"policies": {
|
||||
"dhcp6-duid": {
|
||||
"help": "Override the default when selecting the type of DUID to use. By default, DHCPv6 dhclient "
|
||||
"creates an identifier based on the link-layer address (DUID-LL) if it is running in stateless "
|
||||
"mode (with -S, not requesting an address), or it creates an identifier based on the "
|
||||
"link-layer address plus a timestamp (DUID-LLT) if it is running in stateful mode (without -S, "
|
||||
"requesting an address). When DHCPv4 is configured to use a DUID using -i option the default "
|
||||
"is to use a DUID-LLT. -D overrides these default, with a value of either LL or LLT.",
|
||||
"validvals": ["LL", "LLT"],
|
||||
"example": ["dhcp6-duid LL"]
|
||||
},
|
||||
"dhcp-wait": {
|
||||
"help": "Wait or not wait and become a daemon immediately (nowait) rather than waiting until an "
|
||||
"IP address has been acquired. If not specified default value is true, that is to wait.",
|
||||
"validvals": ["true", "false"],
|
||||
"example": ["dhcp-wait false"]
|
||||
},
|
||||
"dhcp6-ll-wait": {
|
||||
"help": "Overrides the default wait time before DHCPv6 client is started. During this wait time, "
|
||||
"ifupdown2 checks if the interface requesting an address has a valid link-local address. "
|
||||
"If not specified default value used is 10 seconds.",
|
||||
"validvals": ["whole numbers"],
|
||||
"example": ["dhcp6-ll-wait 0"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.dhclientcmd = dhclient(**kargs)
|
||||
self.ipcmd = None
|
||||
|
||||
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
|
||||
return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
|
||||
@@ -99,10 +73,9 @@ class dhcp(moduleBase):
|
||||
pass
|
||||
dhcp6_duid = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
|
||||
ifname=ifaceobj.name, attr='dhcp6-duid')
|
||||
|
||||
vrf = ifaceobj.get_attr_value_first('vrf')
|
||||
if (vrf and self.vrf_exec_cmd_prefix and
|
||||
self.ipcmd.link_exists(vrf)):
|
||||
self.cache.link_exists(vrf)):
|
||||
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
|
||||
|
||||
if 'inet' in ifaceobj.addr_family:
|
||||
@@ -140,8 +113,7 @@ class dhcp(moduleBase):
|
||||
#add delay before starting IPv6 dhclient to
|
||||
#make sure the configured interface/link is up.
|
||||
if timeout > 1:
|
||||
time.sleep(1)
|
||||
|
||||
time.sleep(1)
|
||||
while timeout:
|
||||
addr_output = utils.exec_command('%s -6 addr show %s'
|
||||
%(utils.ip_cmd, ifaceobj.name))
|
||||
@@ -173,18 +145,20 @@ class dhcp(moduleBase):
|
||||
dhclient_cmd_prefix = None
|
||||
vrf = ifaceobj.get_attr_value_first('vrf')
|
||||
if (vrf and self.vrf_exec_cmd_prefix and
|
||||
self.ipcmd.link_exists(vrf)):
|
||||
self.cache.link_exists(vrf)):
|
||||
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
|
||||
dhcp6_duid = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
|
||||
ifname=ifaceobj.name, attr='dhcp6-duid')
|
||||
ifname=ifaceobj.name, attr='dhcp6-duid')
|
||||
if 'inet6' in ifaceobj.addr_family:
|
||||
self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix, duid=dhcp6_duid)
|
||||
self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6)
|
||||
if 'inet' in ifaceobj.addr_family:
|
||||
self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
|
||||
self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET)
|
||||
|
||||
def _down(self, ifaceobj):
|
||||
self._dhcp_down(ifaceobj)
|
||||
self.ipcmd.link_down(ifaceobj.name)
|
||||
self.netlink.link_down(ifaceobj.name)
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
status = ifaceStatus.SUCCESS
|
||||
@@ -210,7 +184,7 @@ class dhcp(moduleBase):
|
||||
ifaceobjcurr.status = status
|
||||
|
||||
def _query_running(self, ifaceobjrunning):
|
||||
if not self.ipcmd.link_exists(ifaceobjrunning.name):
|
||||
if not self.cache.link_exists(ifaceobjrunning.name):
|
||||
return
|
||||
if self.dhclientcmd.is_running(ifaceobjrunning.name):
|
||||
ifaceobjrunning.addr_family.append('inet')
|
||||
@@ -229,10 +203,6 @@ class dhcp(moduleBase):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
""" run dhcp configuration on the interface object passed as argument
|
||||
|
||||
@@ -262,7 +232,6 @@ class dhcp(moduleBase):
|
||||
return
|
||||
if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
|
||||
return
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -7,6 +7,8 @@
|
||||
import os
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown2.ifupdown.policymanager as policymanager
|
||||
|
||||
@@ -15,9 +17,10 @@ try:
|
||||
from ifupdown2.ifupdown.exceptions import moduleNotSupported
|
||||
|
||||
from ifupdown2.ifupdownaddons.utilsbase import *
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown.policymanager as policymanager
|
||||
|
||||
@@ -26,51 +29,64 @@ except ImportError:
|
||||
from ifupdown.exceptions import moduleNotSupported
|
||||
|
||||
from ifupdownaddons.utilsbase import *
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
|
||||
class ethtool(moduleBase,utilsBase):
|
||||
class ethtool(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure ethtool attributes """
|
||||
|
||||
_modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
|
||||
'attrs': {
|
||||
'link-speed' :
|
||||
{'help' : 'set link speed',
|
||||
'validvals' : ['10',
|
||||
'100',
|
||||
'1000',
|
||||
'10000',
|
||||
'25000',
|
||||
'40000',
|
||||
'50000',
|
||||
'100000'],
|
||||
'example' : ['link-speed 1000'],
|
||||
'default' : 'varies by platform and port'},
|
||||
'link-duplex' :
|
||||
{'help': 'set link duplex',
|
||||
'example' : ['link-duplex full'],
|
||||
'validvals' : ['half', 'full'],
|
||||
'default' : 'full'},
|
||||
'link-autoneg' :
|
||||
{'help': 'set autonegotiation',
|
||||
'example' : ['link-autoneg on'],
|
||||
'validvals' : ['yes', 'no', 'on', 'off'],
|
||||
'default' : 'varies by platform and port'},
|
||||
'link-fec' :
|
||||
{'help': 'set forward error correction mode',
|
||||
'example' : ['link-fec rs'],
|
||||
'validvals' : ['rs', 'baser', 'auto', 'off'],
|
||||
'default' : 'varies by platform and port'}}}
|
||||
_modinfo = {
|
||||
"mhelp": "ethtool configuration module for interfaces",
|
||||
"attrs": {
|
||||
"link-speed": {
|
||||
"help": "set link speed",
|
||||
"validvals": [
|
||||
"10",
|
||||
"100",
|
||||
"1000",
|
||||
"10000",
|
||||
"25000",
|
||||
"40000",
|
||||
"50000",
|
||||
"100000"
|
||||
],
|
||||
"example": ["link-speed 1000"],
|
||||
"default": "varies by platform and port"
|
||||
},
|
||||
"link-duplex": {
|
||||
"help": "set link duplex",
|
||||
"example": ["link-duplex full"],
|
||||
"validvals": ["half", "full"],
|
||||
"default": "full"
|
||||
},
|
||||
"link-autoneg": {
|
||||
"help": "set autonegotiation",
|
||||
"example": ["link-autoneg on"],
|
||||
"validvals": ["yes", "no", "on", "off"],
|
||||
"default": "varies by platform and port"
|
||||
},
|
||||
"link-fec": {
|
||||
"help": "set forward error correction mode",
|
||||
"example": ["link-fec rs"],
|
||||
"validvals": ["rs", "baser", "auto", "off"],
|
||||
"default": "varies by platform and port"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
if not os.path.exists(utils.ethtool_cmd):
|
||||
raise moduleNotSupported('module init failed: %s: not found' % utils.ethtool_cmd)
|
||||
self.ipcmd = None
|
||||
# keep a list of iface objects who have modified link attributes
|
||||
self.ifaceobjs_modified_configs = []
|
||||
|
||||
self.ethtool_ignore_errors = policymanager.policymanager_api.get_module_globals(
|
||||
module_name=self.__class__.__name__,
|
||||
attr='ethtool_ignore_errors'
|
||||
)
|
||||
|
||||
def do_fec_settings(self, ifaceobj):
|
||||
feccmd = ''
|
||||
|
||||
@@ -94,10 +110,6 @@ class ethtool(moduleBase,utilsBase):
|
||||
if default_val:
|
||||
default_val = default_val.lower()
|
||||
|
||||
if running_val in ["none", "notsupported"]:
|
||||
# None and NotSupported ethtool FEC values mean "off"
|
||||
running_val = "off"
|
||||
|
||||
# check running values
|
||||
if config_val and config_val == running_val:
|
||||
return
|
||||
@@ -120,7 +132,8 @@ class ethtool(moduleBase,utilsBase):
|
||||
(utils.ethtool_cmd, ifaceobj.name, feccmd))
|
||||
utils.exec_command(feccmd)
|
||||
except Exception, e:
|
||||
self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
|
||||
if not self.ethtool_ignore_errors:
|
||||
self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
|
||||
else:
|
||||
pass
|
||||
|
||||
@@ -214,14 +227,15 @@ class ethtool(moduleBase,utilsBase):
|
||||
cmd = ('%s -s %s %s' % (utils.ethtool_cmd, ifaceobj.name, cmd))
|
||||
utils.exec_command(cmd)
|
||||
except Exception, e:
|
||||
self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
|
||||
if not self.ethtool_ignore_errors:
|
||||
self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
|
||||
|
||||
def _pre_up(self, ifaceobj, operation='post_up'):
|
||||
"""
|
||||
_pre_up and _pre_down will reset the layer 2 attributes to default policy
|
||||
settings.
|
||||
"""
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
|
||||
self.do_speed_settings(ifaceobj)
|
||||
@@ -306,9 +320,9 @@ class ethtool(moduleBase,utilsBase):
|
||||
"""
|
||||
try:
|
||||
for attr in ethtool_output.splitlines():
|
||||
if attr.startswith('FEC encodings'):
|
||||
if attr.startswith('Configured FEC encodings:'):
|
||||
fec_attrs = attr.split()
|
||||
return(fec_attrs[fec_attrs.index(':')+1])
|
||||
return(fec_attrs[fec_attrs.index('encodings:')+1])
|
||||
except Exception as e:
|
||||
self.logger.debug('ethtool: problems in ethtool set-fec output'
|
||||
' %s: %s' %(ethtool_output.splitlines(), str(e)))
|
||||
@@ -328,13 +342,14 @@ class ethtool(moduleBase,utilsBase):
|
||||
(utils.ethtool_cmd, ifaceobj.name))
|
||||
running_attr = self.get_fec_encoding(ethtool_output=output)
|
||||
else:
|
||||
running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \
|
||||
running_attr = self.io.read_file_oneline('/sys/class/net/%s/%s' % \
|
||||
(ifaceobj.name, attr))
|
||||
except Exception as e:
|
||||
# for nonexistent interfaces, we get an error (rc = 256 or 19200)
|
||||
self.logger.debug('ethtool: problems calling ethtool or reading'
|
||||
' /sys/class on iface %s for attr %s: %s' %
|
||||
(ifaceobj.name, attr, str(e)))
|
||||
if not self.ethtool_ignore_errors:
|
||||
# for nonexistent interfaces, we get an error (rc = 256 or 19200)
|
||||
self.logger.debug('ethtool: problems calling ethtool or reading'
|
||||
' /sys/class on iface %s for attr %s: %s' %
|
||||
(ifaceobj.name, attr, str(e)))
|
||||
return running_attr
|
||||
|
||||
|
||||
@@ -347,7 +362,7 @@ class ethtool(moduleBase,utilsBase):
|
||||
"""
|
||||
# do not bother showing swp ifaces that are not up for the speed
|
||||
# duplex and autoneg are not reliable.
|
||||
if not self.ipcmd.is_link_up(ifaceobj.name):
|
||||
if not self.cache.link_is_up(ifaceobj.name):
|
||||
return
|
||||
for attr in ['speed', 'duplex', 'autoneg']:
|
||||
default_val = policymanager.policymanager_api.get_iface_default(
|
||||
@@ -396,10 +411,6 @@ class ethtool(moduleBase,utilsBase):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
""" run ethtool configuration on the interface object passed as
|
||||
argument
|
||||
@@ -423,7 +434,6 @@ class ethtool(moduleBase,utilsBase):
|
||||
op_handler = self._run_ops.get(operation)
|
||||
if not op_handler:
|
||||
return
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -10,59 +10,64 @@
|
||||
# loopback or dummy.
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown2.ifupdown.policymanager as policymanager
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown.policymanager as policymanager
|
||||
|
||||
|
||||
class link(moduleBase):
|
||||
_modinfo = {'mhelp' : 'create/configure link types. similar to ip-link',
|
||||
'attrs' : {
|
||||
'link-type' :
|
||||
{'help' : 'type of link as in \'ip link\' command.',
|
||||
'validvals' : ['dummy', 'veth'],
|
||||
'example' : ['link-type <dummy|veth>']},
|
||||
'link-down' :
|
||||
{'help': 'keep link down',
|
||||
'example' : ['link-down yes/no'],
|
||||
'default' : 'no',
|
||||
'validvals' : ['yes', 'no']}}}
|
||||
class link(Addon, moduleBase):
|
||||
_modinfo = {
|
||||
"mhelp": "create/configure link types. similar to ip-link",
|
||||
"attrs": {
|
||||
"link-type": {
|
||||
"help": "type of link as in 'ip link' command.",
|
||||
"validvals": ["dummy", "veth"],
|
||||
"example": ["link-type <dummy|veth>"]
|
||||
},
|
||||
"link-down": {
|
||||
"help": "keep link down",
|
||||
"example": ["link-down yes/no"],
|
||||
"default": "no",
|
||||
"validvals": ["yes", "no"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.ipcmd = None
|
||||
|
||||
self.check_physical_port_existance = utils.get_boolean_from_string(policymanager.policymanager_api.get_module_globals(
|
||||
self.__class__.__name__,
|
||||
'warn_on_physdev_not_present'
|
||||
))
|
||||
self.check_physical_port_existance = utils.get_boolean_from_string(
|
||||
policymanager.policymanager_api.get_module_globals(
|
||||
self.__class__.__name__,
|
||||
'warn_on_physdev_not_present'
|
||||
)
|
||||
)
|
||||
|
||||
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
|
||||
if self.check_physical_port_existance:
|
||||
if not ifaceobj.link_kind and not LinkUtils.link_exists(ifaceobj.name):
|
||||
if not ifaceobj.link_kind and not self.cache.link_exists(ifaceobj.name):
|
||||
self.logger.warning('%s: interface does not exist' % ifaceobj.name)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_my_interface(self, ifaceobj):
|
||||
if (ifaceobj.get_attr_value_first('link-type')
|
||||
or ifaceobj.get_attr_value_first('link-down')):
|
||||
return True
|
||||
return False
|
||||
@staticmethod
|
||||
def _is_my_interface(ifaceobj):
|
||||
return ifaceobj.get_attr_value_first('link-type') or ifaceobj.get_attr_value_first('link-down')
|
||||
|
||||
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
|
||||
if ifaceobj.get_attr_value_first('link-down') == 'yes':
|
||||
@@ -73,36 +78,32 @@ class link(moduleBase):
|
||||
def _up(self, ifaceobj):
|
||||
link_type = ifaceobj.get_attr_value_first('link-type')
|
||||
if link_type:
|
||||
self.ipcmd.link_create(ifaceobj.name,
|
||||
ifaceobj.get_attr_value_first('link-type'))
|
||||
self.netlink.link_add(ifname=ifaceobj.name, kind=link_type)
|
||||
|
||||
def _down(self, ifaceobj):
|
||||
if not ifaceobj.get_attr_value_first('link-type'):
|
||||
return
|
||||
if (not ifupdownflags.flags.PERFMODE and
|
||||
not self.ipcmd.link_exists(ifaceobj.name)):
|
||||
return
|
||||
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
try:
|
||||
self.ipcmd.link_delete(ifaceobj.name)
|
||||
self.netlink.link_del(ifaceobj.name)
|
||||
except Exception, e:
|
||||
self.log_warn(str(e))
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
if ifaceobj.get_attr_value('link-type'):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
ifaceobjcurr.update_config_with_status('link-type', 'None', 1)
|
||||
else:
|
||||
link_type = ifaceobj.get_attr_value_first('link-type')
|
||||
if self.ipcmd.link_get_kind(ifaceobj.name) == link_type:
|
||||
ifaceobjcurr.update_config_with_status('link-type',
|
||||
link_type, 0)
|
||||
if self.cache.get_link_kind(ifaceobj.name) == link_type:
|
||||
ifaceobjcurr.update_config_with_status('link-type', link_type, 0)
|
||||
else:
|
||||
ifaceobjcurr.update_config_with_status('link-type',
|
||||
link_type, 1)
|
||||
ifaceobjcurr.update_config_with_status('link-type', link_type, 1)
|
||||
|
||||
link_down = ifaceobj.get_attr_value_first('link-down')
|
||||
if link_down:
|
||||
link_up = self.ipcmd.is_link_up(ifaceobj.name)
|
||||
link_up = self.cache.link_is_up(ifaceobj.name)
|
||||
link_should_be_down = utils.get_boolean_from_string(link_down)
|
||||
|
||||
if link_should_be_down and link_up:
|
||||
@@ -117,17 +118,15 @@ class link(moduleBase):
|
||||
|
||||
ifaceobjcurr.update_config_with_status('link-down', link_down, status)
|
||||
|
||||
_run_ops = {'pre-up' : _up,
|
||||
'post-down' : _down,
|
||||
'query-checkcurr' : _query_check}
|
||||
_run_ops = {
|
||||
"pre-up": _up,
|
||||
"post-down": _down,
|
||||
"query-checkcurr": _query_check
|
||||
}
|
||||
|
||||
def get_ops(self):
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
op_handler = self._run_ops.get(operation)
|
||||
if not op_handler:
|
||||
@@ -135,7 +134,6 @@ class link(moduleBase):
|
||||
if (operation != 'query-running' and
|
||||
not self._is_my_interface(ifaceobj)):
|
||||
return
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -9,27 +9,27 @@ import os
|
||||
from sets import Set
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown2.ifupdown.policymanager as policymanager
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
from ifupdown2.ifupdownaddons.mstpctlutil import mstpctlutil
|
||||
from ifupdown2.ifupdownaddons.systemutils import systemUtils
|
||||
from ifupdown2.ifupdown.exceptions import moduleNotSupported
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.netlink import netlink
|
||||
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown.policymanager as policymanager
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
from ifupdownaddons.mstpctlutil import mstpctlutil
|
||||
from ifupdownaddons.systemutils import systemUtils
|
||||
@@ -39,176 +39,221 @@ except ImportError:
|
||||
class mstpctlFlags:
|
||||
PORT_PROCESSED = 0x1
|
||||
|
||||
class mstpctl(moduleBase):
|
||||
class mstpctl(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure mstp attributes """
|
||||
|
||||
_modinfo = {'mhelp' : 'mstp configuration module for bridges',
|
||||
'attrs' : {
|
||||
'mstpctl-ports' :
|
||||
{'help' : 'mstp ports',
|
||||
'compat' : True,
|
||||
'deprecated': True,
|
||||
'new-attribute': 'bridge-ports'},
|
||||
'mstpctl-stp' :
|
||||
{'help': 'bridge stp yes/no',
|
||||
'validvals' : ['yes', 'no', 'on', 'off'],
|
||||
'compat' : True,
|
||||
'default' : 'no',
|
||||
'deprecated': True,
|
||||
'new-attribute': 'bridge-stp'},
|
||||
'mstpctl-treeprio' :
|
||||
{'help': 'tree priority',
|
||||
'default' : '32768',
|
||||
'validvals' : ['0', '4096', '8192', '12288', '16384',
|
||||
'20480', '24576', '28672', '32768',
|
||||
'36864', '40960', '45056', '49152',
|
||||
'53248', '57344', '61440'],
|
||||
'required' : False,
|
||||
'example' : ['mstpctl-treeprio 32768']},
|
||||
'mstpctl-ageing' :
|
||||
{'help': 'ageing time',
|
||||
'validrange' : ['0', '4096'],
|
||||
'default' : '300',
|
||||
'required' : False,
|
||||
'jsonAttr': 'ageingTime',
|
||||
'example' : ['mstpctl-ageing 300']},
|
||||
'mstpctl-maxage' :
|
||||
{ 'help' : 'max message age',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '20',
|
||||
'jsonAttr': 'bridgeMaxAge',
|
||||
'required' : False,
|
||||
'example' : ['mstpctl-maxage 20']},
|
||||
'mstpctl-fdelay' :
|
||||
{ 'help' : 'set forwarding delay',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '15',
|
||||
'jsonAttr': 'bridgeFwdDelay',
|
||||
'required' : False,
|
||||
'example' : ['mstpctl-fdelay 15']},
|
||||
'mstpctl-maxhops' :
|
||||
{ 'help' : 'bridge max hops',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '20',
|
||||
'jsonAttr': 'maxHops',
|
||||
'required' : False,
|
||||
'example' : ['mstpctl-maxhops 15']},
|
||||
'mstpctl-txholdcount' :
|
||||
{ 'help' : 'bridge transmit holdcount',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '6',
|
||||
'jsonAttr': 'txHoldCounter',
|
||||
'required' : False,
|
||||
'example' : ['mstpctl-txholdcount 6']},
|
||||
'mstpctl-forcevers' :
|
||||
{ 'help' : 'bridge force stp version',
|
||||
'validvals' : ['rstp', ],
|
||||
'default' : 'rstp',
|
||||
'required' : False,
|
||||
'jsonAttr': 'forceProtocolVersion',
|
||||
'example' : ['mstpctl-forcevers rstp']},
|
||||
'mstpctl-portpathcost' :
|
||||
{ 'help' : 'bridge port path cost',
|
||||
'validvals': ['<interface-range-list>'],
|
||||
'validrange' : ['0', '65535'],
|
||||
'default' : '0',
|
||||
'jsonAttr' : 'adminExtPortCost',
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-portpathcost swp1=0 swp2=1',
|
||||
'under the port (recommended): mstpctl-portpathcost 0']},
|
||||
'mstpctl-portp2p' :
|
||||
{ 'help' : 'bridge port p2p detection mode',
|
||||
'default' : 'auto',
|
||||
'jsonAttr' : 'adminPointToPoint',
|
||||
'validvals' : ['<interface-yes-no-auto-list>'],
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-portp2p swp1=yes swp2=no',
|
||||
'under the port (recommended): mstpctl-portp2p yes']},
|
||||
'mstpctl-portrestrrole' :
|
||||
{ 'help' :
|
||||
'enable/disable port ability to take root role of the port',
|
||||
'default' : 'no',
|
||||
'jsonAttr' : 'restrictedRole',
|
||||
'validvals' : ['<interface-yes-no-list>'],
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-portrestrrole swp1=yes swp2=no',
|
||||
'under the port (recommended): mstpctl-portrestrrole yes']},
|
||||
'mstpctl-portrestrtcn' :
|
||||
{ 'help' :
|
||||
'enable/disable port ability to propagate received topology change notification of the port',
|
||||
'default' : 'no',
|
||||
'jsonAttr' : 'restrictedTcn',
|
||||
'validvals' : ['<interface-yes-no-list>'],
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no',
|
||||
'under the port (recommended): mstpctl-portrestrtcn yes']},
|
||||
'mstpctl-bpduguard' :
|
||||
{ 'help' :
|
||||
'enable/disable bpduguard',
|
||||
'default' : 'no',
|
||||
'jsonAttr' : 'bpduGuardPort',
|
||||
'validvals' : ['<interface-yes-no-list>'],
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-bpduguard swp1=yes swp2=no',
|
||||
'under the port (recommended): mstpctl-bpduguard yes']},
|
||||
'mstpctl-treeportprio' :
|
||||
{ 'help': 'Sets the <port>\'s priority MSTI instance. '
|
||||
'The priority value must be a number between 0 and 240 and a multiple of 16.',
|
||||
'default' : '128',
|
||||
'validvals': ['<interface-range-list-multiple-of-16>'],
|
||||
'validrange' : ['0', '240'],
|
||||
'jsonAttr': 'treeportprio',
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-treeportprio swp1=128 swp2=128',
|
||||
'under the port (recommended): mstpctl-treeportprio 128']},
|
||||
'mstpctl-hello' :
|
||||
{ 'help' : 'set hello time',
|
||||
'validrange' : ['0', '255'],
|
||||
'default' : '2',
|
||||
'required' : False,
|
||||
'jsonAttr': 'helloTime',
|
||||
'example' : ['mstpctl-hello 2']},
|
||||
'mstpctl-portnetwork' :
|
||||
{ 'help' : 'enable/disable bridge assurance capability for a port',
|
||||
'validvals' : ['<interface-yes-no-list>'],
|
||||
'default' : 'no',
|
||||
'jsonAttr' : 'networkPort',
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-portnetwork swp1=yes swp2=no',
|
||||
'under the port (recommended): mstpctl-portnetwork yes']},
|
||||
'mstpctl-portadminedge' :
|
||||
{ 'help' : 'enable/disable initial edge state of the port',
|
||||
'validvals' : ['<interface-yes-no-list>'],
|
||||
'default' : 'no',
|
||||
'jsonAttr' : 'adminEdgePort',
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-portadminedge swp1=yes swp2=no',
|
||||
'under the port (recommended): mstpctl-portadminedge yes']},
|
||||
'mstpctl-portautoedge' :
|
||||
{ 'help' : 'enable/disable auto transition to/from edge state of the port',
|
||||
'validvals' : ['<interface-yes-no-list>'],
|
||||
'default' : 'yes',
|
||||
'jsonAttr' : 'autoEdgePort',
|
||||
'required' : False,
|
||||
'example' : ['under the bridge: mstpctl-portautoedge swp1=yes swp2=no',
|
||||
'under the port (recommended): mstpctl-portautoedge yes']},
|
||||
'mstpctl-treeportcost' :
|
||||
{ 'help' : 'port tree cost',
|
||||
'validrange' : ['0', '255'],
|
||||
'required' : False,
|
||||
'jsonAttr': 'extPortCost',
|
||||
},
|
||||
'mstpctl-portbpdufilter' :
|
||||
{ 'help' : 'enable/disable bpdu filter on a port. ' +
|
||||
'syntax varies when defined under a bridge ' +
|
||||
'vs under a port',
|
||||
'validvals' : ['<interface-yes-no-list>'],
|
||||
'jsonAttr' : 'bpduFilterPort',
|
||||
'default' : 'no',
|
||||
'required' : False,
|
||||
'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no',
|
||||
'under a port: mstpctl-portbpdufilter yes']},
|
||||
}}
|
||||
_modinfo = {
|
||||
"mhelp": "mstp configuration module for bridges",
|
||||
"attrs": {
|
||||
"mstpctl-ports": {
|
||||
"help": "mstp ports",
|
||||
"compat": True,
|
||||
"deprecated": True,
|
||||
"new-attribute": "bridge-ports"
|
||||
},
|
||||
"mstpctl-stp": {
|
||||
"help": "bridge stp yes/no",
|
||||
"validvals": ["yes", "no", "on", "off"],
|
||||
"compat": True,
|
||||
"default": "no",
|
||||
"deprecated": True,
|
||||
"new-attribute": "bridge-stp"
|
||||
},
|
||||
"mstpctl-treeprio": {
|
||||
"help": "tree priority",
|
||||
"default": "32768",
|
||||
"validvals": [
|
||||
"0", "4096", "8192", "12288", "16384",
|
||||
"20480", "24576", "28672", "32768",
|
||||
"36864", "40960", "45056", "49152",
|
||||
"53248", "57344", "61440"
|
||||
],
|
||||
"required": False,
|
||||
"example": ["mstpctl-treeprio 32768"]
|
||||
},
|
||||
"mstpctl-ageing": {
|
||||
"help": "ageing time",
|
||||
"validrange": ["0", "4096"],
|
||||
"default": "300",
|
||||
"required": False,
|
||||
"jsonAttr": "ageingTime",
|
||||
"example": ["mstpctl-ageing 300"]
|
||||
},
|
||||
"mstpctl-maxage": {
|
||||
"help": "max message age",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "20",
|
||||
"jsonAttr": "bridgeMaxAge",
|
||||
"required": False,
|
||||
"example": ["mstpctl-maxage 20"]
|
||||
},
|
||||
"mstpctl-fdelay": {
|
||||
"help": "set forwarding delay",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "15",
|
||||
"jsonAttr": "bridgeFwdDelay",
|
||||
"required": False,
|
||||
"example": ["mstpctl-fdelay 15"]
|
||||
},
|
||||
"mstpctl-maxhops": {
|
||||
"help": "bridge max hops",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "20",
|
||||
"jsonAttr": "maxHops",
|
||||
"required": False,
|
||||
"example": ["mstpctl-maxhops 15"]
|
||||
},
|
||||
"mstpctl-txholdcount": {
|
||||
"help": "bridge transmit holdcount",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "6",
|
||||
"jsonAttr": "txHoldCounter",
|
||||
"required": False,
|
||||
"example": ["mstpctl-txholdcount 6"]
|
||||
},
|
||||
"mstpctl-forcevers": {
|
||||
"help": "bridge force stp version",
|
||||
"validvals": ["rstp", ],
|
||||
"default": "rstp",
|
||||
"required": False,
|
||||
"jsonAttr": "forceProtocolVersion",
|
||||
"example": ["mstpctl-forcevers rstp"]
|
||||
},
|
||||
"mstpctl-portpathcost": {
|
||||
"help": "bridge port path cost",
|
||||
"validvals": ["<interface-range-list>"],
|
||||
"validrange": ["0", "65535"],
|
||||
"default": "0",
|
||||
"jsonAttr": "adminExtPortCost",
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-portpathcost swp1=0 swp2=1",
|
||||
"under the port (recommended): mstpctl-portpathcost 0"
|
||||
]
|
||||
},
|
||||
"mstpctl-portp2p": {
|
||||
"help": "bridge port p2p detection mode",
|
||||
"default": "auto",
|
||||
"jsonAttr": "adminPointToPoint",
|
||||
"validvals": ["<interface-yes-no-auto-list>"],
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-portp2p swp1=yes swp2=no",
|
||||
"under the port (recommended): mstpctl-portp2p yes"
|
||||
]
|
||||
},
|
||||
"mstpctl-portrestrrole": {
|
||||
"help":
|
||||
"enable/disable port ability to take root role of the port",
|
||||
"default": "no",
|
||||
"jsonAttr": "restrictedRole",
|
||||
"validvals": ["<interface-yes-no-list>"],
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-portrestrrole swp1=yes swp2=no",
|
||||
"under the port (recommended): mstpctl-portrestrrole yes"
|
||||
]
|
||||
},
|
||||
"mstpctl-portrestrtcn": {
|
||||
"help":
|
||||
"enable/disable port ability to propagate received "
|
||||
"topology change notification of the port",
|
||||
"default": "no",
|
||||
"jsonAttr": "restrictedTcn",
|
||||
"validvals": ["<interface-yes-no-list>"],
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no",
|
||||
"under the port (recommended): mstpctl-portrestrtcn yes"
|
||||
]
|
||||
},
|
||||
"mstpctl-bpduguard": {
|
||||
"help":
|
||||
"enable/disable bpduguard",
|
||||
"default": "no",
|
||||
"jsonAttr": "bpduGuardPort",
|
||||
"validvals": ["<interface-yes-no-list>"],
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-bpduguard swp1=yes swp2=no",
|
||||
"under the port (recommended): mstpctl-bpduguard yes"
|
||||
]
|
||||
},
|
||||
"mstpctl-treeportprio": {
|
||||
"help": "Sets the <port>'s priority MSTI instance. "
|
||||
"The priority value must be a number between 0 and 240 "
|
||||
"and a multiple of 16.",
|
||||
"default": "128",
|
||||
"validvals": ["<interface-range-list-multiple-of-16>"],
|
||||
"validrange": ["0", "240"],
|
||||
"jsonAttr": "treeportprio",
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-treeportprio swp1=128 swp2=128",
|
||||
"under the port (recommended): mstpctl-treeportprio 128"
|
||||
]
|
||||
},
|
||||
"mstpctl-hello": {
|
||||
"help": "set hello time",
|
||||
"validrange": ["0", "255"],
|
||||
"default": "2",
|
||||
"required": False,
|
||||
"jsonAttr": "helloTime",
|
||||
"example": ["mstpctl-hello 2"]
|
||||
},
|
||||
"mstpctl-portnetwork": {
|
||||
"help": "enable/disable bridge assurance capability for a port",
|
||||
"validvals": ["<interface-yes-no-list>"],
|
||||
"default": "no",
|
||||
"jsonAttr": "networkPort",
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-portnetwork swp1=yes swp2=no",
|
||||
"under the port (recommended): mstpctl-portnetwork yes"
|
||||
]
|
||||
},
|
||||
"mstpctl-portadminedge": {
|
||||
"help": "enable/disable initial edge state of the port",
|
||||
"validvals": ["<interface-yes-no-list>"],
|
||||
"default": "no",
|
||||
"jsonAttr": "adminEdgePort",
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-portadminedge swp1=yes swp2=no",
|
||||
"under the port (recommended): mstpctl-portadminedge yes"
|
||||
]
|
||||
},
|
||||
"mstpctl-portautoedge": {
|
||||
"help": "enable/disable auto transition to/from edge state of the port",
|
||||
"validvals": ["<interface-yes-no-list>"],
|
||||
"default": "yes",
|
||||
"jsonAttr": "autoEdgePort",
|
||||
"required": False,
|
||||
"example": [
|
||||
"under the bridge: mstpctl-portautoedge swp1=yes swp2=no",
|
||||
"under the port (recommended): mstpctl-portautoedge yes"
|
||||
]
|
||||
},
|
||||
"mstpctl-treeportcost": {
|
||||
"help": "port tree cost",
|
||||
# "validrange": ["0", "255"],
|
||||
"required": False,
|
||||
"jsonAttr": "extPortCost",
|
||||
},
|
||||
"mstpctl-portbpdufilter": {
|
||||
"help": "enable/disable bpdu filter on a port. syntax varies "
|
||||
"when defined under a bridge vs under a port",
|
||||
"validvals": ["<interface-yes-no-list>"],
|
||||
"jsonAttr": "bpduFilterPort",
|
||||
"default": "no",
|
||||
"required": False,
|
||||
"example": [
|
||||
"under a bridge: mstpctl-portbpdufilter swp1=no swp2=no",
|
||||
"under a port: mstpctl-portbpdufilter yes"
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# Maps mstp bridge attribute names to corresponding mstpctl commands
|
||||
# XXX: This can be encoded in the modules dict above
|
||||
@@ -236,12 +281,11 @@ class mstpctl(moduleBase):
|
||||
'mstpctl-portbpdufilter' : 'portbpdufilter'}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
if not os.path.exists('/sbin/mstpctl'):
|
||||
raise moduleNotSupported('module init failed: no /sbin/mstpctl found')
|
||||
self.ipcmd = None
|
||||
self.name = self.__class__.__name__
|
||||
self.brctlcmd = None
|
||||
self.mstpctlcmd = None
|
||||
self.mstpd_running = (True if systemUtils.is_process_running('mstpd')
|
||||
else False)
|
||||
@@ -290,15 +334,9 @@ class mstpctl(moduleBase):
|
||||
return True
|
||||
|
||||
def _is_bridge(self, ifaceobj):
|
||||
if (ifaceobj.get_attr_value_first('mstpctl-ports') or
|
||||
ifaceobj.get_attr_value_first('bridge-ports')):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_bridge_port(self, ifaceobj):
|
||||
if self.brctlcmd.is_bridge_port(ifaceobj.name):
|
||||
return True
|
||||
return False
|
||||
return ifaceobj.link_kind & ifaceLinkKind.BRIDGE \
|
||||
or ifaceobj.get_attr_value_first('mstpctl-ports') \
|
||||
or ifaceobj.get_attr_value_first('bridge-ports')
|
||||
|
||||
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
|
||||
if not self._is_bridge(ifaceobj):
|
||||
@@ -309,10 +347,10 @@ class mstpctl(moduleBase):
|
||||
|
||||
def get_dependent_ifacenames_running(self, ifaceobj):
|
||||
self._init_command_handlers()
|
||||
if (self.brctlcmd.bridge_exists(ifaceobj.name) and
|
||||
if (self.cache.bridge_exists(ifaceobj.name) and
|
||||
not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
|
||||
return None
|
||||
return self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
||||
return self.cache.get_slaves(ifaceobj.name)
|
||||
|
||||
def _get_bridge_port_attr_value(self, bridgename, portname, attr):
|
||||
json_attr = self.get_mod_subattr(attr, 'jsonAttr')
|
||||
@@ -349,11 +387,11 @@ class mstpctl(moduleBase):
|
||||
runningbridgeports = []
|
||||
# Delete active ports not in the new port list
|
||||
if not ifupdownflags.flags.PERFMODE:
|
||||
runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
||||
runningbridgeports = self.cache.get_slaves(ifaceobj.name)
|
||||
if runningbridgeports:
|
||||
[self.ipcmd.link_set(bport, 'nomaster')
|
||||
for bport in runningbridgeports
|
||||
if not bridgeports or bport not in bridgeports]
|
||||
for bport in runningbridgeports:
|
||||
if not bridgeports or bport not in bridgeports:
|
||||
self.netlink.link_set_nomaster(bport)
|
||||
else:
|
||||
runningbridgeports = []
|
||||
if not bridgeports:
|
||||
@@ -362,13 +400,13 @@ class mstpctl(moduleBase):
|
||||
for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
|
||||
try:
|
||||
if (not ifupdownflags.flags.DRYRUN and
|
||||
not self.ipcmd.link_exists(bridgeport)):
|
||||
not self.cache.link_exists(bridgeport)):
|
||||
self.log_warn('%s: bridge port %s does not exist'
|
||||
%(ifaceobj.name, bridgeport))
|
||||
err += 1
|
||||
continue
|
||||
self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
|
||||
self.ipcmd.addr_flush(bridgeport)
|
||||
self.netlink.link_set_master(bridgeport, ifaceobj.name)
|
||||
self.netlink.addr_flush(bridgeport)
|
||||
except Exception, e:
|
||||
self.log_error(str(e), ifaceobj)
|
||||
|
||||
@@ -410,7 +448,10 @@ class mstpctl(moduleBase):
|
||||
self.logger.warn('%s' %str(e))
|
||||
pass
|
||||
|
||||
if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
|
||||
if self.cache.bridge_is_vlan_aware(ifaceobj.name):
|
||||
return
|
||||
bridgeports = self._get_bridge_port_list(ifaceobj)
|
||||
if not bridgeports:
|
||||
return
|
||||
# set bridge port attributes
|
||||
for attrname, dstattrname in self._port_attrs_map.items():
|
||||
@@ -422,9 +463,8 @@ class mstpctl(moduleBase):
|
||||
try:
|
||||
jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
|
||||
if default_val and jsonAttr:
|
||||
bridgeports = self._get_bridge_port_list(ifaceobj)
|
||||
for port in bridgeports:
|
||||
if not self.brctlcmd.is_bridge_port(port):
|
||||
if not self.cache.link_is_bridge_port(port):
|
||||
continue
|
||||
|
||||
bport_ifaceobjs = ifaceobj_getfunc(port)
|
||||
@@ -502,7 +542,7 @@ class mstpctl(moduleBase):
|
||||
return self.get_mod_subattr(attr,'default')
|
||||
return default_val
|
||||
|
||||
def _apply_bridge_port_settings(self, ifaceobj, bridgename=None,
|
||||
def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None,
|
||||
bridgeifaceobj=None,
|
||||
stp_running_on=True,
|
||||
mstpd_running=True):
|
||||
@@ -517,7 +557,6 @@ class mstpctl(moduleBase):
|
||||
%(ifaceobj.name) +
|
||||
' (stp on bridge %s is not on yet)' %bridgename)
|
||||
return applied
|
||||
bvlan_aware = self.ipcmd.bridge_is_vlan_aware(bridgename)
|
||||
if (not mstpd_running or
|
||||
not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or
|
||||
not bvlan_aware):
|
||||
@@ -602,15 +641,18 @@ class mstpctl(moduleBase):
|
||||
self.logger.info('%s: applying mstp configuration '
|
||||
%ifaceobj.name + 'specific to ports')
|
||||
# Query running bridge ports. and only apply attributes on them
|
||||
bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
||||
bridgeports = self.cache.get_slaves(ifaceobj.name)
|
||||
if not bridgeports:
|
||||
self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
|
||||
return
|
||||
|
||||
bvlan_aware = self.cache.bridge_is_vlan_aware(ifaceobj.name)
|
||||
|
||||
for bport in bridgeports:
|
||||
self.logger.info('%s: processing mstp config for port %s'
|
||||
%(ifaceobj.name, bport))
|
||||
if not self.ipcmd.link_exists(bport):
|
||||
continue
|
||||
if not self.cache.link_exists(bport):
|
||||
continue
|
||||
if not os.path.exists('/sys/class/net/%s/brport' %bport):
|
||||
continue
|
||||
bportifaceobjlist = ifaceobj_getfunc(bport)
|
||||
@@ -622,7 +664,7 @@ class mstpctl(moduleBase):
|
||||
mstpctlFlags.PORT_PROCESSED):
|
||||
continue
|
||||
try:
|
||||
self._apply_bridge_port_settings(bportifaceobj,
|
||||
self._apply_bridge_port_settings(bportifaceobj, bvlan_aware,
|
||||
ifaceobj.name, ifaceobj)
|
||||
except Exception, e:
|
||||
pass
|
||||
@@ -638,12 +680,17 @@ class mstpctl(moduleBase):
|
||||
return False
|
||||
|
||||
def _up(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
# Check if bridge port
|
||||
bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
|
||||
if bridgename:
|
||||
|
||||
# bridge port specific:
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
|
||||
bridgename = self.cache.get_master(ifaceobj.name)
|
||||
|
||||
if not bridgename:
|
||||
return
|
||||
bvlan_aware = self.cache.bridge_is_vlan_aware(bridgename)
|
||||
mstpd_running = self.mstpd_running
|
||||
stp_running_on = self._is_running_userspace_stp_state_on(bridgename)
|
||||
applied = self._apply_bridge_port_settings(ifaceobj, bridgename,
|
||||
stp_running_on = bool(self.cache.get_bridge_stp(bridgename))
|
||||
applied = self._apply_bridge_port_settings(ifaceobj, bvlan_aware, bridgename,
|
||||
None, stp_running_on,
|
||||
mstpd_running)
|
||||
if applied:
|
||||
@@ -651,7 +698,8 @@ class mstpctl(moduleBase):
|
||||
ifaceobj.module_flags.setdefault(self.name,0) | \
|
||||
mstpctlFlags.PORT_PROCESSED
|
||||
return
|
||||
if not self._is_bridge(ifaceobj):
|
||||
|
||||
elif not self._is_bridge(ifaceobj):
|
||||
return
|
||||
# we are now here because the ifaceobj is a bridge
|
||||
stp = None
|
||||
@@ -661,21 +709,17 @@ class mstpctl(moduleBase):
|
||||
if ifaceobj.get_attr_value_first('mstpctl-ports'):
|
||||
# If bridge ports specified with mstpctl attr, create the
|
||||
# bridge and also add its ports
|
||||
self.ipcmd.batch_start()
|
||||
if not ifupdownflags.flags.PERFMODE:
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
self.ipcmd.link_create(ifaceobj.name, 'bridge')
|
||||
else:
|
||||
self.ipcmd.link_create(ifaceobj.name, 'bridge')
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
self.netlink.link_add_bridge(ifaceobj.name)
|
||||
|
||||
try:
|
||||
self._add_ports(ifaceobj)
|
||||
except Exception, e:
|
||||
porterr = True
|
||||
porterrstr = str(e)
|
||||
pass
|
||||
finally:
|
||||
self.ipcmd.batch_commit()
|
||||
running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
||||
|
||||
running_ports = self.cache.get_slaves(ifaceobj.name)
|
||||
if running_ports:
|
||||
# disable ipv6 for ports that were added to bridge
|
||||
self._ports_enable_disable_ipv6(running_ports, '1')
|
||||
@@ -683,14 +727,16 @@ class mstpctl(moduleBase):
|
||||
stp = ifaceobj.get_attr_value_first('mstpctl-stp')
|
||||
if stp:
|
||||
self.set_iface_attr(ifaceobj, 'mstpctl-stp',
|
||||
self.brctlcmd.bridge_set_stp)
|
||||
self.iproute2.bridge_set_stp)
|
||||
stp = utils.get_boolean_from_string(stp)
|
||||
else:
|
||||
stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
|
||||
if (self.mstpd_running and
|
||||
(stp == 'yes' or stp == 'on')):
|
||||
stp = self.cache.get_bridge_stp(ifaceobj.name)
|
||||
if self.mstpd_running and stp:
|
||||
self.mstpctlcmd.batch_start()
|
||||
self._apply_bridge_settings(ifaceobj, ifaceobj_getfunc)
|
||||
self._apply_bridge_port_settings_all(ifaceobj,
|
||||
ifaceobj_getfunc=ifaceobj_getfunc)
|
||||
self.mstpctlcmd.batch_commit()
|
||||
except Exception, e:
|
||||
self.log_error(str(e), ifaceobj)
|
||||
if porterr:
|
||||
@@ -703,10 +749,10 @@ class mstpctl(moduleBase):
|
||||
if ifaceobj.get_attr_value_first('mstpctl-ports'):
|
||||
# If bridge ports specified with mstpctl attr, delete the
|
||||
# bridge
|
||||
ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
||||
ports = self.cache.get_slaves(ifaceobj.name)
|
||||
if ports:
|
||||
self._ports_enable_disable_ipv6(ports, '0')
|
||||
self.brctlcmd.delete_bridge(ifaceobj.name)
|
||||
self.netlink.link_del(ifaceobj.name)
|
||||
except Exception, e:
|
||||
self.log_error(str(e), ifaceobj)
|
||||
|
||||
@@ -729,7 +775,7 @@ class mstpctl(moduleBase):
|
||||
and attrname != 'mstpctl-maxhops'):
|
||||
bridgeattrdict[attrname] = [v]
|
||||
|
||||
ports = self.brctlcmd.get_bridge_ports(ifaceobjrunning.name)
|
||||
ports = self.cache.get_slaves(ifaceobjrunning.name)
|
||||
# Do this only for vlan-UNAWARE-bridge
|
||||
if ports and not bridge_vlan_aware:
|
||||
portconfig = {'mstpctl-portautoedge' : '',
|
||||
@@ -778,16 +824,12 @@ class mstpctl(moduleBase):
|
||||
policymanager.policymanager_api.get_iface_default(module_name='bridge', ifname=ifaceobj.name, attr='bridge-stp'))
|
||||
return utils.get_boolean_from_string(stp)
|
||||
|
||||
def _get_running_stp(self, ifaceobj):
|
||||
stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
|
||||
return utils.get_boolean_from_string(stp)
|
||||
|
||||
def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
|
||||
ifaceobj_getfunc=None):
|
||||
# list of attributes that are not supported currently
|
||||
blacklistedattrs = ['mstpctl-portpathcost',
|
||||
'mstpctl-treeportprio', 'mstpctl-treeportcost']
|
||||
if not self.brctlcmd.bridge_exists(ifaceobj.name):
|
||||
if not self.cache.bridge_exists(ifaceobj.name):
|
||||
self.logger.debug('bridge %s does not exist' %ifaceobj.name)
|
||||
return
|
||||
ifaceattrs = self.dict_key_subset(ifaceobj.config,
|
||||
@@ -803,8 +845,8 @@ class mstpctl(moduleBase):
|
||||
if not runningattrs:
|
||||
runningattrs = {}
|
||||
config_stp = self._get_config_stp(ifaceobj)
|
||||
running_stp = self._get_running_stp(ifaceobj)
|
||||
running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
||||
running_stp = self.cache.get_bridge_stp(ifaceobj.name)
|
||||
running_port_list = self.cache.get_slaves(ifaceobj.name)
|
||||
for k in ifaceattrs:
|
||||
# for all mstpctl options
|
||||
if k in blacklistedattrs:
|
||||
@@ -875,7 +917,7 @@ class mstpctl(moduleBase):
|
||||
# contain more than one valid values
|
||||
stp_on_vals = ['on', 'yes']
|
||||
stp_off_vals = ['off']
|
||||
rv = self.brctlcmd.bridge_get_stp(ifaceobj.name)
|
||||
rv = self.sysfs.bridge_get_stp(ifaceobj.name)
|
||||
if ((v in stp_on_vals and rv in stp_on_vals) or
|
||||
(v in stp_off_vals and rv in stp_off_vals)):
|
||||
ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
|
||||
@@ -945,7 +987,7 @@ class mstpctl(moduleBase):
|
||||
self.set_default_mstp_vxlan_bridge_config and
|
||||
(bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
|
||||
config_stp = self._get_config_stp(bifaceobj)
|
||||
running_stp = self._get_running_stp(bifaceobj)
|
||||
running_stp = self.cache.get_bridge_stp(bifaceobj.name)
|
||||
if (not config_stp or not running_stp):
|
||||
continue
|
||||
for attr in (
|
||||
@@ -981,17 +1023,17 @@ class mstpctl(moduleBase):
|
||||
|
||||
|
||||
def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
#self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
|
||||
ifaceobjcurr.status = ifaceStatus.NOTFOUND
|
||||
return
|
||||
# Check if this is a bridge port
|
||||
if not self._is_bridge_port(ifaceobj):
|
||||
if not self.cache.link_is_bridge_port(ifaceobj.name):
|
||||
# mark all the bridge attributes as error
|
||||
ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
|
||||
self._port_attrs_map.keys(), 0)
|
||||
return
|
||||
bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
|
||||
bridgename = self.cache.get_master(ifaceobj.name)
|
||||
# list of attributes that are not supported currently
|
||||
blacklistedattrs = ['mstpctl-portpathcost',
|
||||
'mstpctl-treeportprio', 'mstpctl-treeportcost']
|
||||
@@ -1038,13 +1080,12 @@ class mstpctl(moduleBase):
|
||||
ifaceobjrunning.update_config(attr, v)
|
||||
|
||||
def _query_running_bridge_port(self, ifaceobjrunning):
|
||||
bridgename = self.ipcmd.bridge_port_get_bridge_name(
|
||||
ifaceobjrunning.name)
|
||||
bridgename = self.cache.get_master(ifaceobjrunning.name)
|
||||
if not bridgename:
|
||||
self.logger.warn('%s: unable to determine bridgename'
|
||||
%ifaceobjrunning.name)
|
||||
return
|
||||
if self.brctlcmd.bridge_get_stp(bridgename) == 'no':
|
||||
if self.sysfs.bridge_get_stp(bridgename) == 'no':
|
||||
# This bridge does not run stp, return
|
||||
return
|
||||
# if userspace stp not set, return
|
||||
@@ -1103,7 +1144,7 @@ class mstpctl(moduleBase):
|
||||
# portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
|
||||
|
||||
def _query_running_bridge(self, ifaceobjrunning):
|
||||
if self.brctlcmd.bridge_get_stp(ifaceobjrunning.name) == 'no':
|
||||
if self.sysfs.bridge_get_stp(ifaceobjrunning.name) == 'no':
|
||||
# This bridge does not run stp, return
|
||||
return
|
||||
# if userspace stp not set, return
|
||||
@@ -1120,9 +1161,9 @@ class mstpctl(moduleBase):
|
||||
bridge_vlan_aware))
|
||||
|
||||
def _query_running(self, ifaceobjrunning, **extra_args):
|
||||
if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
|
||||
if self.cache.bridge_exists(ifaceobjrunning.name):
|
||||
self._query_running_bridge(ifaceobjrunning)
|
||||
elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
|
||||
elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
|
||||
self._query_running_bridge_port(ifaceobjrunning)
|
||||
|
||||
def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
@@ -1235,21 +1276,19 @@ class mstpctl(moduleBase):
|
||||
if config:
|
||||
ifaceobj.replace_config(attr, config)
|
||||
|
||||
|
||||
|
||||
_run_ops = {'pre-up' : _up,
|
||||
'post-down' : _down,
|
||||
'query-checkcurr' : _query_check,
|
||||
'query-running' : _query_running,
|
||||
'query' : _query}
|
||||
_run_ops = {
|
||||
"pre-up": _up,
|
||||
"post-down": _down,
|
||||
"query-checkcurr": _query_check,
|
||||
"query-running": _query_running,
|
||||
"query": _query
|
||||
}
|
||||
|
||||
def get_ops(self):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = self.brctlcmd = LinkUtils()
|
||||
if not self.mstpctlcmd:
|
||||
self.mstpctlcmd = mstpctlutil()
|
||||
|
||||
|
@@ -4,28 +4,28 @@ import os
|
||||
import hashlib
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
import ifupdown2.ifupdown.statemanager as statemanager
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
from ifupdown2.ifupdown.exceptions import moduleNotSupported
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
import ifupdown.statemanager as statemanager
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
from ifupdown.exceptions import moduleNotSupported
|
||||
|
||||
|
||||
class ppp(moduleBase):
|
||||
class ppp(Addon, moduleBase):
|
||||
"""
|
||||
ifupdown2 addon module to configure ppp
|
||||
"""
|
||||
@@ -48,10 +48,10 @@ class ppp(moduleBase):
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
if not os.path.exists('/usr/bin/pon'):
|
||||
raise moduleNotSupported('module init failed: no /usr/bin/pon found')
|
||||
self.ipcmd = None
|
||||
|
||||
@staticmethod
|
||||
def _is_my_interface(ifaceobj):
|
||||
@@ -81,7 +81,7 @@ class ppp(moduleBase):
|
||||
# Always save the current config files hash
|
||||
ifaceobj.update_config('provider_file', config)
|
||||
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
try:
|
||||
# This fails if not running
|
||||
utils.exec_user_command('/bin/ps ax | /bin/grep pppd | /bin/grep -v grep | /bin/grep ' + provider)
|
||||
@@ -97,7 +97,7 @@ class ppp(moduleBase):
|
||||
utils.exec_commandl(['/usr/bin/poff', old_provider], stdout=None, stderr=None)
|
||||
utils.exec_commandl(['/usr/bin/pon', provider], stdout=None, stderr=None)
|
||||
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.log_warn(str(e))
|
||||
|
||||
def _down(self, ifaceobj):
|
||||
@@ -109,7 +109,7 @@ class ppp(moduleBase):
|
||||
# This fails if not running
|
||||
utils.exec_user_command('/bin/ps ax | /bin/grep pppd | /bin/grep -v grep | /bin/grep ' + provider)
|
||||
utils.exec_commandl(['/usr/bin/poff', provider], stdout=None, stderr=None)
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.log_warn(str(e))
|
||||
|
||||
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
|
||||
@@ -124,12 +124,12 @@ class ppp(moduleBase):
|
||||
return None
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
ifaceobjcurr.status = ifaceStatus.SUCCESS
|
||||
|
||||
def _query_running(self, ifaceobjrunning):
|
||||
if not self.ipcmd.link_exists(ifaceobjrunning.name):
|
||||
if not self.cache.link_exists(ifaceobjrunning.name):
|
||||
return
|
||||
|
||||
# Operations supported by this addon (yet).
|
||||
@@ -143,10 +143,6 @@ class ppp(moduleBase):
|
||||
def get_ops(self):
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
op_handler = self._run_ops.get(operation)
|
||||
|
||||
@@ -156,7 +152,6 @@ class ppp(moduleBase):
|
||||
if operation != 'query-running' and not self._is_my_interface(ifaceobj):
|
||||
return
|
||||
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -4,18 +4,20 @@
|
||||
# -- Mon 10 Oct 2016 10:53:13 PM CEST
|
||||
#
|
||||
try:
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
from ifupdown2.lib.addon import Addon
|
||||
from ifupdown2.nlmanager.nlmanager import Link
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
except ImportError:
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.netlink import netlink
|
||||
from lib.addon import Addon
|
||||
from nlmanager.nlmanager import Link
|
||||
|
||||
from ifupdown.iface import *
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
@@ -24,38 +26,38 @@ except ImportError:
|
||||
#
|
||||
# TODO: Add checks for ipip tunnels.
|
||||
#
|
||||
class tunnel(moduleBase):
|
||||
class tunnel(Addon, moduleBase):
|
||||
"""
|
||||
ifupdown2 addon module to configure tunnels
|
||||
"""
|
||||
_modinfo = {
|
||||
'mhelp': 'create/configure GRE/IPIP/SIT and GRETAP tunnel interfaces',
|
||||
'attrs': {
|
||||
'mode': {
|
||||
'tunnel-mode': {
|
||||
'help': 'type of tunnel as in \'ip link\' command.',
|
||||
'validvals': ['gre', 'gretap', 'ipip', 'sit', 'vti', 'ip6gre', 'ipip6', 'ip6ip6', 'vti6'],
|
||||
'required': True,
|
||||
'example': ['mode gre']
|
||||
},
|
||||
'local': {
|
||||
'tunnel-local': {
|
||||
'help': 'IP of local tunnel endpoint',
|
||||
'validvals': ['<ipv4>', '<ipv6>'],
|
||||
'required': True,
|
||||
'example': ['local 192.2.0.42']
|
||||
},
|
||||
'endpoint': {
|
||||
'tunnel-endpoint': {
|
||||
'help': 'IP of remote tunnel endpoint',
|
||||
'validvals': ['<ipv4>', '<ipv6>'],
|
||||
'required': True,
|
||||
'example': ['endpoint 192.2.0.23']
|
||||
},
|
||||
'ttl': {
|
||||
'tunnel-ttl': {
|
||||
'help': 'TTL for tunnel packets',
|
||||
'validvals': ['<number>'],
|
||||
'required': False,
|
||||
'example': ['ttl 64']
|
||||
},
|
||||
'tunnel-physdev': {
|
||||
'tunnel-dev': {
|
||||
'help': 'Physical underlay device to use for tunnel packets',
|
||||
'validvals': ['<interface>'],
|
||||
'required': False,
|
||||
@@ -66,28 +68,72 @@ class tunnel(moduleBase):
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.ipcmd = None
|
||||
Addon.__init__(self)
|
||||
|
||||
@staticmethod
|
||||
def _is_my_interface(ifaceobj):
|
||||
return ifaceobj.addr_method == "tunnel" and ifaceobj.get_attr_value_first('mode')
|
||||
return ifaceobj.get_attr_value_first("tunnel-mode")
|
||||
|
||||
def _has_config_changed(self, attrs_present, attrs_configured):
|
||||
@staticmethod
|
||||
def _has_config_changed(attrs_present, attrs_configured):
|
||||
for key, value in attrs_configured.iteritems():
|
||||
if attrs_present.get(key) != value:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __get_info_data_gre_tunnel(self, info_data):
|
||||
tunnel_link_ifindex = info_data.get(Link.IFLA_GRE_LINK)
|
||||
|
||||
return {
|
||||
"tunnel-endpoint": str(info_data.get(Link.IFLA_GRE_REMOTE)),
|
||||
"tunnel-local": str(info_data.get(Link.IFLA_GRE_LOCAL)),
|
||||
"tunnel-ttl": str(info_data.get(Link.IFLA_GRE_TTL)),
|
||||
"tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
|
||||
}
|
||||
|
||||
def __get_info_data_iptun_tunnel(self, info_data):
|
||||
tunnel_link_ifindex = info_data.get(Link.IFLA_IPTUN_LINK)
|
||||
|
||||
return {
|
||||
"tunnel-endpoint": str(info_data.get(Link.IFLA_IPTUN_REMOTE)),
|
||||
"tunnel-local": str(info_data.get(Link.IFLA_IPTUN_LOCAL)),
|
||||
"tunnel-ttl": str(info_data.get(Link.IFLA_IPTUN_TTL)),
|
||||
"tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
|
||||
}
|
||||
|
||||
def __get_info_data_vti_tunnel(self, info_data):
|
||||
tunnel_link_ifindex = info_data.get(Link.IFLA_VTI_LINK)
|
||||
|
||||
return {
|
||||
"tunnel-endpoint": str(info_data.get(Link.IFLA_VTI_REMOTE)),
|
||||
"tunnel-local": str(info_data.get(Link.IFLA_VTI_LOCAL)),
|
||||
"tunnel-dev": self.cache.get_ifname(tunnel_link_ifindex) if tunnel_link_ifindex else ""
|
||||
}
|
||||
|
||||
def get_linkinfo_attrs(self, ifname, link_kind):
|
||||
return {
|
||||
"gre": self.__get_info_data_gre_tunnel,
|
||||
"gretap": self.__get_info_data_gre_tunnel,
|
||||
"ip6gre": self.__get_info_data_gre_tunnel,
|
||||
"ip6gretap": self.__get_info_data_gre_tunnel,
|
||||
"ip6erspan": self.__get_info_data_gre_tunnel,
|
||||
"ipip": self.__get_info_data_iptun_tunnel,
|
||||
"sit": self.__get_info_data_iptun_tunnel,
|
||||
"ip6tnl": self.__get_info_data_iptun_tunnel,
|
||||
"vti": self.__get_info_data_vti_tunnel,
|
||||
"vti6": self.__get_info_data_vti_tunnel,
|
||||
}.get(link_kind, lambda x: {})(self.cache.get_link_info_data(ifname))
|
||||
|
||||
def _up(self, ifaceobj):
|
||||
attr_map = {
|
||||
# attr_name -> ip route param name
|
||||
'local': 'local',
|
||||
'endpoint': 'remote',
|
||||
'ttl': 'ttl',
|
||||
'tunnel-physdev': 'dev',
|
||||
'tunnel-local': 'local',
|
||||
'tunnel-endpoint': 'remote',
|
||||
'tunnel-ttl': 'ttl',
|
||||
'tunnel-dev': 'dev',
|
||||
}
|
||||
|
||||
mode = ifaceobj.get_attr_value_first('mode')
|
||||
mode = ifaceobj.get_attr_value_first('tunnel-mode')
|
||||
attrs = {}
|
||||
attrs_mapped = {}
|
||||
|
||||
@@ -95,33 +141,33 @@ class tunnel(moduleBase):
|
||||
# to attribute names expected by iproute
|
||||
for attr, iproute_attr in attr_map.items():
|
||||
attr_val = ifaceobj.get_attr_value_first(attr)
|
||||
if attr_val != None:
|
||||
if attr_val is not None:
|
||||
attrs_mapped[iproute_attr] = attr_val
|
||||
attrs[attr] = attr_val
|
||||
|
||||
# Create the tunnel if it doesn't exist yet...
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
self.ipcmd.tunnel_create(ifaceobj.name, mode, attrs_mapped)
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped)
|
||||
return
|
||||
|
||||
# If it's present, check if there were changes
|
||||
current_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
|
||||
current_mode = self.ipcmd.link_cache_get([ifaceobj.name, 'kind'])
|
||||
current_mode = self.cache.get_link_kind(ifaceobj.name)
|
||||
current_attrs = self.get_linkinfo_attrs(ifaceobj.name, current_mode)
|
||||
|
||||
try:
|
||||
if current_attrs and current_mode != mode or self._has_config_changed(current_attrs, attrs):
|
||||
# Mode and some other changes are not possible without recreating the interface,
|
||||
# so just recreate it IFF there have been changes.
|
||||
self.ipcmd.link_delete(ifaceobj.name)
|
||||
self.ipcmd.tunnel_create(ifaceobj.name, mode, attrs_mapped)
|
||||
self.netlink.link_del(ifaceobj.name)
|
||||
self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped)
|
||||
except Exception, e:
|
||||
self.log_warn(str(e))
|
||||
|
||||
def _down(self, ifaceobj):
|
||||
if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
try:
|
||||
self.ipcmd.link_delete(ifaceobj.name)
|
||||
self.netlink.link_del(ifaceobj.name)
|
||||
except Exception, e:
|
||||
self.log_warn(str(e))
|
||||
|
||||
@@ -129,7 +175,7 @@ class tunnel(moduleBase):
|
||||
if not self._is_my_interface(ifaceobj):
|
||||
return None
|
||||
|
||||
device = ifaceobj.get_attr_value_first('tunnel-physdev')
|
||||
device = ifaceobj.get_attr_value_first('tunnel-dev')
|
||||
if device:
|
||||
return [device]
|
||||
|
||||
@@ -147,19 +193,21 @@ class tunnel(moduleBase):
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
ifname = ifaceobj.name
|
||||
|
||||
if not self.ipcmd.link_exists(ifname):
|
||||
if not self.cache.link_exists(ifname):
|
||||
return
|
||||
|
||||
tunattrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
|
||||
link_kind = self.cache.get_link_kind(ifname)
|
||||
tunattrs = self.get_linkinfo_attrs(ifaceobj.name, link_kind)
|
||||
|
||||
if not tunattrs:
|
||||
ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1)
|
||||
return
|
||||
|
||||
tunattrs["mode"] = self.ipcmd.link_get_kind(ifname)
|
||||
tunattrs["tunnel-mode"] = link_kind
|
||||
|
||||
user_config_mode = ifaceobj.get_attr_value_first("mode")
|
||||
user_config_mode = ifaceobj.get_attr_value_first("tunnel-mode")
|
||||
if user_config_mode in ('ipip6', 'ip6ip6'):
|
||||
ifaceobj.replace_config("mode", "ip6tnl")
|
||||
ifaceobj.replace_config("tunnel-mode", "ip6tnl")
|
||||
|
||||
for attr in self.get_mod_attrs():
|
||||
if not ifaceobj.get_attr_value_first(attr):
|
||||
@@ -182,10 +230,6 @@ class tunnel(moduleBase):
|
||||
def get_ops(self):
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
op_handler = self._run_ops.get(operation)
|
||||
if not op_handler:
|
||||
@@ -194,7 +238,6 @@ class tunnel(moduleBase):
|
||||
if operation != 'query-running' and not self._is_my_interface(ifaceobj):
|
||||
return
|
||||
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -5,52 +5,51 @@
|
||||
#
|
||||
|
||||
try:
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
from ifupdown2.lib.addon import Addon
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.nlmanager.nlmanager import Link
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
from ifupdown.iface import *
|
||||
from nlmanager.nlmanager import Link
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.netlink import netlink
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
|
||||
|
||||
class vlan(moduleBase):
|
||||
class vlan(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure vlans """
|
||||
|
||||
_modinfo = {'mhelp' : 'vlan module configures vlan interfaces.' +
|
||||
'This module understands vlan interfaces with dot ' +
|
||||
'notations. eg swp1.100. Vlan interfaces with any ' +
|
||||
'other names need to have raw device and vlan id ' +
|
||||
'attributes',
|
||||
'attrs' : {
|
||||
'vlan-raw-device' :
|
||||
{'help' : 'vlan raw device',
|
||||
'validvals': ['<interface>']},
|
||||
'vlan-id' :
|
||||
{'help' : 'vlan id',
|
||||
'validrange' : ['0', '4096']},
|
||||
'vlan-protocol' :
|
||||
{'help' : 'vlan protocol',
|
||||
'default' : '802.1q',
|
||||
'validvals': ['802.1q', '802.1ad'],
|
||||
'example' : ['vlan-protocol 802.1q']},
|
||||
}}
|
||||
|
||||
_modinfo = {
|
||||
"mhelp": "vlan module configures vlan interfaces. "
|
||||
"This module understands vlan interfaces with dot "
|
||||
"notations. eg swp1.100. Vlan interfaces with any "
|
||||
"other names need to have raw device and vlan id attributes",
|
||||
"attrs": {
|
||||
"vlan-raw-device": {
|
||||
"help": "vlan raw device",
|
||||
"validvals": ["<interface>"]
|
||||
},
|
||||
"vlan-id": {
|
||||
"help": "vlan id",
|
||||
"validrange": ["0", "4096"]
|
||||
},
|
||||
"vlan-protocol": {
|
||||
"help": "vlan protocol",
|
||||
"default": "802.1q",
|
||||
"validvals": ["802.1q", "802.1ad"],
|
||||
"example": ["vlan-protocol 802.1q"]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.ipcmd = None
|
||||
|
||||
def _is_vlan_device(self, ifaceobj):
|
||||
@staticmethod
|
||||
def _is_vlan_device(ifaceobj):
|
||||
vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
|
||||
if vlan_raw_device:
|
||||
return True
|
||||
@@ -58,10 +57,12 @@ class vlan(moduleBase):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_vlan_by_name(self, ifacename):
|
||||
@staticmethod
|
||||
def _is_vlan_by_name(ifacename):
|
||||
return '.' in ifacename
|
||||
|
||||
def _get_vlan_raw_device_from_ifacename(self, ifacename):
|
||||
@staticmethod
|
||||
def _get_vlan_raw_device_from_ifacename(ifacename):
|
||||
""" Returns vlan raw device from ifname
|
||||
Example:
|
||||
Returns eth0 for ifname eth0.100
|
||||
@@ -88,22 +89,21 @@ class vlan(moduleBase):
|
||||
ifaceobj.link_kind |= ifaceLinkKind.VLAN
|
||||
return [self._get_vlan_raw_device(ifaceobj)]
|
||||
|
||||
def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
|
||||
add=True):
|
||||
def _bridge_vid_add_del(self, bridgename, vlanid, add=True):
|
||||
""" If the lower device is a vlan aware bridge, add/del the vlanid
|
||||
to the bridge """
|
||||
if self.ipcmd.bridge_is_vlan_aware(bridgename):
|
||||
if add:
|
||||
netlink.link_add_bridge_vlan(bridgename, vlanid)
|
||||
else:
|
||||
netlink.link_del_bridge_vlan(bridgename, vlanid)
|
||||
if self.cache.bridge_is_vlan_aware(bridgename):
|
||||
if add:
|
||||
self.netlink.link_add_bridge_vlan(bridgename, vlanid)
|
||||
else:
|
||||
self.netlink.link_del_bridge_vlan(bridgename, vlanid)
|
||||
|
||||
def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
|
||||
def _bridge_vid_check(self, ifaceobjcurr, bridgename, vlanid):
|
||||
""" If the lower device is a vlan aware bridge, check if the vlanid
|
||||
is configured on the bridge """
|
||||
if not self.ipcmd.bridge_is_vlan_aware(bridgename):
|
||||
if not self.cache.bridge_is_vlan_aware(bridgename):
|
||||
return
|
||||
vids = self.ipcmd.bridge_vlan_get_vids(bridgename)
|
||||
_, vids = self.cache.get_pvid_and_vids(bridgename)
|
||||
if not vids or vlanid not in vids:
|
||||
ifaceobjcurr.status = ifaceStatus.ERROR
|
||||
ifaceobjcurr.status_str = 'bridge vid error'
|
||||
@@ -116,8 +116,15 @@ class vlan(moduleBase):
|
||||
if not vlanrawdevice:
|
||||
raise Exception('could not determine vlan raw device')
|
||||
|
||||
vlan_protocol = ifaceobj.get_attr_value_first('vlan-protocol')
|
||||
cached_vlan_protocol = self.ipcmd.get_vlan_protocol(ifaceobj.name)
|
||||
ifname = ifaceobj.name
|
||||
|
||||
if ifupdownflags.flags.PERFMODE:
|
||||
cached_vlan_ifla_info_data = {}
|
||||
else:
|
||||
cached_vlan_ifla_info_data = self.cache.get_link_info_data(ifname)
|
||||
|
||||
vlan_protocol = ifaceobj.get_attr_value_first('vlan-protocol')
|
||||
cached_vlan_protocol = cached_vlan_ifla_info_data.get(Link.IFLA_VLAN_PROTOCOL)
|
||||
|
||||
if not vlan_protocol:
|
||||
vlan_protocol = self.get_attr_default_value('vlan-protocol')
|
||||
@@ -130,25 +137,28 @@ class vlan(moduleBase):
|
||||
|
||||
if not ifupdownflags.flags.PERFMODE:
|
||||
|
||||
vlan_exists = self.ipcmd.link_exists(ifaceobj.name)
|
||||
vlan_exists = self.cache.link_exists(ifaceobj.name)
|
||||
|
||||
if vlan_exists:
|
||||
user_vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
|
||||
cached_vlan_raw_device = self.ipcmd.cache_get('link', [ifaceobj.name, 'link'])
|
||||
cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
|
||||
|
||||
if cached_vlan_raw_device and user_vlan_raw_device and cached_vlan_raw_device != user_vlan_raw_device:
|
||||
raise Exception('%s: cannot change vlan-raw-device from %s to %s: operation not supported. '
|
||||
'Please delete the device with \'ifdown %s\' and recreate it to apply the change.'
|
||||
% (ifaceobj.name, cached_vlan_raw_device, user_vlan_raw_device, ifaceobj.name))
|
||||
|
||||
if not self.ipcmd.link_exists(vlanrawdevice):
|
||||
raise Exception('rawdevice %s not present' %vlanrawdevice)
|
||||
if not self.cache.link_exists(vlanrawdevice):
|
||||
if ifupdownflags.flags.DRYRUN:
|
||||
return
|
||||
else:
|
||||
raise Exception('rawdevice %s not present' % vlanrawdevice)
|
||||
if vlan_exists:
|
||||
self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
|
||||
self._bridge_vid_add_del(vlanrawdevice, vlanid)
|
||||
return
|
||||
|
||||
netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
|
||||
self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
|
||||
self.netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
|
||||
self._bridge_vid_add_del(vlanrawdevice, vlanid)
|
||||
|
||||
def _down(self, ifaceobj):
|
||||
vlanid = self._get_vlan_id(ifaceobj)
|
||||
@@ -157,72 +167,103 @@ class vlan(moduleBase):
|
||||
vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
|
||||
if not vlanrawdevice:
|
||||
raise Exception('could not determine vlan raw device')
|
||||
if (not ifupdownflags.flags.PERFMODE and
|
||||
not self.ipcmd.link_exists(ifaceobj.name)):
|
||||
return
|
||||
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
try:
|
||||
netlink.link_del(ifaceobj.name)
|
||||
self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
|
||||
self.netlink.link_del(ifaceobj.name)
|
||||
self._bridge_vid_add_del(vlanrawdevice, vlanid, add=False)
|
||||
except Exception, e:
|
||||
self.log_warn(str(e))
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
return
|
||||
if not '.' in ifaceobj.name:
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
if '.' not in ifaceobj.name:
|
||||
# if vlan name is not in the dot format, check its running state
|
||||
(vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
|
||||
if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
|
||||
ifaceobjcurr.update_config_with_status('vlan-raw-device',
|
||||
vlanrawdev, 1)
|
||||
else:
|
||||
ifaceobjcurr.update_config_with_status('vlan-raw-device',
|
||||
vlanrawdev, 0)
|
||||
|
||||
ifname = ifaceobj.name
|
||||
cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
|
||||
|
||||
#
|
||||
# vlan-raw-device
|
||||
#
|
||||
ifaceobjcurr.update_config_with_status(
|
||||
'vlan-raw-device',
|
||||
cached_vlan_raw_device,
|
||||
cached_vlan_raw_device != ifaceobj.get_attr_value_first('vlan-raw-device')
|
||||
)
|
||||
|
||||
cached_vlan_info_data = self.cache.get_link_info_data(ifname)
|
||||
|
||||
#
|
||||
# vlan-id
|
||||
#
|
||||
vlanid_config = ifaceobj.get_attr_value_first('vlan-id')
|
||||
if not vlanid_config:
|
||||
vlanid_config = str(self._get_vlan_id(ifaceobj))
|
||||
if vlanid != vlanid_config:
|
||||
ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
|
||||
else:
|
||||
ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0)
|
||||
|
||||
cached_vlan_id = cached_vlan_info_data.get(Link.IFLA_VLAN_ID)
|
||||
cached_vlan_id_str = str(cached_vlan_id)
|
||||
ifaceobjcurr.update_config_with_status('vlan-id', cached_vlan_id_str, vlanid_config != cached_vlan_id_str)
|
||||
|
||||
#
|
||||
# vlan-protocol
|
||||
#
|
||||
protocol_config = ifaceobj.get_attr_value_first('vlan-protocol')
|
||||
if protocol_config:
|
||||
if protocol_config.upper() != protocol.upper():
|
||||
ifaceobjcurr.update_config_with_status('vlan-protocol',
|
||||
protocol, 1)
|
||||
|
||||
cached_vlan_protocol = cached_vlan_info_data.get(Link.IFLA_VLAN_PROTOCOL)
|
||||
|
||||
if protocol_config.upper() != cached_vlan_protocol.upper():
|
||||
ifaceobjcurr.update_config_with_status(
|
||||
'vlan-protocol',
|
||||
cached_vlan_protocol,
|
||||
1
|
||||
)
|
||||
else:
|
||||
ifaceobjcurr.update_config_with_status('vlan-protocol',
|
||||
protocol, 0)
|
||||
self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, int(vlanid))
|
||||
ifaceobjcurr.update_config_with_status(
|
||||
'vlan-protocol',
|
||||
protocol_config,
|
||||
0
|
||||
)
|
||||
|
||||
self._bridge_vid_check(ifaceobjcurr, cached_vlan_raw_device, cached_vlan_id)
|
||||
|
||||
def _query_running(self, ifaceobjrunning):
|
||||
if not self.ipcmd.link_exists(ifaceobjrunning.name):
|
||||
ifname = ifaceobjrunning.name
|
||||
|
||||
if not self.cache.link_exists(ifname):
|
||||
return
|
||||
(vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
|
||||
if not vlanid:
|
||||
|
||||
if not self.cache.get_link_kind(ifname) == 'vlan':
|
||||
return
|
||||
|
||||
# If vlan name is not in the dot format, get the
|
||||
# vlan dev and vlan id
|
||||
if not '.' in ifaceobjrunning.name:
|
||||
ifaceobjrunning.update_config_dict({k: [v] for k, v in
|
||||
{'vlan-raw-device' : vlanrawdev,
|
||||
'vlan-id' : vlanid,
|
||||
'vlan-protocol' : protocol}.items()
|
||||
if v})
|
||||
if '.' in ifname:
|
||||
return
|
||||
|
||||
_run_ops = {'pre-up' : _up,
|
||||
'post-down' : _down,
|
||||
'query-checkcurr' : _query_check,
|
||||
'query-running' : _query_running}
|
||||
cached_vlan_info_data = self.cache.get_link_info_data(ifname)
|
||||
|
||||
for attr_name, nl_attr in (
|
||||
('vlan-id', Link.IFLA_VLAN_ID),
|
||||
('vlan-protocol', Link.IFLA_VLAN_PROTOCOL)
|
||||
):
|
||||
ifaceobjrunning.update_config(attr_name, str(cached_vlan_info_data.get(nl_attr)))
|
||||
|
||||
ifaceobjrunning.update_config('vlan-raw-device', self.cache.get_lower_device_ifname(ifname))
|
||||
|
||||
_run_ops = {
|
||||
"pre-up": _up,
|
||||
"post-down": _down,
|
||||
"query-checkcurr": _query_check,
|
||||
"query-running": _query_running
|
||||
}
|
||||
|
||||
def get_ops(self):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
""" run vlan configuration on the interface object passed as argument
|
||||
|
||||
@@ -247,7 +288,6 @@ class vlan(moduleBase):
|
||||
if (operation != 'query-running' and
|
||||
not self._is_vlan_device(ifaceobj)):
|
||||
return
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -13,69 +13,86 @@ import signal
|
||||
from sets import Set
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
|
||||
import ifupdown2.ifupdown.policymanager as policymanager
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
|
||||
from ifupdown2.nlmanager.nlmanager import Link
|
||||
|
||||
from ifupdown2.ifupdownaddons.dhclient import dhclient
|
||||
from ifupdown2.ifupdownaddons.utilsbase import *
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
|
||||
import ifupdown.policymanager as policymanager
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
from ifupdown.statemanager import statemanager_api as statemanager
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.netlink import netlink
|
||||
|
||||
from nlmanager.nlmanager import Link
|
||||
|
||||
from ifupdownaddons.dhclient import dhclient
|
||||
from ifupdownaddons.utilsbase import *
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
|
||||
class vrfPrivFlags:
|
||||
PROCESSED = 0x1
|
||||
|
||||
class vrf(moduleBase):
|
||||
""" ifupdown2 addon module to configure vrfs """
|
||||
_modinfo = { 'mhelp' : 'vrf configuration module',
|
||||
'attrs' : {
|
||||
'vrf-table':
|
||||
{'help' : 'vrf device routing table id. key to ' +
|
||||
'creating a vrf device. ' +
|
||||
'Table id is either \'auto\' or '+
|
||||
'\'valid routing table id\'',
|
||||
'validvals': ['auto', '<number>'],
|
||||
'example': ['vrf-table auto', 'vrf-table 1001']},
|
||||
'vrf':
|
||||
{'help' : 'vrf the interface is part of.',
|
||||
'validvals': ['<text>'],
|
||||
'example': ['vrf blue']}}}
|
||||
|
||||
iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
|
||||
iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
|
||||
'# It contains the vrf name to table mapping.\n' + \
|
||||
'# Reserved table range %s %s\n'
|
||||
class vrf(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to configure vrfs """
|
||||
|
||||
_modinfo = {
|
||||
"mhelp": "vrf configuration module",
|
||||
"attrs": {
|
||||
"vrf-table": {
|
||||
"help": "vrf device routing table id. key to creating a vrf device. "
|
||||
"Table id is either 'auto' or 'valid routing table id'",
|
||||
"validvals": ["auto", "<number>"],
|
||||
"example": ["vrf-table auto", "vrf-table 1001"]
|
||||
},
|
||||
"vrf": {
|
||||
"help": "vrf the interface is part of",
|
||||
"validvals": ["<text>"],
|
||||
"example": ["vrf blue"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iproute2_vrf_filename = "/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf"
|
||||
iproute2_vrf_filehdr = "# This file is autogenerated by ifupdown2.\n" \
|
||||
"# It contains the vrf name to table mapping.\n" \
|
||||
"# Reserved table range %s %s\n"
|
||||
VRF_TABLE_START = 1001
|
||||
VRF_TABLE_END = 5000
|
||||
|
||||
system_reserved_rt_tables = {'255' : 'local', '254' : 'main',
|
||||
'253' : 'default', '0' : 'unspec'}
|
||||
system_reserved_rt_tables = {
|
||||
"255": "local",
|
||||
"254": "main",
|
||||
"253": "default",
|
||||
"0": "unspec"
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.ipcmd = None
|
||||
self.bondcmd = None
|
||||
self.dhclientcmd = None
|
||||
self.name = self.__class__.__name__
|
||||
self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
|
||||
self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(
|
||||
module_name=self.__class__.__name__,
|
||||
attr="vrf-mgmt-devname"
|
||||
)
|
||||
|
||||
self.at_exit = False
|
||||
|
||||
self.user_reserved_vrf_table = []
|
||||
|
||||
@@ -130,7 +147,6 @@ class vrf(moduleBase):
|
||||
self.l3mdev_checked = True
|
||||
self._iproute2_vrf_map_initialized = False
|
||||
self.iproute2_vrf_map = {}
|
||||
self.iproute2_vrf_map_fd = None
|
||||
self.iproute2_vrf_map_sync_to_disk = False
|
||||
|
||||
self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
|
||||
@@ -196,7 +212,7 @@ class vrf(moduleBase):
|
||||
iproute2_vrf_map_force_rewrite = False
|
||||
# read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
|
||||
if os.path.exists(self.iproute2_vrf_filename):
|
||||
with open(self.iproute2_vrf_filename, 'r+') as vrf_map_fd:
|
||||
with open(self.iproute2_vrf_filename, "r+" if writetodisk else "r") as vrf_map_fd:
|
||||
lines = vrf_map_fd.readlines()
|
||||
for l in lines:
|
||||
l = l.strip()
|
||||
@@ -215,19 +231,12 @@ class vrf(moduleBase):
|
||||
self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e)))
|
||||
pass
|
||||
|
||||
vrfs = self.ipcmd.link_get_vrfs()
|
||||
running_vrf_map = {}
|
||||
if vrfs:
|
||||
for v, lattrs in vrfs.iteritems():
|
||||
table = lattrs.get('table', None)
|
||||
if table:
|
||||
running_vrf_map[int(table)] = v
|
||||
running_vrf_map = self.cache.get_vrf_table_map()
|
||||
|
||||
if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
|
||||
self.iproute2_vrf_map = running_vrf_map
|
||||
iproute2_vrf_map_force_rewrite = True
|
||||
|
||||
self.iproute2_vrf_map_fd = None
|
||||
if writetodisk:
|
||||
if iproute2_vrf_map_force_rewrite:
|
||||
# reopen the file and rewrite the map
|
||||
@@ -236,7 +245,8 @@ class vrf(moduleBase):
|
||||
self._iproute2_vrf_map_open(False, True)
|
||||
|
||||
self.iproute2_vrf_map_sync_to_disk = False
|
||||
atexit.register(self._iproute2_vrf_map_sync_to_disk)
|
||||
self.at_exit = True
|
||||
#atexit.register(self._iproute2_vrf_map_sync_to_disk)
|
||||
|
||||
self.logger.info("vrf: dumping iproute2_vrf_map")
|
||||
self.logger.info(self.iproute2_vrf_map)
|
||||
@@ -287,22 +297,15 @@ class vrf(moduleBase):
|
||||
if ifupdownflags.flags.DRYRUN:
|
||||
return
|
||||
fmode = 'a+' if append else 'w'
|
||||
try:
|
||||
self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
|
||||
'%s' %fmode)
|
||||
fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
|
||||
except Exception, e:
|
||||
self._iproute2_map_warn(str(e))
|
||||
return
|
||||
|
||||
if not append:
|
||||
# write file header
|
||||
self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
|
||||
%(self.vrf_table_id_start,
|
||||
self.vrf_table_id_end))
|
||||
for t, v in self.iproute2_vrf_map.iteritems():
|
||||
self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
|
||||
self.iproute2_vrf_map_fd.flush()
|
||||
with open(self.iproute2_vrf_filename, fmode) as vrf_map_fd:
|
||||
vrf_map_fd.write(self.iproute2_vrf_filehdr
|
||||
%(self.vrf_table_id_start,
|
||||
self.vrf_table_id_end))
|
||||
for t, v in self.iproute2_vrf_map.iteritems():
|
||||
vrf_map_fd.write('%s %s\n' %(t, v))
|
||||
vrf_map_fd.flush()
|
||||
|
||||
def _is_vrf(self, ifaceobj):
|
||||
if ifaceobj.get_attr_value_first('vrf-table'):
|
||||
@@ -364,10 +367,10 @@ class vrf(moduleBase):
|
||||
old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
|
||||
if not old_vrf_name:
|
||||
self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
|
||||
if self.iproute2_vrf_map_fd:
|
||||
self.iproute2_vrf_map_fd.write('%s %s\n'
|
||||
%(table_id, vrfifaceobj.name))
|
||||
self.iproute2_vrf_map_fd.flush()
|
||||
with open(self.iproute2_vrf_filename, "a+") as vrf_map_fd:
|
||||
vrf_map_fd.write('%s %s\n'
|
||||
% (table_id, vrfifaceobj.name))
|
||||
vrf_map_fd.flush()
|
||||
self.vrf_count += 1
|
||||
return
|
||||
if old_vrf_name != vrfifaceobj.name:
|
||||
@@ -441,7 +444,7 @@ class vrf(moduleBase):
|
||||
break
|
||||
self._handle_existing_connections(ifaceobj, vrfname)
|
||||
self.enable_ipv6_if_prev_brport(ifacename)
|
||||
self.ipcmd.link_set(ifacename, 'master', vrfname)
|
||||
self.netlink.link_set_master(ifacename, vrfname)
|
||||
return
|
||||
|
||||
def enable_ipv6_if_prev_brport(self, ifname):
|
||||
@@ -449,8 +452,10 @@ class vrf(moduleBase):
|
||||
If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
|
||||
"""
|
||||
try:
|
||||
if os.path.exists("/sys/class/net/%s/brport" % ifname):
|
||||
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
|
||||
for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
|
||||
self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
|
||||
return
|
||||
except Exception, e:
|
||||
self.logger.info(str(e))
|
||||
|
||||
@@ -458,7 +463,7 @@ class vrf(moduleBase):
|
||||
try:
|
||||
dhclient_cmd_prefix = None
|
||||
if (vrfname and self.vrf_exec_cmd_prefix and
|
||||
self.ipcmd.link_exists(vrfname)):
|
||||
self.cache.link_exists(vrfname)):
|
||||
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
|
||||
vrfname)
|
||||
self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
|
||||
@@ -469,9 +474,10 @@ class vrf(moduleBase):
|
||||
def _handle_existing_connections(self, ifaceobj, vrfname):
|
||||
if not ifaceobj or ifupdownflags.flags.PERFMODE:
|
||||
return
|
||||
if (self.vrf_mgmt_devname and
|
||||
self.vrf_mgmt_devname == vrfname):
|
||||
self._kill_ssh_connections(ifaceobj.name)
|
||||
# XXX: re-evaluate kill_ssh_sessions
|
||||
if (self.vrf_mgmt_devname and self.vrf_mgmt_devname == vrfname):
|
||||
self._kill_ssh_connections(ifaceobj.name, ifaceobj)
|
||||
self._close_sockets(ifaceobj.name)
|
||||
if self._is_dhcp_slave(ifaceobj):
|
||||
self._down_dhcp_slave(ifaceobj, vrfname)
|
||||
|
||||
@@ -479,12 +485,12 @@ class vrf(moduleBase):
|
||||
ifaceobj_getfunc=None, vrf_exists=False):
|
||||
try:
|
||||
master_exists = True
|
||||
if vrf_exists or self.ipcmd.link_exists(vrfname):
|
||||
uppers = self.ipcmd.link_get_uppers(ifacename)
|
||||
if vrf_exists or self.cache.link_exists(vrfname):
|
||||
uppers = self.sysfs.link_get_uppers(ifacename)
|
||||
if not uppers or vrfname not in uppers:
|
||||
self._handle_existing_connections(ifaceobj, vrfname)
|
||||
self.enable_ipv6_if_prev_brport(ifacename)
|
||||
self.ipcmd.link_set(ifacename, 'master', vrfname)
|
||||
self.netlink.link_set_master(ifacename, vrfname)
|
||||
elif ifaceobj:
|
||||
vrf_master_objs = ifaceobj_getfunc(vrfname)
|
||||
if not vrf_master_objs:
|
||||
@@ -492,7 +498,7 @@ class vrf(moduleBase):
|
||||
# but user has not provided a vrf interface.
|
||||
# people expect you to warn them but go ahead with the
|
||||
# rest of the config on that interface
|
||||
netlink.link_set_updown(ifacename, "up")
|
||||
self.netlink.link_up(ifacename)
|
||||
self.log_error('vrf master ifaceobj %s not found'
|
||||
%vrfname)
|
||||
return
|
||||
@@ -511,7 +517,7 @@ class vrf(moduleBase):
|
||||
master_exists = False
|
||||
if master_exists:
|
||||
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
|
||||
netlink.link_set_updown(ifacename, "up")
|
||||
self.netlink.link_up(ifacename)
|
||||
else:
|
||||
self.log_error('vrf %s not around, skipping vrf config'
|
||||
%(vrfname), ifaceobj)
|
||||
@@ -641,14 +647,14 @@ class vrf(moduleBase):
|
||||
# - check if it is also a macvlan device of the
|
||||
# format <vrf_slave>-v<int> created by the
|
||||
# address virtual module
|
||||
vrfslave_lowers = self.ipcmd.link_get_lowers(vrfslave)
|
||||
vrfslave_lowers = self.sysfs.link_get_lowers(vrfslave)
|
||||
if vrfslave_lowers:
|
||||
if vrfslave_lowers[0] in config_vrfslaves:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
|
||||
running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
|
||||
config_slaves = ifaceobj.lowerifaces
|
||||
if not config_slaves and not running_slaves:
|
||||
return
|
||||
@@ -660,7 +666,7 @@ class vrf(moduleBase):
|
||||
if add_slaves:
|
||||
for s in add_slaves:
|
||||
try:
|
||||
if not self.ipcmd.link_exists(s):
|
||||
if not self.cache.link_exists(s):
|
||||
continue
|
||||
sobj = None
|
||||
if ifaceobj_getfunc:
|
||||
@@ -691,7 +697,7 @@ class vrf(moduleBase):
|
||||
for slave_ifaceobj in ifaceobj_getfunc(s) or []:
|
||||
if slave_ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
|
||||
raise Exception("link-down yes: keeping VRF slave down")
|
||||
netlink.link_set_updown(s, "up")
|
||||
self.netlink.link_up(s)
|
||||
except Exception, e:
|
||||
self.logger.debug("%s: %s" % (s, str(e)))
|
||||
pass
|
||||
@@ -707,7 +713,7 @@ class vrf(moduleBase):
|
||||
return False
|
||||
|
||||
def _create_vrf_dev(self, ifaceobj, vrf_table):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
self._check_vrf_system_reserved_names(ifaceobj)
|
||||
|
||||
if self.vrf_count == self.vrf_max_count:
|
||||
@@ -739,8 +745,7 @@ class vrf(moduleBase):
|
||||
self.vrf_table_id_start,
|
||||
self.vrf_table_id_end), ifaceobj)
|
||||
try:
|
||||
self.ipcmd.link_create(ifaceobj.name, 'vrf',
|
||||
{'table' : '%s' %vrf_table})
|
||||
self.netlink.link_add_vrf(ifaceobj.name, vrf_table)
|
||||
except Exception, e:
|
||||
self.log_error('create failed (%s)\n' % str(e), ifaceobj)
|
||||
if vrf_table != 'auto':
|
||||
@@ -752,13 +757,12 @@ class vrf(moduleBase):
|
||||
self.log_error('unable to get vrf table id', ifaceobj)
|
||||
|
||||
# if the device exists, check if table id is same
|
||||
vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
|
||||
if vrfdev_attrs:
|
||||
running_table = vrfdev_attrs.get('table', None)
|
||||
if vrf_table != running_table:
|
||||
self.log_error('cannot change vrf table id,running table id'
|
||||
' %s is different from config id %s'
|
||||
% (running_table, vrf_table), ifaceobj)
|
||||
running_table = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE)
|
||||
|
||||
if running_table is not None and vrf_table != str(running_table):
|
||||
self.log_error('cannot change vrf table id,running table id'
|
||||
' %s is different from config id %s'
|
||||
% (running_table, vrf_table), ifaceobj)
|
||||
return vrf_table
|
||||
|
||||
def _up_vrf_helper(self, ifaceobj, vrf_table):
|
||||
@@ -794,16 +798,22 @@ class vrf(moduleBase):
|
||||
self._set_vrf_dev_processed_flag(ifaceobj)
|
||||
|
||||
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
|
||||
netlink.link_set_updown(ifaceobj.name, "up")
|
||||
self.netlink.link_up(ifaceobj.name)
|
||||
except Exception, e:
|
||||
self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
|
||||
|
||||
def _kill_ssh_connections(self, ifacename):
|
||||
def _kill_ssh_connections(self, ifacename, ifaceobj):
|
||||
try:
|
||||
runningaddrsdict = self.ipcmd.get_running_addrs(None, ifacename)
|
||||
if not runningaddrsdict:
|
||||
running_addrs_list = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
|
||||
ifaceobj_list=[ifaceobj],
|
||||
ifname=ifacename,
|
||||
)]
|
||||
|
||||
if not running_addrs_list:
|
||||
return
|
||||
iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
|
||||
|
||||
iplist = [i.split('/', 1)[0] for i in running_addrs_list]
|
||||
|
||||
if not iplist:
|
||||
return
|
||||
proc=[]
|
||||
@@ -884,13 +894,17 @@ class vrf(moduleBase):
|
||||
else:
|
||||
vrf = ifaceobj.get_attr_value_first('vrf')
|
||||
if vrf:
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
self.logger.warning("%s: device not found - please check your configuration" % ifaceobj.name)
|
||||
return
|
||||
|
||||
self._iproute2_vrf_map_initialize()
|
||||
# This is a vrf slave
|
||||
self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
|
||||
ifaceobj_getfunc)
|
||||
elif not ifupdownflags.flags.PERFMODE:
|
||||
# check if we were a slave before
|
||||
master = self.ipcmd.link_get_master(ifaceobj.name)
|
||||
master = self.cache.get_master(ifaceobj.name)
|
||||
if master:
|
||||
self._iproute2_vrf_map_initialize()
|
||||
if self._is_vrf_dev(master):
|
||||
@@ -910,27 +924,36 @@ class vrf(moduleBase):
|
||||
vrf_table,
|
||||
mode))
|
||||
|
||||
def _close_sockets(self, ifaceobj, ifindex):
|
||||
def _close_sockets(self, ifacename):
|
||||
if not self.vrf_close_socks_on_down:
|
||||
return
|
||||
|
||||
try:
|
||||
ifindex = self.cache.get_ifindex(ifacename)
|
||||
except Exception as e:
|
||||
self.logger.debug("%s: vrf: close sockets error: %s" % str(e))
|
||||
ifindex = 0
|
||||
|
||||
if not ifindex:
|
||||
return
|
||||
|
||||
try:
|
||||
utils.exec_command('%s -aK \"dev == %s\"'
|
||||
%(utils.ss_cmd, ifindex))
|
||||
except Exception, e:
|
||||
self.logger.info('%s: closing socks using ss'
|
||||
' failed (%s)\n' %(ifaceobj.name, str(e)))
|
||||
' failed (%s)\n' %(ifacename, str(e)))
|
||||
pass
|
||||
|
||||
def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
|
||||
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
|
||||
if vrf_table == 'auto':
|
||||
vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
|
||||
|
||||
running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
|
||||
running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
|
||||
if running_slaves:
|
||||
for s in running_slaves:
|
||||
if ifaceobj_getfunc:
|
||||
@@ -943,10 +966,10 @@ class vrf(moduleBase):
|
||||
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
|
||||
pass
|
||||
try:
|
||||
self.ipcmd.addr_flush(s)
|
||||
netlink.link_set_updown(s, "down")
|
||||
self.netlink.addr_flush(s)
|
||||
self.netlink.link_down(s)
|
||||
except Exception, e:
|
||||
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
|
||||
self.logger.info('%s: %s' %(s, str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
@@ -961,16 +984,13 @@ class vrf(moduleBase):
|
||||
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
|
||||
pass
|
||||
|
||||
ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
|
||||
self._close_sockets(ifaceobj.name)
|
||||
|
||||
if ifindex:
|
||||
try:
|
||||
self.ipcmd.link_delete(ifaceobj.name)
|
||||
except Exception, e:
|
||||
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
|
||||
pass
|
||||
|
||||
self._close_sockets(ifaceobj, ifindex)
|
||||
try:
|
||||
self.netlink.link_del(ifaceobj.name)
|
||||
except Exception, e:
|
||||
self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
|
||||
pass
|
||||
|
||||
try:
|
||||
self._iproute2_vrf_table_entry_del(vrf_table)
|
||||
@@ -982,13 +1002,13 @@ class vrf(moduleBase):
|
||||
def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
|
||||
try:
|
||||
self._handle_existing_connections(ifaceobj, vrfname)
|
||||
self.ipcmd.link_set(ifacename, 'nomaster')
|
||||
self.netlink.link_set_nomaster(ifacename)
|
||||
# Down this slave only if it is a slave ifupdown2 manages.
|
||||
# we dont want to down slaves that maybe up'ed by
|
||||
# somebody else. One such example is a macvlan device
|
||||
# which ifupdown2 addressvirtual addon module auto creates
|
||||
if ifaceobj:
|
||||
netlink.link_set_updown(ifacename, "down")
|
||||
self.netlink.link_down(ifacename)
|
||||
except Exception, e:
|
||||
self.logger.warn('%s: %s' %(ifacename, str(e)))
|
||||
|
||||
@@ -1008,7 +1028,7 @@ class vrf(moduleBase):
|
||||
|
||||
def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
|
||||
try:
|
||||
master = self.ipcmd.link_get_master(ifaceobj.name)
|
||||
master = self.cache.get_master(ifaceobj.name)
|
||||
if not master or master != vrf:
|
||||
ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
|
||||
else:
|
||||
@@ -1018,27 +1038,18 @@ class vrf(moduleBase):
|
||||
|
||||
def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
|
||||
try:
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
|
||||
return
|
||||
if vrf_table == 'auto':
|
||||
config_table = self._get_iproute2_vrf_table(ifaceobj.name)
|
||||
config_table = str(self._get_iproute2_vrf_table(ifaceobj.name) or 0)
|
||||
else:
|
||||
config_table = vrf_table
|
||||
vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
|
||||
if not vrfdev_attrs:
|
||||
ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
|
||||
return
|
||||
running_table = vrfdev_attrs.get('table')
|
||||
if not running_table:
|
||||
ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
|
||||
return
|
||||
if config_table != running_table:
|
||||
ifaceobjcurr.update_config_with_status('vrf-table',
|
||||
running_table, 1)
|
||||
else:
|
||||
ifaceobjcurr.update_config_with_status('vrf-table',
|
||||
running_table, 0)
|
||||
|
||||
running_vrf_table = str(self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE))
|
||||
|
||||
ifaceobjcurr.update_config_with_status('vrf-table', running_vrf_table, config_table != running_vrf_table)
|
||||
|
||||
if not ifupdownflags.flags.WITHDEFAULTS:
|
||||
return
|
||||
if self.vrf_helper:
|
||||
@@ -1077,18 +1088,17 @@ class vrf(moduleBase):
|
||||
|
||||
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
|
||||
try:
|
||||
kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
|
||||
kind = self.cache.get_link_kind(ifaceobjrunning.name)
|
||||
if kind == 'vrf':
|
||||
vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
|
||||
if vrfdev_attrs:
|
||||
running_table = vrfdev_attrs.get('table')
|
||||
if running_table:
|
||||
ifaceobjrunning.update_config('vrf-table',
|
||||
running_table)
|
||||
return
|
||||
slave_kind = self.ipcmd.link_get_slave_kind(ifaceobjrunning.name)
|
||||
running_table = self.cache.get_link_info_data_attribute(ifaceobjrunning.name, Link.IFLA_VRF_TABLE)
|
||||
|
||||
if running_table is not None:
|
||||
ifaceobjrunning.update_config('vrf-table', str(running_table))
|
||||
return
|
||||
|
||||
slave_kind = self.cache.get_link_slave_kind(ifaceobjrunning.name)
|
||||
if slave_kind == 'vrf_slave':
|
||||
vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
|
||||
vrf = self.cache.get_master(ifaceobjrunning.name)
|
||||
if vrf:
|
||||
ifaceobjrunning.update_config('vrf', vrf)
|
||||
except Exception, e:
|
||||
@@ -1101,19 +1111,19 @@ class vrf(moduleBase):
|
||||
ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
|
||||
ifaceobj.name))
|
||||
|
||||
_run_ops = {'pre-up' : _up,
|
||||
'post-down' : _down,
|
||||
'query-running' : _query_running,
|
||||
'query-checkcurr' : _query_check,
|
||||
'query' : _query}
|
||||
_run_ops = {
|
||||
"pre-up": _up,
|
||||
"post-down": _down,
|
||||
"query-running": _query_running,
|
||||
"query-checkcurr": _query_check,
|
||||
"query": _query
|
||||
}
|
||||
|
||||
def get_ops(self):
|
||||
""" returns list of ops supported by this module """
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = self.bondcmd = LinkUtils()
|
||||
if not self.dhclientcmd:
|
||||
self.dhclientcmd = dhclient()
|
||||
|
||||
@@ -1143,3 +1153,6 @@ class vrf(moduleBase):
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
|
||||
if self.at_exit:
|
||||
self._iproute2_vrf_map_sync_to_disk()
|
||||
self.at_exit = False
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -11,11 +11,13 @@ import socket
|
||||
from ipaddr import IPNetwork, IPv6Network
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.addon import Addon
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.netlink import netlink
|
||||
|
||||
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
|
||||
from ifupdown2.nlmanager.nlpacket import Link
|
||||
|
||||
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown2.ifupdown.statemanager as statemanager
|
||||
@@ -23,11 +25,13 @@ try:
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
|
||||
except ImportError:
|
||||
from lib.addon import Addon
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.netlink import netlink
|
||||
|
||||
from ifupdownaddons.LinkUtils import LinkUtils
|
||||
from nlmanager.nlpacket import Link
|
||||
|
||||
from ifupdownaddons.modulebase import moduleBase
|
||||
|
||||
import ifupdown.statemanager as statemanager
|
||||
@@ -36,26 +40,26 @@ except ImportError:
|
||||
import ifupdown.ifupdownconfig as ifupdownconfig
|
||||
|
||||
|
||||
class xfrm(moduleBase):
|
||||
class xfrm(Addon, moduleBase):
|
||||
""" ifupdown2 addon module to create a xfrm interface """
|
||||
_modinfo = {'mhelp' : 'xfrm module creates a xfrm interface for',
|
||||
'attrs' : {
|
||||
'xfrm-id' :
|
||||
{ 'help' : 'xfrm id',
|
||||
'validrange' : ['1', '65535'],
|
||||
'example': ['xfrm-id 1']
|
||||
},
|
||||
'xfrm-physdev':
|
||||
{'help': 'xfrm physical device',
|
||||
'example': ['xfrm-physdev lo']
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_modinfo = {
|
||||
'mhelp': 'xfrm module creates a xfrm interface for',
|
||||
'attrs': {
|
||||
'xfrm-id': {
|
||||
'help': 'xfrm id',
|
||||
'validrange': ['1', '65535'],
|
||||
'example': ['xfrm-id 1']
|
||||
},
|
||||
'xfrm-physdev': {
|
||||
'help': 'xfrm physical device',
|
||||
'example': ['xfrm-physdev lo']
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
Addon.__init__(self)
|
||||
moduleBase.__init__(self, *args, **kargs)
|
||||
self.ipcmd = None
|
||||
|
||||
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
|
||||
|
||||
@@ -96,25 +100,29 @@ class xfrm(moduleBase):
|
||||
xfrm_ifacename = self._get_xfrm_name(ifaceobj)
|
||||
physdev = self._get_parent_ifacename(ifaceobj)
|
||||
xfrmid = self._get_xfrmid(ifaceobj)
|
||||
if not self.ipcmd.link_exists(xfrm_ifacename):
|
||||
try:
|
||||
netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
|
||||
except:
|
||||
self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
|
||||
link_created = True
|
||||
if not self.cache.link_exists(xfrm_ifacename):
|
||||
self.iproute2.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
|
||||
else:
|
||||
current_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
|
||||
xfrmid_cur = current_attrs.get('xfrm-id', None)
|
||||
physdev_cur = current_attrs.get('xfrm-physdev', None)
|
||||
xfrmid_cur = str(
|
||||
self.cache.get_link_info_data_attribute(
|
||||
xfrm_ifacename,
|
||||
Link.IFLA_XFRM_IF_ID,
|
||||
0
|
||||
)
|
||||
)
|
||||
physdev_cur = self.cache.get_ifname(
|
||||
self.cache.get_link_info_data_attribute(
|
||||
xfrm_ifacename,
|
||||
Link.IFLA_XFRM_LINK,
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
# Check XFRM Values
|
||||
if xfrmid != xfrmid_cur or physdev != physdev_cur:
|
||||
# Delete and recreate
|
||||
self.ipcmd.link_delete(xfrm_ifacename)
|
||||
try:
|
||||
netlink.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
|
||||
except:
|
||||
self.ipcmd.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
|
||||
link_created = True
|
||||
self.netlink.link_del(xfrm_ifacename)
|
||||
self.iproute2.link_add_xfrm(physdev, xfrm_ifacename, xfrmid)
|
||||
|
||||
def _down(self, ifaceobj, ifaceobj_getfunc=None):
|
||||
"""
|
||||
@@ -122,17 +130,17 @@ class xfrm(moduleBase):
|
||||
"""
|
||||
try:
|
||||
xfrm_ifacename = self._get_xfrm_name(ifaceobj)
|
||||
self.ipcmd.link_delete(xfrm_ifacename)
|
||||
except Exception, e:
|
||||
self.netlink.link_del(xfrm_ifacename)
|
||||
except Exception as e:
|
||||
self.log_warn(str(e))
|
||||
|
||||
def _query_check(self, ifaceobj, ifaceobjcurr):
|
||||
if not self.ipcmd.link_exists(ifaceobj.name):
|
||||
if not self.cache.link_exists(ifaceobj.name):
|
||||
return
|
||||
ifaceobjcurr.status = ifaceStatus.SUCCESS
|
||||
|
||||
def _query_running(self, ifaceobjrunning):
|
||||
if not self.ipcmd.link_exists(ifaceobjrunning.name):
|
||||
if not self.cache.link_exists(ifaceobjrunning.name):
|
||||
return
|
||||
|
||||
# Operations supported by this addon (yet).
|
||||
@@ -146,10 +154,6 @@ class xfrm(moduleBase):
|
||||
def get_ops(self):
|
||||
return self._run_ops.keys()
|
||||
|
||||
def _init_command_handlers(self):
|
||||
if not self.ipcmd:
|
||||
self.ipcmd = LinkUtils()
|
||||
|
||||
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
||||
op_handler = self._run_ops.get(operation)
|
||||
|
||||
@@ -159,7 +163,6 @@ class xfrm(moduleBase):
|
||||
if operation != 'query-running' and not self._is_my_interface(ifaceobj):
|
||||
return
|
||||
|
||||
self._init_command_handlers()
|
||||
if operation == 'query-checkcurr':
|
||||
op_handler(self, ifaceobj, query_ifaceobj)
|
||||
else:
|
||||
|
@@ -12,10 +12,10 @@ import argcomplete
|
||||
|
||||
try:
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.exceptions import ArgvParseError
|
||||
from ifupdown2.ifupdown.exceptions import ArgvParseError, ArgvParseHelp
|
||||
except:
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.exceptions import ArgvParseError
|
||||
from ifupdown.exceptions import ArgvParseError, ArgvParseHelp
|
||||
|
||||
|
||||
class VersionAction(argparse.Action):
|
||||
@@ -78,7 +78,18 @@ class Parse:
|
||||
self.update_ifquery_argparser(argparser)
|
||||
self.update_common_argparser(argparser)
|
||||
argcomplete.autocomplete(argparser)
|
||||
self.args = argparser.parse_args(self.argv)
|
||||
|
||||
try:
|
||||
self.args = argparser.parse_args(self.argv)
|
||||
except SystemExit as e:
|
||||
# on "--help" parse_args will raise SystemExit.
|
||||
# We need to catch this behavior and raise a custom
|
||||
# exception to return 0 properly
|
||||
#raise ArgvParseHelp()
|
||||
for help_str in ('-h', '--help'):
|
||||
if help_str in argv:
|
||||
raise ArgvParseHelp()
|
||||
raise
|
||||
|
||||
def validate(self):
|
||||
if self.op == 'query' and (self.args.syntaxhelp or self.args.list):
|
||||
@@ -102,8 +113,9 @@ class Parse:
|
||||
for key, value in self.valid_ops.iteritems():
|
||||
if self.executable_name.endswith(key):
|
||||
return value
|
||||
raise ArgvParseError("Unexpected executable. Should be '%s'" % "' or '".join(self.valid_ops.keys()))
|
||||
except:
|
||||
raise ArgvParseError("Unexpected executable. Should be 'ifup' or 'ifdown' or 'ifquery'")
|
||||
raise ArgvParseError("Unexpected executable. Should be '%s'" % "' or '".join(self.valid_ops.keys()))
|
||||
|
||||
def get_args(self):
|
||||
return self.args
|
||||
@@ -140,7 +152,7 @@ class Parse:
|
||||
def update_ifupdown_argparser(self, argparser):
|
||||
""" common arg parser for ifup and ifdown """
|
||||
argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
|
||||
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
|
||||
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true')
|
||||
group = argparser.add_mutually_exclusive_group(required=False)
|
||||
group.add_argument('-n', '--no-act', dest='noact', action='store_true',
|
||||
help="print out what would happen, but don't do it")
|
||||
@@ -220,7 +232,7 @@ class Parse:
|
||||
'With this option ifreload will only look at the current interfaces file. '
|
||||
'Useful when your state file is corrupted or you want down to use the latest '
|
||||
'from the interfaces file')
|
||||
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
|
||||
argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true')
|
||||
argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
|
||||
argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck', action='store_true',
|
||||
help='Only run the interfaces file parser')
|
||||
|
232
ifupdown2/ifupdown/client.py
Normal file
232
ifupdown2/ifupdown/client.py
Normal file
@@ -0,0 +1,232 @@
|
||||
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# ifupdown2 client-side
|
||||
#
|
||||
|
||||
import struct
|
||||
import pickle
|
||||
|
||||
import SocketServer
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import socket
|
||||
import signal
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.io import SocketIO
|
||||
from ifupdown2.lib.status import Status
|
||||
from ifupdown2.lib.log import LogManager, root_logger
|
||||
from ifupdown2.lib.exceptions import ExitWithStatus, ExitWithStatusAndError
|
||||
|
||||
from ifupdown2.ifupdown.argv import Parse
|
||||
except:
|
||||
from lib.status import Status
|
||||
from lib.io import SocketIO
|
||||
from lib.log import LogManager, root_logger
|
||||
from lib.exceptions import ExitWithStatus, ExitWithStatusAndError
|
||||
|
||||
from ifupdown.argv import Parse
|
||||
|
||||
|
||||
class LogRecordStreamHandler(SocketServer.StreamRequestHandler):
|
||||
"""
|
||||
Handler for a streaming logging request.
|
||||
This basically logs the record using whatever logging policy is configured
|
||||
locally.
|
||||
"""
|
||||
|
||||
def handle(self):
|
||||
"""
|
||||
Handle multiple requests - each expected to be a 4-byte length,
|
||||
followed by the LogRecord in pickle format.
|
||||
"""
|
||||
while True:
|
||||
chunk = self.connection.recv(4)
|
||||
if len(chunk) < 4:
|
||||
break
|
||||
slen = struct.unpack(">L", chunk)[0]
|
||||
chunk = self.connection.recv(slen)
|
||||
|
||||
while len(chunk) < slen:
|
||||
chunk = chunk + self.connection.recv(slen - len(chunk))
|
||||
|
||||
record = logging.makeLogRecord(pickle.loads(chunk))
|
||||
logging.getLogger(record.name).handle(record)
|
||||
|
||||
|
||||
class LogRecordSocketReceiver(SocketServer.TCPServer):
|
||||
"""
|
||||
Simple TCP socket-based logging receiver. In ifupdown2d context, the running
|
||||
daemon is the "sender" and the client is the "receiver". The TCPServer is
|
||||
setup on the client/receiver side, the daemon will connect to the server to
|
||||
transmit and stream LogRecord to a socket.
|
||||
"""
|
||||
allow_reuse_address = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
host="localhost",
|
||||
handler=LogRecordStreamHandler,
|
||||
port=LogManager.DEFAULT_TCP_LOGGING_PORT
|
||||
):
|
||||
SocketServer.TCPServer.__init__(self, (host, port), handler)
|
||||
|
||||
|
||||
class Client(SocketIO):
|
||||
def __init__(self, argv):
|
||||
SocketIO.__init__(self)
|
||||
|
||||
# we setup our log receiver which reads LogRecord from a socket
|
||||
# thus handing the logging-handling to the logging module and it's
|
||||
# dedicated classes.
|
||||
self.socket_receiver = LogRecordSocketReceiver()
|
||||
|
||||
self.stdin = None
|
||||
self.argv = argv
|
||||
|
||||
# First we need to set the correct log level for the client.
|
||||
# Unfortunately the only reliable way to do this is to use our main
|
||||
# argument parser. We can't simply have a parse to catch -v and -d, a
|
||||
# simple command like "ifup -av" wouldn't be recognized...
|
||||
# Ideally it would be great to be able to send the Namespace returned by
|
||||
# parse_args, and send it on the socket in pickle format. Unfortunately
|
||||
# this might be a serious security issue. It needs to be studied and
|
||||
# evaluated a bit more, that way we would save time and resources only
|
||||
# parsing argv once.
|
||||
args_parse = Parse(argv)
|
||||
args_parse.validate()
|
||||
# store the args namespace to send it to the daemon, we don't want
|
||||
# the daemon to spend time to parsing argv again...
|
||||
self.args = args_parse.get_args()
|
||||
|
||||
LogManager.get_instance().start_client_logging(self.args)
|
||||
|
||||
root_logger.info("starting ifupdown2 client...")
|
||||
|
||||
self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
try:
|
||||
self.uds.connect("/var/run/ifupdown2d/uds")
|
||||
except socket.error:
|
||||
self.__shutdown()
|
||||
sys.stderr.write("""
|
||||
ERROR: %s could not connect to ifupdown2 daemon
|
||||
|
||||
Try starting ifupdown2 daemon with:
|
||||
sudo systemctl start ifupdown2
|
||||
|
||||
To configure ifupdown2d to start when the box boots:
|
||||
sudo systemctl enable ifupdown2\n\n""" % argv[0])
|
||||
raise ExitWithStatus(status=Status.Client.STATUS_COULD_NOT_CONNECT)
|
||||
|
||||
signal.signal(signal.SIGINT, self.__signal_handler)
|
||||
signal.signal(signal.SIGTERM, self.__signal_handler)
|
||||
signal.signal(signal.SIGQUIT, self.__signal_handler)
|
||||
|
||||
try:
|
||||
self.SO_PEERCRED = socket.SO_PEERCRED
|
||||
except AttributeError:
|
||||
# powerpc is the only non-generic we care about. alpha, mips,
|
||||
# sparc, and parisc also have non-generic values.
|
||||
machine = os.uname()[4]
|
||||
if re.search(r"^(ppc|powerpc)", machine):
|
||||
self.SO_PASSCRED = 20
|
||||
self.SO_PEERCRED = 21
|
||||
else:
|
||||
self.SO_PASSCRED = 16
|
||||
self.SO_PEERCRED = 17
|
||||
try:
|
||||
self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
|
||||
except Exception as e:
|
||||
self.__shutdown()
|
||||
raise Exception("setsockopt: %s" % str(e))
|
||||
|
||||
self.daemon_pid, _, _ = self.get_socket_peer_cred(self.uds)
|
||||
|
||||
if self.daemon_pid < 0:
|
||||
self.__shutdown()
|
||||
raise ExitWithStatusAndError(
|
||||
status=Status.Client.STATUS_NO_PID,
|
||||
message="could not get ifupdown2 daemon PID"
|
||||
)
|
||||
|
||||
root_logger.info("connection to ifupdown2d successful (server pid %s)" % self.daemon_pid)
|
||||
|
||||
def __shutdown(self):
|
||||
try:
|
||||
self.uds.close()
|
||||
self.uds = None
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.socket_receiver.server_close()
|
||||
self.socket_receiver = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def __signal_handler(self, sig, frame):
|
||||
""" Forward all signals to daemon """
|
||||
if self.daemon_pid > 0:
|
||||
os.kill(self.daemon_pid, sig)
|
||||
|
||||
def __get_stdin(self):
|
||||
"""
|
||||
If stdin data is provided we need to store it to forward it to the
|
||||
daemon
|
||||
"""
|
||||
if hasattr(self.args, "interfacesfile") and self.args.interfacesfile == "-":
|
||||
return sys.stdin.read()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
# First we need to send the user request to the daemon (argv + stdin)
|
||||
self.tx_data(self.uds, json.dumps({
|
||||
"argv": self.argv,
|
||||
"stdin": self.__get_stdin()
|
||||
}))
|
||||
|
||||
# Then "handle_request" will block until the daemon closes
|
||||
# the channel, meaning that the request was processed.
|
||||
self.socket_receiver.handle_request()
|
||||
self.socket_receiver.server_close()
|
||||
|
||||
# Next the daemon should send us a dictionary containing stdout and
|
||||
# stderr buffers as well as the request's exit status. We print those
|
||||
# buffers in the correct channel and exit with the request status.
|
||||
response = self.rx_json_packet(self.uds)
|
||||
|
||||
if response:
|
||||
sys.stdout.write(response.get("stdout", ""))
|
||||
sys.stderr.write(response.get("stderr", ""))
|
||||
|
||||
status = response.get("status", Status.Client.STATUS_EMPTY)
|
||||
else:
|
||||
status = Status.Client.STATUS_EMPTY
|
||||
|
||||
return status
|
||||
finally:
|
||||
self.__shutdown()
|
@@ -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
|
||||
|
||||
|
@@ -14,9 +14,12 @@ file. But can be extended to include any other network interface format
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
log = logging.getLogger()
|
||||
|
||||
|
||||
class ifaceStatusUserStrs():
|
||||
""" This class declares strings user can see during an ifquery --check
|
||||
@@ -47,16 +50,16 @@ class ifaceLinkKind():
|
||||
bond have an ifaceobj.role attribute of SLAVE and the bridge or
|
||||
bond itself has ifaceobj.role of MASTER.
|
||||
"""
|
||||
UNKNOWN = 0x0000000
|
||||
BRIDGE = 0x0000001
|
||||
BOND = 0x0000010
|
||||
VLAN = 0x0000100
|
||||
VXLAN = 0x0001000
|
||||
VRF = 0x0010000
|
||||
BATMAN_ADV = 0x0100000
|
||||
UNKNOWN = 0x000000
|
||||
BRIDGE = 0x000001
|
||||
BOND = 0x000010
|
||||
VLAN = 0x000100
|
||||
VXLAN = 0x001000
|
||||
VRF = 0x010000
|
||||
BATMAN_ADV = 0x0100000
|
||||
# to indicate logical interface created by an external entity.
|
||||
# the 'kind' of which ifupdown2 does not really understand
|
||||
OTHER = 0x1000000
|
||||
OTHER = 0x100000
|
||||
|
||||
@classmethod
|
||||
def to_str(cls, kind):
|
||||
@@ -70,6 +73,8 @@ class ifaceLinkKind():
|
||||
return "vxlan"
|
||||
elif kind == cls.VRF:
|
||||
return "vrf"
|
||||
else:
|
||||
return "OTHER"
|
||||
|
||||
class ifaceLinkPrivFlags():
|
||||
""" This corresponds to kernel netdev->priv_flags
|
||||
@@ -86,33 +91,34 @@ class ifaceLinkPrivFlags():
|
||||
|
||||
@classmethod
|
||||
def get_str(cls, flag):
|
||||
if flag == cls.UNKNOWN:
|
||||
return 'unknown'
|
||||
elif flag == cls.BRIDGE_PORT:
|
||||
return 'bridge port'
|
||||
elif flag == cls.BOND_SLAVE:
|
||||
return 'bond slave'
|
||||
elif flag == cls.VRF_SLAVE:
|
||||
return 'vrf slave'
|
||||
elif flag == cls.BRIDGE_VLAN_AWARE:
|
||||
return 'vlan aware bridge'
|
||||
elif flag == cls.BRIDGE_VXLAN:
|
||||
return 'vxlan bridge'
|
||||
string_list = []
|
||||
|
||||
if flag & cls.BRIDGE_PORT:
|
||||
string_list.append("bridge port")
|
||||
|
||||
if flag & cls.BOND_SLAVE:
|
||||
string_list.append("bond slave")
|
||||
|
||||
if flag & cls.VRF_SLAVE:
|
||||
string_list.append("vrf slave")
|
||||
|
||||
if flag & cls.BRIDGE_VLAN_AWARE:
|
||||
string_list.append("vlan aware bridge")
|
||||
|
||||
if flag & cls.BRIDGE_VXLAN:
|
||||
string_list.append("vxlan bridge")
|
||||
|
||||
if flag & cls.ADDRESS_VIRTUAL_SLAVE:
|
||||
string_list.append("address virtual slave")
|
||||
|
||||
if flag & cls.LOOPBACK:
|
||||
string_list.append("loopback")
|
||||
|
||||
if flag & cls.KEEP_LINK_DOWN:
|
||||
string_list.append("keep ling down")
|
||||
|
||||
return ", ".join(string_list)
|
||||
|
||||
@classmethod
|
||||
def get_all_str(cls, flags):
|
||||
str = ''
|
||||
if flags & cls.BRIDGE_PORT:
|
||||
str += 'bridgeport '
|
||||
if flags & cls.BOND_SLAVE:
|
||||
str += 'bondslave '
|
||||
if flags & cls.VRF_SLAVE:
|
||||
str += 'vrfslave '
|
||||
if flags & cls.BRIDGE_VLAN_AWARE:
|
||||
str += 'vlanawarebridge '
|
||||
if flags & cls.BRIDGE_VXLAN:
|
||||
str += 'vxlanbridge '
|
||||
return str
|
||||
|
||||
class ifaceLinkType():
|
||||
LINK_UNKNOWN = 0x0
|
||||
@@ -647,14 +653,13 @@ class iface():
|
||||
del odict['env']
|
||||
del odict['link_type']
|
||||
del odict['link_kind']
|
||||
del odict['link_privflags']
|
||||
#del odict['link_privflags']
|
||||
del odict['role']
|
||||
del odict['dependency_type']
|
||||
del odict['blacklisted']
|
||||
return odict
|
||||
|
||||
def __setstate__(self, dict):
|
||||
self.__dict__.update(dict)
|
||||
self._config_status = {}
|
||||
self.state = ifaceState.NEW
|
||||
self.status = ifaceStatus.UNKNOWN
|
||||
@@ -674,6 +679,7 @@ class iface():
|
||||
self.link_privflags = ifaceLinkPrivFlags.UNKNOWN
|
||||
self.dependency_type = ifaceDependencyType.UNKNOWN
|
||||
self.blacklisted = False
|
||||
self.__dict__.update(dict)
|
||||
|
||||
def dump_raw(self, logger):
|
||||
indent = ' '
|
||||
@@ -706,6 +712,24 @@ class iface():
|
||||
else:
|
||||
logger.info(indent + 'upperdevs: None')
|
||||
|
||||
logger.info("%srole: %s" % (indent, {
|
||||
ifaceRole.UNKNOWN: "UNKNOWN",
|
||||
ifaceRole.SLAVE: "SLAVE",
|
||||
ifaceRole.MASTER: "MASTER"}.get(self.role)))
|
||||
|
||||
logger.info("%stype: %s" % (indent, {
|
||||
ifaceType.UNKNOWN: "UNKNOWN",
|
||||
ifaceType.IFACE: "IFACE",
|
||||
ifaceType.BRIDGE_VLAN: "BRIDGE_VLAN"}.get(self.type)))
|
||||
|
||||
logger.info("%slink_kind: %s" % (indent, ifaceLinkKind.to_str(self.link_kind)))
|
||||
|
||||
logger.info("%slink_privflags: %s" % (indent, ifaceLinkPrivFlags.get_str(self.link_privflags)))
|
||||
|
||||
if self.priv_flags:
|
||||
if self.priv_flags.BUILTIN:
|
||||
logger.info("%spriv_flags: BUILTIN" % indent)
|
||||
|
||||
logger.info(indent + 'config: ')
|
||||
config = self.config
|
||||
if config:
|
||||
@@ -768,7 +792,8 @@ class iface():
|
||||
outbuf += (indent + '{0:55} {1:>10}'.format(
|
||||
'%s %s' %(cname, cv), status_str)) + '\n'
|
||||
else:
|
||||
outbuf += indent + '%s %s\n' %(cname, cv)
|
||||
if cv:
|
||||
outbuf += indent + '%s %s\n' % (cname, cv)
|
||||
idx += 1
|
||||
if with_status:
|
||||
outbuf = (outbuf.encode('utf8')
|
||||
|
@@ -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
|
@@ -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")
|
@@ -7,6 +7,10 @@
|
||||
# ifupdown main module
|
||||
#
|
||||
|
||||
import re
|
||||
import os
|
||||
import logging
|
||||
import traceback
|
||||
import pprint
|
||||
|
||||
from collections import OrderedDict
|
||||
@@ -14,12 +18,11 @@ from collections import OrderedDict
|
||||
from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPAddress, IPv4Address, IPv6Address
|
||||
|
||||
try:
|
||||
import ifupdown2.ifupdownaddons.cache
|
||||
import ifupdown2.ifupdownaddons.LinkUtils
|
||||
import ifupdown2.lib.nlcache as nlcache
|
||||
|
||||
import ifupdown2.ifupdownaddons.mstpctlutil
|
||||
|
||||
import ifupdown2.ifupdown.policymanager
|
||||
import ifupdown2.ifupdown.ifupdownflags
|
||||
import ifupdown2.ifupdown.statemanager as statemanager
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown2.ifupdown.ifupdownconfig as ifupdownConfig
|
||||
@@ -31,8 +34,8 @@ try:
|
||||
from ifupdown2.ifupdown.networkinterfaces import *
|
||||
from ifupdown2.ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
|
||||
except ImportError:
|
||||
import ifupdownaddons.cache
|
||||
import ifupdownaddons.LinkUtils
|
||||
import lib.nlcache as nlcache
|
||||
|
||||
import ifupdownaddons.mstpctlutil
|
||||
|
||||
import ifupdown.ifupdownflags
|
||||
@@ -80,7 +83,7 @@ class ifacePrivFlags():
|
||||
self.BUILTIN = builtin
|
||||
self.NOCONFIG = noconfig
|
||||
|
||||
class ifupdownMain(ifupdownBase):
|
||||
class ifupdownMain:
|
||||
""" ifupdown2 main class """
|
||||
|
||||
scripts_dir = '/etc/network'
|
||||
@@ -111,7 +114,7 @@ class ifupdownMain(ifupdownBase):
|
||||
if self._keep_link_down(ifaceobj):
|
||||
return
|
||||
try:
|
||||
self.link_up(ifaceobj.name)
|
||||
self.netlink.link_up(ifaceobj.name)
|
||||
except:
|
||||
if ifaceobj.addr_method == 'manual':
|
||||
pass
|
||||
@@ -123,7 +126,7 @@ class ifupdownMain(ifupdownBase):
|
||||
# user has asked to explicitly keep the link down,
|
||||
# so, force link down
|
||||
self.logger.info('%s: keeping link down due to user config' %ifaceobj.name)
|
||||
self.link_down(ifaceobj.name)
|
||||
self.netlink.link_down(ifaceobj.name)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -147,7 +150,11 @@ class ifupdownMain(ifupdownBase):
|
||||
if not self.link_exists(ifaceobj.name):
|
||||
return
|
||||
try:
|
||||
self.link_down(ifaceobj.name)
|
||||
if not ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
|
||||
# set intf down (except loopback)
|
||||
self.netlink.link_down(ifaceobj.name)
|
||||
else:
|
||||
self.logger.info("%s: ifupdown2 cannot bring loopback interface down" % ifaceobj.name)
|
||||
except:
|
||||
if ifaceobj.addr_method == 'manual':
|
||||
pass
|
||||
@@ -169,17 +176,48 @@ class ifupdownMain(ifupdownBase):
|
||||
sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
|
||||
|
||||
def reset_ifupdown2(self):
|
||||
self.modules = OrderedDict({})
|
||||
self.module_attrs = {}
|
||||
|
||||
ifaceScheduler.reset()
|
||||
try:
|
||||
ifupdown2.ifupdown.statemanager.reset()
|
||||
ifupdown2.ifupdown.policymanager.reset()
|
||||
ifupdown2.ifupdown.ifupdownflags.reset()
|
||||
ifupdownConfig.reset()
|
||||
ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
|
||||
except:
|
||||
try:
|
||||
ifupdown.statemanager.reset()
|
||||
ifupdown.policymanager.reset()
|
||||
ifupdown.ifupdownflags.reset()
|
||||
ifupdownConfig.reset()
|
||||
ifupdownaddons.mstpctlutil.mstpctlutil.reset()
|
||||
except:
|
||||
pass
|
||||
|
||||
ifupdown2.ifupdown.statemanager.reset()
|
||||
ifupdown2.ifupdown.policymanager.reset()
|
||||
ifupdown2.ifupdown.ifupdownflags.reset()
|
||||
ifupdownConfig.reset()
|
||||
ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
|
||||
ifupdown2.ifupdownaddons.LinkUtils.LinkUtils.reset()
|
||||
def ignore_error(self, errmsg):
|
||||
if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
|
||||
re.IGNORECASE | re.MULTILINE) is not None):
|
||||
return True
|
||||
return False
|
||||
|
||||
ifupdown2.ifupdownaddons.cache.linkCache.reset()
|
||||
ifupdown2.ifupdownaddons.cache.MSTPAttrsCache.invalidate()
|
||||
def log_warn(self, str):
|
||||
if self.ignore_error(str) == False:
|
||||
if self.logger.getEffectiveLevel() == logging.DEBUG:
|
||||
traceback.print_stack()
|
||||
traceback.print_exc()
|
||||
self.logger.warn(str)
|
||||
pass
|
||||
|
||||
def log_error(self, str):
|
||||
if self.ignore_error(str) == False:
|
||||
raise Exception(str)
|
||||
else:
|
||||
pass
|
||||
|
||||
def link_exists(self, ifacename):
|
||||
return os.path.exists('/sys/class/net/%s' %ifacename)
|
||||
|
||||
def __init__(self, config={},
|
||||
daemon=False, force=False, dryrun=False, nowait=False,
|
||||
@@ -202,8 +240,21 @@ class ifupdownMain(ifupdownBase):
|
||||
Raises:
|
||||
AttributeError, KeyError """
|
||||
|
||||
modulename = self.__class__.__name__
|
||||
self.logger = logging.getLogger('ifupdown.' + modulename)
|
||||
|
||||
if daemon:
|
||||
self.reset_ifupdown2()
|
||||
else:
|
||||
# init nlcache with appropriate log level
|
||||
nlcache.NetlinkListenerWithCache.init(logging.root.level)
|
||||
|
||||
# start netlink listener and cache link/addr/netconf dumps
|
||||
nlcache.NetlinkListenerWithCache.get_instance().start()
|
||||
|
||||
# save reference to nlcache
|
||||
self.netlink = nlcache.NetlinkListenerWithCache.get_instance()
|
||||
self.netlink.reset_errorq()
|
||||
|
||||
# iface dictionary in the below format:
|
||||
# { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
|
||||
@@ -444,9 +495,6 @@ class ifupdownMain(ifupdownBase):
|
||||
else:
|
||||
return ifaceobjlist[idx]
|
||||
|
||||
def get_ifaceobjrunning(self, ifacename):
|
||||
return self.ifaceobjrunningdict.get(ifacename)
|
||||
|
||||
def get_iface_refcnt(self, ifacename):
|
||||
""" Return iface ref count """
|
||||
max = 0
|
||||
@@ -522,7 +570,7 @@ class ifupdownMain(ifupdownBase):
|
||||
(role == ifaceRole.SLAVE) and (upperifaceobj.role & ifaceRole.MASTER)):
|
||||
self.logger.error("misconfig..? %s %s is enslaved to multiple interfaces %s"
|
||||
%(ifaceobj.name,
|
||||
ifaceLinkPrivFlags.get_all_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
|
||||
ifaceLinkPrivFlags.get_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
|
||||
ifaceobj.set_status(ifaceStatus.ERROR)
|
||||
return
|
||||
ifaceobj.role = role
|
||||
@@ -855,13 +903,12 @@ class ifupdownMain(ifupdownBase):
|
||||
|
||||
def _keyword_mac_ip_prefixlen_list(self, value, validrange=None):
|
||||
"""
|
||||
<mac> <ip> [<ip> ...]
|
||||
MAC address followed by optional list of ip addresses
|
||||
<mac> [<ip> <ip> ...]
|
||||
ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
|
||||
"""
|
||||
try:
|
||||
res = value.split()
|
||||
if len(res) < 2:
|
||||
return False
|
||||
if not self._keyword_mac(res[0]):
|
||||
return False
|
||||
for ip in res[1:]:
|
||||
@@ -1254,7 +1301,7 @@ class ifupdownMain(ifupdownBase):
|
||||
continue
|
||||
return ret
|
||||
|
||||
def read_iface_config(self):
|
||||
def read_iface_config(self, raw=False):
|
||||
""" Reads default network interface config /etc/network/interfaces. """
|
||||
ret = True
|
||||
nifaces = networkInterfaces(self.interfacesfile,
|
||||
@@ -1262,7 +1309,8 @@ class ifupdownMain(ifupdownBase):
|
||||
self.interfacesfileformat,
|
||||
template_enable=self.config.get('template_enable', 0),
|
||||
template_engine=self.config.get('template_engine'),
|
||||
template_lookuppath=self.config.get('template_lookuppath'))
|
||||
template_lookuppath=self.config.get('template_lookuppath'),
|
||||
raw=raw)
|
||||
if self._ifaceobj_squash or self._ifaceobj_squash_internal:
|
||||
nifaces.subscribe('iface_found', self._save_iface_squash)
|
||||
else:
|
||||
@@ -1331,7 +1379,7 @@ class ifupdownMain(ifupdownBase):
|
||||
script_override = minstance.get_overrides_ifupdown_scripts()
|
||||
self.overridden_ifupdown_scripts.extend(script_override)
|
||||
except moduleNotSupported, e:
|
||||
self.logger.info('module %s not loaded (%s)\n'
|
||||
self.logger.info('module %s not loaded (%s)'
|
||||
%(mname, str(e)))
|
||||
continue
|
||||
except:
|
||||
@@ -1598,9 +1646,9 @@ class ifupdownMain(ifupdownBase):
|
||||
if not self._delay_admin_state_iface_queue:
|
||||
return
|
||||
if op == 'up':
|
||||
func = self.link_up
|
||||
func = self.netlink.link_up
|
||||
elif op == 'down':
|
||||
func = self.link_down
|
||||
func = self.netlink.link_down
|
||||
else:
|
||||
return
|
||||
for i in self._delay_admin_state_iface_queue:
|
||||
@@ -1611,6 +1659,38 @@ class ifupdownMain(ifupdownBase):
|
||||
self.logger.warn(str(e))
|
||||
pass
|
||||
|
||||
def _get_iface_exclude_companion(self, ifacename):
|
||||
try:
|
||||
return ifupdown2.ifupdown.policymanager.policymanager_api.get_iface_default(
|
||||
module_name='main', ifname=ifacename,
|
||||
attr='exclude-companion')
|
||||
except:
|
||||
return ifupdown.policymanager.policymanager_api.get_iface_default(
|
||||
module_name='main', ifname=ifacename,
|
||||
attr='exclude-companion')
|
||||
|
||||
def _preprocess_excludepats(self, excludepats):
|
||||
new_excludepats = excludepats
|
||||
for e in excludepats:
|
||||
ifaceobjs = self.get_ifaceobjs(e)
|
||||
for iobj in ifaceobjs or []:
|
||||
ec = iobj.get_attr_value_first('exclude-companion')
|
||||
if not ec:
|
||||
ec = self._get_iface_exclude_companion(e)
|
||||
if not ec:
|
||||
continue
|
||||
else:
|
||||
ec = ec.encode('ascii','ignore')
|
||||
for ee in ec.split():
|
||||
if ee in new_excludepats:
|
||||
continue
|
||||
if self.get_ifaceobjs(ee):
|
||||
# if we know the object add it to the new
|
||||
# excludepats list
|
||||
new_excludepats.append(ee)
|
||||
self.logger.info('excludepats after processing companions [%s]' %' '.join(new_excludepats))
|
||||
return new_excludepats
|
||||
|
||||
def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
|
||||
excludepats=None, printdependency=None, syntaxcheck=False,
|
||||
type=None, skipupperifaces=False):
|
||||
@@ -1642,6 +1722,9 @@ class ifupdownMain(ifupdownBase):
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
if excludepats:
|
||||
excludepats = self._preprocess_excludepats(excludepats)
|
||||
|
||||
filtered_ifacenames = None
|
||||
if ifacenames:
|
||||
ifacenames = self._preprocess_ifacenames(ifacenames)
|
||||
@@ -1695,6 +1778,35 @@ class ifupdownMain(ifupdownBase):
|
||||
if not iface_read_ret or not ret:
|
||||
raise Exception()
|
||||
|
||||
self.check_running_configuration(filtered_ifacenames)
|
||||
|
||||
def check_running_configuration(self, filtered_ifacenames, all=False):
|
||||
"""
|
||||
Print warning and better info message when we don't recognize an interface
|
||||
AKA when the interface wasn't created.
|
||||
|
||||
:param filtered_ifacenames:
|
||||
:param all:
|
||||
:return:
|
||||
"""
|
||||
if not filtered_ifacenames:
|
||||
filtered_ifacenames = []
|
||||
|
||||
for ifname, ifaceobj_list in self.ifaceobjdict.iteritems():
|
||||
|
||||
if not all and ifname not in filtered_ifacenames:
|
||||
continue
|
||||
|
||||
auto = True
|
||||
|
||||
for ifaceobj in ifaceobj_list:
|
||||
if not ifaceobj.auto:
|
||||
auto = False
|
||||
break
|
||||
|
||||
if auto and not os.path.exists("/sys/class/net/%s" % ifname):
|
||||
self.logger.warning("%s: interface not recognized - please check interface configuration" % ifname)
|
||||
|
||||
def _get_filtered_ifacenames_with_classes(self, auto, allow_classes, excludepats, ifacenames):
|
||||
# if user has specified ifacelist and allow_classes
|
||||
# append the allow_classes interfaces to user
|
||||
@@ -1738,6 +1850,10 @@ class ifupdownMain(ifupdownBase):
|
||||
self.read_iface_config()
|
||||
except Exception, e:
|
||||
raise Exception('error reading iface config (%s)' %str(e))
|
||||
|
||||
if excludepats:
|
||||
excludepats = self._preprocess_excludepats(excludepats)
|
||||
|
||||
filtered_ifacenames = None
|
||||
if ifacenames:
|
||||
# If iface list is given by the caller, always check if iface
|
||||
@@ -1817,7 +1933,7 @@ class ifupdownMain(ifupdownBase):
|
||||
ifacePrivFlags(False, True)), ifacenames)
|
||||
else:
|
||||
try:
|
||||
iface_read_ret = self.read_iface_config()
|
||||
iface_read_ret = self.read_iface_config(raw=ops[0] == "query-raw")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
@@ -1859,7 +1975,7 @@ class ifupdownMain(ifupdownBase):
|
||||
if ops[0] == 'query' and not ifupdownflags.flags.WITHDEFAULTS:
|
||||
return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
|
||||
elif ops[0] == 'query-raw':
|
||||
return self.print_ifaceobjs_raw(filtered_ifacenames)
|
||||
return self.print_ifaceobjs_raw(filtered_ifacenames, format)
|
||||
|
||||
ret = self._sched_ifaces(filtered_ifacenames, ops,
|
||||
followdependents=True
|
||||
@@ -2028,6 +2144,9 @@ class ifupdownMain(ifupdownBase):
|
||||
new_ifaceobjdict = self.ifaceobjdict
|
||||
new_dependency_graph = self.dependency_graph
|
||||
|
||||
if excludepats:
|
||||
excludepats = self._preprocess_excludepats(excludepats)
|
||||
|
||||
if op == 'reload' and ifacenames:
|
||||
ifacenames = self.ifaceobjdict.keys()
|
||||
old_filtered_ifacenames = [i for i in ifacenames
|
||||
@@ -2236,6 +2355,8 @@ class ifupdownMain(ifupdownBase):
|
||||
else:
|
||||
self._reload_default(*args, **kargs)
|
||||
|
||||
self.check_running_configuration([], all=True)
|
||||
|
||||
def _any_iface_errors(self, ifacenames):
|
||||
for i in ifacenames:
|
||||
ifaceobjs = self.get_ifaceobjs(i)
|
||||
@@ -2271,9 +2392,13 @@ class ifupdownMain(ifupdownBase):
|
||||
for i in ifacenames:
|
||||
print i
|
||||
|
||||
def print_ifaceobjs_raw(self, ifacenames):
|
||||
def print_ifaceobjs_raw(self, ifacenames, format=None):
|
||||
""" prints raw lines for ifaces from config file """
|
||||
|
||||
if format == "json":
|
||||
self.print_ifaceobjs_pretty(ifacenames, format)
|
||||
return
|
||||
|
||||
for i in ifacenames:
|
||||
for ifaceobj in self.get_ifaceobjs(i):
|
||||
if self.is_ifaceobj_builtin(ifaceobj):
|
||||
@@ -2310,7 +2435,7 @@ class ifupdownMain(ifupdownBase):
|
||||
if not ifaceobjs: return
|
||||
if format == 'json':
|
||||
print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
|
||||
indent=4, separators=(',', ': '))
|
||||
indent=2, separators=(',', ': '))
|
||||
else:
|
||||
expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
|
||||
for i in ifaceobjs:
|
||||
|
@@ -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()
|
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
|
||||
# Copyright 2014-2019 Cumulus Networks, Inc. All rights reserved.
|
||||
# Authors:
|
||||
# Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
@@ -10,26 +10,26 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import logging
|
||||
import StringIO
|
||||
import ConfigParser
|
||||
|
||||
try:
|
||||
from ifupdown2.ifupdown.log import log
|
||||
from ifupdown2.ifupdown.argv import Parse
|
||||
from ifupdown2.ifupdown.config import IFUPDOWN2_CONF_PATH
|
||||
from ifupdown2.ifupdown.ifupdownmain import ifupdownMain
|
||||
|
||||
from ifupdown2.lib.dry_run import DryRunManager
|
||||
|
||||
except ImportError:
|
||||
from ifupdown.log import log
|
||||
from ifupdown.argv import Parse
|
||||
from ifupdown.config import IFUPDOWN2_CONF_PATH
|
||||
from ifupdown.ifupdownmain import ifupdownMain
|
||||
|
||||
from lib.dry_run import DryRunManager
|
||||
|
||||
_SIGINT = signal.getsignal(signal.SIGINT)
|
||||
_SIGTERM = signal.getsignal(signal.SIGTERM)
|
||||
_SIGQUIT = signal.getsignal(signal.SIGQUIT)
|
||||
|
||||
log = logging.getLogger()
|
||||
configmap_g = None
|
||||
|
||||
|
||||
@@ -57,14 +57,6 @@ class Ifupdown2:
|
||||
self.args = args_parse.get_args()
|
||||
self.op = args_parse.get_op()
|
||||
|
||||
def update_logger(self, socket=None):
|
||||
syslog = self.args.syslog if hasattr(self.args, 'syslog') else False
|
||||
log.update_current_logger(syslog=syslog,
|
||||
verbose=self.args.verbose,
|
||||
debug=self.args.debug)
|
||||
if socket:
|
||||
log.set_socket(socket)
|
||||
|
||||
def main(self, stdin_buffer=None):
|
||||
if self.op != 'query' and self.uid != 0:
|
||||
raise Exception('must be root to run this command')
|
||||
@@ -80,7 +72,7 @@ class Ifupdown2:
|
||||
raise
|
||||
# else:
|
||||
if log:
|
||||
log.error(str(e))
|
||||
log.error('main exception: ' + str(e))
|
||||
else:
|
||||
print str(e)
|
||||
# if args and not args.debug:
|
||||
@@ -89,6 +81,23 @@ class Ifupdown2:
|
||||
return 0
|
||||
|
||||
def init(self, stdin_buffer):
|
||||
##############
|
||||
# dry run mode
|
||||
##############
|
||||
dry_run_mode_on = DryRunManager.get_instance().is_dry_mode_on()
|
||||
|
||||
if hasattr(self.args, 'noact'):
|
||||
if self.args.noact and not dry_run_mode_on:
|
||||
DryRunManager.get_instance().dry_run_mode_on()
|
||||
elif not self.args.noact and dry_run_mode_on:
|
||||
DryRunManager.get_instance().dry_run_mode_off()
|
||||
elif dry_run_mode_on:
|
||||
# if noact is not in self.args we are probably in
|
||||
# ifquery mode so we need to turn off dry run mode.
|
||||
DryRunManager.get_instance().dry_run_mode_off()
|
||||
|
||||
###
|
||||
|
||||
if hasattr(self.args, 'interfacesfile') and self.args.interfacesfile != None:
|
||||
# Check to see if -i option is allowed by config file
|
||||
# But for ifquery, we will not check this
|
||||
@@ -269,9 +278,3 @@ class Ifupdown2:
|
||||
currentlyup=args.currentlyup)
|
||||
except:
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def set_signal_handlers():
|
||||
signal.signal(signal.SIGQUIT, _SIGQUIT)
|
||||
signal.signal(signal.SIGTERM, _SIGTERM)
|
||||
signal.signal(signal.SIGINT, _SIGINT)
|
||||
|
@@ -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()
|
@@ -30,11 +30,12 @@ class networkInterfaces():
|
||||
|
||||
_addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel'],
|
||||
'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel']}
|
||||
# tunnel is part of the address family for backward compatibility but is not required.
|
||||
|
||||
def __init__(self, interfacesfile='/etc/network/interfaces',
|
||||
interfacesfileiobuf=None, interfacesfileformat='native',
|
||||
template_enable='0', template_engine=None,
|
||||
template_lookuppath=None):
|
||||
template_lookuppath=None, raw=False):
|
||||
"""This member function initializes the networkinterfaces parser object.
|
||||
|
||||
Kwargs:
|
||||
@@ -54,7 +55,7 @@ class networkInterfaces():
|
||||
self.auto_ifaces = []
|
||||
self.callbacks = {}
|
||||
self.auto_all = False
|
||||
|
||||
self.raw = raw
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
self.callbacks = {'iface_found' : None,
|
||||
@@ -257,12 +258,14 @@ class networkInterfaces():
|
||||
self._parse_warn(self._currentfile, lineno,
|
||||
'%s: unexpected characters in interface name' %ifacename)
|
||||
|
||||
ifaceobj.raw_config.append(iface_line)
|
||||
if self.raw:
|
||||
ifaceobj.raw_config.append(iface_line)
|
||||
iface_config = collections.OrderedDict()
|
||||
for line_idx in range(cur_idx + 1, len(lines)):
|
||||
l = lines[line_idx].strip(whitespaces)
|
||||
if self.ignore_line(l) == 1:
|
||||
ifaceobj.raw_config.append(l)
|
||||
if self.raw:
|
||||
ifaceobj.raw_config.append(l)
|
||||
continue
|
||||
attrs = re.split(self._ws_split_regex, l, 1)
|
||||
if self._is_keyword(attrs[0]):
|
||||
@@ -273,7 +276,8 @@ class networkInterfaces():
|
||||
self._parse_error(self._currentfile, line_idx,
|
||||
'iface %s: invalid syntax \'%s\'' %(ifacename, l))
|
||||
continue
|
||||
ifaceobj.raw_config.append(l)
|
||||
if self.raw:
|
||||
ifaceobj.raw_config.append(l)
|
||||
attrname = attrs[0]
|
||||
# preprocess vars (XXX: only preprocesses $IFACE for now)
|
||||
attrval = re.sub(r'\$IFACE', ifacename, attrs[1])
|
||||
|
@@ -40,6 +40,8 @@ class policymanager():
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
|
||||
self.logger.info("policymanager init")
|
||||
|
||||
# we grab the json files from a known location and make sure that
|
||||
# the defaults_policy is checked first
|
||||
user_files = glob.glob('/etc/network/ifupdown2/policy.d/*.json')
|
||||
@@ -64,7 +66,16 @@ class policymanager():
|
||||
self.logger.debug("policymanager: merging system module %s policy with file %s" % (module, filename))
|
||||
self.system_policy_array[module].update(system_array[module])
|
||||
else:
|
||||
self.system_policy_array[module] = system_array[module]
|
||||
json_dict = system_array[module]
|
||||
|
||||
if isinstance(json_dict, dict):
|
||||
self.system_policy_array[module] = system_array[module]
|
||||
elif module != "README":
|
||||
self.logger.warning(
|
||||
"file %s contains an invalid policy schema, key "
|
||||
"\"%s\" contains %s when a dictionary is expected" %
|
||||
(filename, module, type(json_dict))
|
||||
)
|
||||
|
||||
# take care of user defined policy defaults
|
||||
self.user_policy_array = {}
|
||||
|
@@ -14,22 +14,20 @@ from sets import Set
|
||||
|
||||
try:
|
||||
from ifupdown2.ifupdown.graph import *
|
||||
from ifupdown2.ifupdown.ifupdownbase import *
|
||||
|
||||
from ifupdown2.ifupdown.iface import *
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.statemanager import *
|
||||
|
||||
import ifupdown2.ifupdown.policymanager as policymanager
|
||||
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
||||
except ImportError:
|
||||
from ifupdown.graph import *
|
||||
from ifupdown.ifupdownbase import *
|
||||
|
||||
from ifupdown.iface import *
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.statemanager import *
|
||||
|
||||
import ifupdown.ifupdownflags as ifupdownflags
|
||||
import ifupdown.policymanager as policymanager
|
||||
|
||||
|
||||
class ifaceSchedulerFlags():
|
||||
@@ -50,6 +48,11 @@ class ifaceScheduler():
|
||||
|
||||
_SCHED_STATUS = True
|
||||
|
||||
VRF_MGMT_DEVNAME = policymanager.policymanager_api.get_module_globals(
|
||||
module_name="vrf",
|
||||
attr="vrf-mgmt-devname"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
cls._STATE_CHECK = True
|
||||
@@ -102,8 +105,10 @@ class ifaceScheduler():
|
||||
ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
|
||||
except Exception, e:
|
||||
if not ifupdownobj.ignore_error(str(e)):
|
||||
err = 1
|
||||
ifupdownobj.logger.error(str(e))
|
||||
err = 1
|
||||
#import traceback
|
||||
#traceback.print_exc()
|
||||
ifupdownobj.logger.error(str(e))
|
||||
# Continue with rest of the modules
|
||||
pass
|
||||
finally:
|
||||
@@ -148,7 +153,6 @@ class ifaceScheduler():
|
||||
# if interface exists in the system
|
||||
ifacename = ifaceobjs[0].name
|
||||
ifupdownobj.logger.info('%s: running ops ...' %ifacename)
|
||||
|
||||
if ('down' in ops[0] and
|
||||
ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
|
||||
ifaceobjs[0].addr_method != 'ppp' and
|
||||
@@ -265,6 +269,14 @@ class ifaceScheduler():
|
||||
# Run lowerifaces or dependents
|
||||
dlist = ifaceobj.lowerifaces
|
||||
if dlist:
|
||||
|
||||
if ifaceobj.link_kind == ifaceLinkKind.VRF:
|
||||
# remove non-auto lowerifaces from 'dlist'
|
||||
for lower_ifname in list(dlist):
|
||||
for lower_ifaceobj in ifupdownobj.get_ifaceobjs(lower_ifname) or []:
|
||||
if lower_ifaceobj and not lower_ifaceobj.auto and ifaceobj.name == cls.VRF_MGMT_DEVNAME:
|
||||
dlist.remove(lower_ifname)
|
||||
|
||||
ifupdownobj.logger.debug('%s: found dependents %s'
|
||||
%(ifacename, str(dlist)))
|
||||
try:
|
||||
|
@@ -63,7 +63,7 @@ class stateManager():
|
||||
|
||||
"""
|
||||
|
||||
__DEFAULT_STATE_DIR = "/run/network/"
|
||||
__DEFAULT_STATE_DIR = "/var/tmp/network/"
|
||||
|
||||
state_filename = 'ifstatenew'
|
||||
"""name of the satefile """
|
||||
@@ -84,6 +84,7 @@ class stateManager():
|
||||
self.ifaceobjdict = OrderedDict()
|
||||
self.logger = logging.getLogger('ifupdown.' +
|
||||
self.__class__.__name__)
|
||||
self.logger.info("stateManager init")
|
||||
|
||||
def init(self):
|
||||
self.state_dir = ifupdownConfig.config.get("state_dir")
|
||||
@@ -172,6 +173,11 @@ class stateManager():
|
||||
return
|
||||
if ifaceobj.status != ifaceStatus.SUCCESS:
|
||||
return
|
||||
# ifupdown2 prevents user from bringing the loopback interface
|
||||
# down - to avoid any issue (like wrong error messages) we
|
||||
# shouldn't remove lo ifaceobj from the statemanager
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
|
||||
return
|
||||
# If it matches any of the object, return
|
||||
oidx = 0
|
||||
for o in old_ifaceobjs:
|
||||
|
@@ -15,6 +15,7 @@ import signal
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from string import maketrans
|
||||
from functools import partial
|
||||
from ipaddr import IPNetwork, IPAddress
|
||||
|
||||
@@ -103,6 +104,7 @@ class utils():
|
||||
systemctl_cmd = '/bin/systemctl'
|
||||
dpkg_cmd = '/usr/bin/dpkg'
|
||||
|
||||
logger.info("utils init command paths")
|
||||
for cmd in ['bridge',
|
||||
'ip',
|
||||
'brctl',
|
||||
@@ -130,6 +132,17 @@ class utils():
|
||||
else:
|
||||
logger.debug('warning: path %s not found: %s won\'t be usable' % (path + cmd, cmd))
|
||||
|
||||
mac_translate_tab = maketrans(":.-,", " ")
|
||||
|
||||
@classmethod
|
||||
def mac_str_to_int(cls, hw_address):
|
||||
mac = 0
|
||||
if hw_address:
|
||||
for i in hw_address.translate(cls.mac_translate_tab).split():
|
||||
mac = mac << 8
|
||||
mac += int(i, 16)
|
||||
return mac
|
||||
|
||||
@staticmethod
|
||||
def get_onff_from_onezero(value):
|
||||
if value in utils._onoff_onezero:
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,6 @@
|
||||
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
#
|
||||
|
||||
import pprint
|
||||
|
||||
|
||||
class MSTPAttrsCache():
|
||||
bridges = {}
|
||||
@@ -24,91 +22,3 @@ class MSTPAttrsCache():
|
||||
@classmethod
|
||||
def invalidate(cls):
|
||||
MSTPAttrsCache.bridges = {}
|
||||
|
||||
|
||||
class linkCache():
|
||||
""" This class contains methods and instance variables to cache
|
||||
link info """
|
||||
|
||||
""" { <ifacename> : { 'ifindex': <index>,
|
||||
'mtu': <mtu>,
|
||||
'state' : <state>',
|
||||
'flags' : <flags>,
|
||||
'kind' : <kind: bridge, bond, vlan>,
|
||||
'linkinfo' : {<attr1> : <attrval1>,
|
||||
<attr2> : <attrval2>,
|
||||
<ports> : {
|
||||
} """
|
||||
links = {}
|
||||
vrfs = {}
|
||||
|
||||
@classmethod
|
||||
def get_attr(cls, mapList):
|
||||
return reduce(lambda d, k: d[k], mapList, linkCache.links)
|
||||
|
||||
@classmethod
|
||||
def set_attr(cls, mapList, value):
|
||||
cls.get_attr(mapList[:-1])[mapList[-1]] = value
|
||||
|
||||
@classmethod
|
||||
def del_attr(cls, mapList):
|
||||
try:
|
||||
del cls.get_attr(mapList[:-1])[mapList[-1]]
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def update_attrdict(cls, mapList, valuedict):
|
||||
try:
|
||||
cls.get_attr(mapList[:-1])[mapList[-1]].update(valuedict)
|
||||
except:
|
||||
cls.get_attr(mapList[:-1])[mapList[-1]] = valuedict
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def append_to_attrlist(cls, mapList, value):
|
||||
cls.get_attr(mapList[:-1])[mapList[-1]].append(value)
|
||||
|
||||
@classmethod
|
||||
def remove_from_attrlist(cls, mapList, value):
|
||||
try:
|
||||
cls.get_attr(mapList[:-1])[mapList[-1]].remove(value)
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def check_attr(cls, attrlist, value=None):
|
||||
try:
|
||||
cachedvalue = cls.get_attr(attrlist)
|
||||
if value:
|
||||
if cachedvalue == value:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif cachedvalue:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def invalidate(cls):
|
||||
cls.links = {}
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
cls.invalidate()
|
||||
cls.vrfs = {}
|
||||
|
||||
@classmethod
|
||||
def dump(cls):
|
||||
print 'Dumping link cache'
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
pp.pprint(cls.links)
|
||||
|
||||
@classmethod
|
||||
def dump_link(cls, linkname):
|
||||
print 'Dumping link %s' % linkname
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
pp.pprint(cls.links.get(linkname))
|
||||
|
@@ -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)
|
||||
|
@@ -60,6 +60,53 @@ class mstpctlutil(utilsBase):
|
||||
def __init__(self, *args, **kargs):
|
||||
utilsBase.__init__(self, *args, **kargs)
|
||||
|
||||
self.__batch = []
|
||||
self.__batch_mode = False
|
||||
|
||||
def __add_to_batch(self, cmd):
|
||||
self.__batch.append(cmd)
|
||||
|
||||
def __execute_or_batch(self, cmd):
|
||||
if self.__batch_mode:
|
||||
self.__add_to_batch(cmd)
|
||||
else:
|
||||
utils.exec_command("%s %s" % (utils.mstpctl_cmd, cmd))
|
||||
|
||||
def __execute_or_batch_dry_run(self, cmd):
|
||||
"""
|
||||
The batch function has it's own dryrun handler so we only handle
|
||||
dryrun for non-batch mode. Which will be removed once the "utils"
|
||||
module has it's own dryrun handlers
|
||||
"""
|
||||
if self.__batch_mode:
|
||||
self.__add_to_batch(cmd)
|
||||
else:
|
||||
self.logger.info("DRY-RUN: executing: %s %s" % (utils.mstpctl_cmd, cmd))
|
||||
|
||||
def batch_start(self):
|
||||
if not self.__batch_mode:
|
||||
self.__batch_mode = True
|
||||
self.__batch = []
|
||||
|
||||
def batch_commit(self):
|
||||
if not self.__batch_mode or not self.__batch:
|
||||
return
|
||||
try:
|
||||
utils.exec_command(
|
||||
"%s batch -" % utils.mstpctl_cmd,
|
||||
stdin="\n".join(self.__batch)
|
||||
)
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
self.__batch_mode = False
|
||||
del self.__batch
|
||||
self.__batch = None
|
||||
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
cls._cache_fill_done = False
|
||||
@@ -93,6 +140,7 @@ class mstpctlutil(utilsBase):
|
||||
except Exception as e:
|
||||
self.logger.info(str(e))
|
||||
return mstpctl_bridgeport_attrs_dict
|
||||
portname = bridgename # assigning portname to avoid an exception, in the exception handler
|
||||
try:
|
||||
mstpctl_bridge_cache = json.loads(output.strip('\n'))
|
||||
for portname in mstpctl_bridge_cache.keys():
|
||||
@@ -104,7 +152,7 @@ class mstpctlutil(utilsBase):
|
||||
mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal)
|
||||
MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict)
|
||||
except Exception as e:
|
||||
self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % str(e))
|
||||
self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % (portname, str(e)))
|
||||
|
||||
mstpctl_bridge_attrs_dict = {}
|
||||
try:
|
||||
@@ -127,7 +175,7 @@ class mstpctlutil(utilsBase):
|
||||
del mstpctl_bridge_attrs_dict['bridgeId']
|
||||
MSTPAttrsCache.bridges[bridgename].update(mstpctl_bridge_attrs_dict)
|
||||
except Exception as e:
|
||||
self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % str(e))
|
||||
self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % (bridgename, str(e)))
|
||||
return MSTPAttrsCache.get(bridgename)
|
||||
|
||||
def get_bridge_ports_attrs(self, bridgename):
|
||||
@@ -161,11 +209,9 @@ class mstpctlutil(utilsBase):
|
||||
if cache_value and cache_value == value:
|
||||
return
|
||||
if attrname == 'treeportcost' or attrname == 'treeportprio':
|
||||
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
|
||||
bridgename, portname, '0', value])
|
||||
self.__execute_or_batch("set%s %s %s 0 %s" % (attrname, bridgename, portname, value))
|
||||
else:
|
||||
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
|
||||
bridgename, portname, value])
|
||||
self.__execute_or_batch("set%s %s %s %s" % (attrname, bridgename, portname, value))
|
||||
if json_attr:
|
||||
self.update_bridge_port_cache(bridgename, portname, json_attr, value)
|
||||
|
||||
@@ -195,13 +241,12 @@ class mstpctlutil(utilsBase):
|
||||
self._bridge_jsonAttr_map[attrname])
|
||||
if attrvalue_curr and attrvalue_curr == attrvalue:
|
||||
return
|
||||
|
||||
if attrname == 'treeprio':
|
||||
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
|
||||
'%s' % bridgename, '0', '%s' % attrvalue], stderr=None)
|
||||
self.__execute_or_batch("set%s %s 0 %s" % (attrname, bridgename, attrvalue))
|
||||
self.update_bridge_cache(bridgename, attrname, str(attrvalue))
|
||||
else:
|
||||
utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
|
||||
'%s' % bridgename, '%s' % attrvalue], stderr=None)
|
||||
self.__execute_or_batch("set%s %s %s" % (attrname, bridgename, attrvalue))
|
||||
self.update_bridge_cache(bridgename,
|
||||
self._bridge_jsonAttr_map[attrname],
|
||||
str(attrvalue))
|
||||
@@ -223,9 +268,8 @@ class mstpctlutil(utilsBase):
|
||||
attrvalue_curr = self.get_bridge_treeprio(bridgename)
|
||||
if attrvalue_curr and attrvalue_curr == attrvalue:
|
||||
return
|
||||
utils.exec_commandl([utils.mstpctl_cmd,
|
||||
'settreeprio', bridgename, '0',
|
||||
str(attrvalue)])
|
||||
self.__execute_or_batch("settreeprio %s 0 %s" % (bridgename, str(attrvalue)))
|
||||
|
||||
self.update_bridge_cache(bridgename, 'treeprio', str(attrvalue))
|
||||
|
||||
def showbridge(self, bridgename=None):
|
||||
|
0
ifupdown2/lib/__init__.py
Normal file
0
ifupdown2/lib/__init__.py
Normal file
54
ifupdown2/lib/addon.py
Normal file
54
ifupdown2/lib/addon.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# addon -- Addon base class
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.io import IO
|
||||
from ifupdown2.lib.sysfs import Sysfs
|
||||
from ifupdown2.lib.iproute2 import IPRoute2
|
||||
from ifupdown2.lib.base_objects import Netlink, Cache, Requirements
|
||||
except ImportError:
|
||||
from lib.io import IO
|
||||
from lib.sysfs import Sysfs
|
||||
from lib.iproute2 import IPRoute2
|
||||
from lib.base_objects import Netlink, Cache, Requirements
|
||||
|
||||
|
||||
class Addon(Netlink, Cache):
|
||||
"""
|
||||
Base class for ifupdown2 addon modules
|
||||
Provides common infrastructure methods for all addon modules
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Netlink.__init__(self)
|
||||
Cache.__init__(self)
|
||||
|
||||
self.logger = logging.getLogger("ifupdown2.addons.%s" % self.__class__.__name__)
|
||||
|
||||
self.io = IO()
|
||||
self.sysfs = Sysfs
|
||||
self.iproute2 = IPRoute2()
|
||||
self.requirements = Requirements()
|
69
ifupdown2/lib/base_objects.py
Normal file
69
ifupdown2/lib/base_objects.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# base_objects -- Base classes used by higher level classes
|
||||
#
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.dry_run import DryRun
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
except ImportError:
|
||||
from lib.dry_run import DryRun
|
||||
from ifupdown.utils import utils
|
||||
|
||||
|
||||
class BaseObject(DryRun):
|
||||
"""
|
||||
BaseObject should be the parent of any ifupdown2 object that wishes to
|
||||
implement any "dry run" specific code and have a default logger.
|
||||
More classes can inherit BaseObject and add features like Addon, FileIO or Sysfs...
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
DryRun.__init__(self)
|
||||
self.logger = logging.getLogger("ifupdown2.%s" % self.__class__.__name__)
|
||||
|
||||
|
||||
def _import_NetlinkListenerWithCache():
|
||||
try:
|
||||
from ifupdown2.lib.nlcache import NetlinkListenerWithCache
|
||||
except ImportError:
|
||||
from lib.nlcache import NetlinkListenerWithCache
|
||||
return NetlinkListenerWithCache
|
||||
|
||||
|
||||
class Cache(BaseObject):
|
||||
def __init__(self):
|
||||
BaseObject.__init__(self)
|
||||
self.cache = _import_NetlinkListenerWithCache().get_instance().cache
|
||||
|
||||
|
||||
class Netlink(BaseObject):
|
||||
def __init__(self):
|
||||
BaseObject.__init__(self)
|
||||
self.netlink = _import_NetlinkListenerWithCache().get_instance()
|
||||
|
||||
|
||||
class Requirements(BaseObject):
|
||||
bridge_utils_is_installed = os.path.exists(utils.brctl_cmd)
|
250
ifupdown2/lib/dry_run.py
Normal file
250
ifupdown2/lib/dry_run.py
Normal file
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (C) 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# ifupdown2 -- dry_run module
|
||||
#
|
||||
#
|
||||
# __WeakMethodBound and __WeakMethodFree classes as well as the WeakMethod
|
||||
# function were inspired by the implementation of ActiveState recipes 81253
|
||||
# weakmethod.
|
||||
# This code solves an important issue here. You can't have weakrefs on bound
|
||||
# methods. Here is quote from the recipie:
|
||||
#
|
||||
# "Normal weakref.refs to bound methods don't quite work the way one expects,
|
||||
# because bound methods are first-class objects; weakrefs to bound methods are
|
||||
# dead-on-arrival unless some other strong reference to the same bound method
|
||||
# exists."
|
||||
#
|
||||
|
||||
import logging
|
||||
import inspect
|
||||
import weakref
|
||||
|
||||
|
||||
class __WeakMethodBound:
|
||||
""" ActiveState recipes 81253-weakmethod """
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f.im_func
|
||||
self.c = weakref.ref(f.im_self)
|
||||
|
||||
def __call__(self, *arg, **kwargs):
|
||||
if not self.c():
|
||||
raise TypeError("Method called on dead object")
|
||||
apply(self.f, (self.c(),) + arg, kwargs)
|
||||
|
||||
|
||||
class __WeakMethodFree:
|
||||
""" ActiveState recipes 81253-weakmethod """
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = weakref.ref(f)
|
||||
|
||||
def __call__(self, *arg, **kwargs):
|
||||
if not self.f():
|
||||
raise TypeError("Function no longer exist")
|
||||
apply(self.f(), arg, kwargs)
|
||||
|
||||
|
||||
def WeakMethod(f):
|
||||
""" ActiveState recipes 81253-weakmethod """
|
||||
try:
|
||||
f.im_func
|
||||
except AttributeError:
|
||||
return __WeakMethodFree(f)
|
||||
return __WeakMethodBound(f)
|
||||
|
||||
|
||||
def _weakref_call_back_delete(reference):
|
||||
try:
|
||||
DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(reference)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class DryRun(object):
|
||||
"""
|
||||
Detect dry_run functions and save the associated handler
|
||||
"""
|
||||
__DRY_RUN_PREFIX = "DRY-RUN"
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger("ifupdown2.%s" % self.__class__.__name__)
|
||||
|
||||
for attr_name in dir(self):
|
||||
try:
|
||||
# We need to iterate through the object attribute
|
||||
# to find dryrun methods
|
||||
if attr_name.lower().endswith("_dry_run"):
|
||||
attr_value = getattr(self, attr_name)
|
||||
|
||||
# When we find a dryrun attribute we need to make sure
|
||||
# it is a callable function or method.
|
||||
if not self.__is_method_or_function(attr_value):
|
||||
continue
|
||||
|
||||
base_attr_name = attr_name[:-8]
|
||||
base_attr_value = getattr(self, base_attr_name)
|
||||
# We try infere the base method/function name
|
||||
# then make sure its a function or method
|
||||
if not self.__is_method_or_function(base_attr_value):
|
||||
continue
|
||||
|
||||
# now we are pretty sure we have want we want:
|
||||
# - the base function
|
||||
# - the associated dry_run code
|
||||
# we will now register this couple in the DryRunManager
|
||||
DryRunManager.get_instance().register_dry_run_handler(
|
||||
weakref.ref(self, _weakref_call_back_delete),
|
||||
handler_name=base_attr_name,
|
||||
handler_code_weakref=WeakMethod(base_attr_value),
|
||||
dry_run_code_weakref=WeakMethod(attr_value)
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
def log_info_ifname_dry_run(self, ifname, string):
|
||||
self.logger.info("DRY-RUN: %s: %s" % (ifname, string))
|
||||
|
||||
def log_info_dry_run(self, string):
|
||||
self.logger.info("DRY-RUN: %s" % string)
|
||||
|
||||
@staticmethod
|
||||
def __is_method_or_function(obj):
|
||||
return callable(obj) and (inspect.ismethod(obj) or inspect.isfunction(obj))
|
||||
|
||||
|
||||
class _DryRunEntry(object):
|
||||
def __init__(self, target_module_weakref, handler_name, handler_code_weakref, dry_run_code_weakref):
|
||||
self.target_module_weakref = target_module_weakref
|
||||
self.handler_name = handler_name
|
||||
self.handler_code_weakref = handler_code_weakref
|
||||
self.dry_run_code_weakref = dry_run_code_weakref
|
||||
self.__status = False
|
||||
|
||||
def set(self):
|
||||
target_module_ref = self.target_module_weakref()
|
||||
|
||||
if target_module_ref:
|
||||
if self.dry_run_code_weakref:
|
||||
target_module_ref.__dict__[self.handler_name] = self.dry_run_code_weakref
|
||||
self.__status = True
|
||||
else:
|
||||
# if the reference is dead we need to unregister it
|
||||
DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(self.target_module_weakref)
|
||||
|
||||
def unset(self):
|
||||
target_module_ref = self.target_module_weakref()
|
||||
|
||||
if target_module_ref:
|
||||
if self.handler_code_weakref:
|
||||
target_module_ref.__dict__[self.handler_name] = self.handler_code_weakref
|
||||
self.__status = False
|
||||
else:
|
||||
# if the reference is dead we need to unregister it
|
||||
DryRunManager.get_instance().unregister_dry_run_handler_weakref_callback(self.target_module_weakref)
|
||||
|
||||
def get_status(self):
|
||||
return self.__status
|
||||
|
||||
|
||||
class DryRunManager(object):
|
||||
__instance = None
|
||||
|
||||
@staticmethod
|
||||
def get_instance():
|
||||
if not DryRunManager.__instance:
|
||||
DryRunManager.__instance = DryRunManager()
|
||||
return DryRunManager.__instance
|
||||
|
||||
def __init__(self):
|
||||
if DryRunManager.__instance:
|
||||
raise RuntimeError("DryRunManager: invalid access. Please use DryRunManager.getInstance()")
|
||||
else:
|
||||
DryRunManager.__instance = self
|
||||
|
||||
self.__entries = dict()
|
||||
self.__is_on = False
|
||||
|
||||
def register_dry_run_handler(self, module_weakref, handler_name, handler_code_weakref, dry_run_code_weakref):
|
||||
"""
|
||||
Register the dry run handler only using weakrefs - we don't want to mess up with garbage collection
|
||||
:param module_weakref:
|
||||
:param handler_name:
|
||||
:param handler_code_weakref:
|
||||
:param dry_run_code_weakref:
|
||||
:return:
|
||||
"""
|
||||
dry_run_entry = _DryRunEntry(
|
||||
target_module_weakref=module_weakref,
|
||||
handler_name=handler_name,
|
||||
handler_code_weakref=handler_code_weakref,
|
||||
dry_run_code_weakref=dry_run_code_weakref
|
||||
)
|
||||
if self.__is_on:
|
||||
dry_run_entry.set()
|
||||
|
||||
if module_weakref in self.__entries:
|
||||
self.__entries[module_weakref].append(dry_run_entry)
|
||||
else:
|
||||
self.__entries[module_weakref] = [dry_run_entry]
|
||||
|
||||
def unregister_dry_run_handler_weakref_callback(self, reference):
|
||||
"""
|
||||
If we detect a dead reference, we should remove this reference from our
|
||||
internal data structure
|
||||
"""
|
||||
try:
|
||||
del self.__entries[reference]
|
||||
except:
|
||||
pass
|
||||
|
||||
def dry_run_mode_on(self):
|
||||
"""
|
||||
Enable the dry run mode
|
||||
WARNING: not thread-safe
|
||||
"""
|
||||
for entries in self.__entries.itervalues():
|
||||
for entry in entries:
|
||||
entry.set()
|
||||
self.__is_on = True
|
||||
|
||||
def dry_run_mode_off(self):
|
||||
"""
|
||||
Disable the dry run mode
|
||||
WARNING: not thread-safe
|
||||
"""
|
||||
for entries in self.__entries.itervalues():
|
||||
for entry in entries:
|
||||
entry.unset()
|
||||
self.__is_on = False
|
||||
|
||||
def dump_entries_stdout(self):
|
||||
print "== DryRunManager dump =="
|
||||
print " MODULE: HANDLER STATUS"
|
||||
for entries in self.__entries.itervalues():
|
||||
for entry in entries:
|
||||
print " %s: %s() %s" % (repr(entry.target_module_weakref), entry.handler_name, "ON" if entry.get_status() else "OFF")
|
||||
print "========================"
|
||||
|
||||
def is_dry_mode_on(self):
|
||||
return self.__is_on
|
43
ifupdown2/lib/exceptions.py
Normal file
43
ifupdown2/lib/exceptions.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# ifupdown2 custom exceptions
|
||||
#
|
||||
|
||||
|
||||
class Ifupdown2Exception(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ExitWithStatus(Ifupdown2Exception):
|
||||
|
||||
def __init__(self, status):
|
||||
Ifupdown2Exception.__init__(self)
|
||||
self.status = status
|
||||
|
||||
def get_status(self):
|
||||
return self.status
|
||||
|
||||
|
||||
class ExitWithStatusAndError(ExitWithStatus):
|
||||
def __init__(self, status, message):
|
||||
ExitWithStatus.__init__(self, status)
|
||||
self.message = message
|
130
ifupdown2/lib/io.py
Normal file
130
ifupdown2/lib/io.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# io -- all io (file) handlers
|
||||
#
|
||||
|
||||
import json
|
||||
import struct
|
||||
import socket
|
||||
import select
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.base_objects import BaseObject
|
||||
except ImportError:
|
||||
from lib.base_objects import BaseObject
|
||||
|
||||
|
||||
class IO(BaseObject):
|
||||
def __init__(self):
|
||||
BaseObject.__init__(self)
|
||||
|
||||
def write_to_file(self, path, string):
|
||||
try:
|
||||
self.logger.info("writing \"%s\" to file %s" % (string, path))
|
||||
with open(path, "w") as f:
|
||||
f.write(string)
|
||||
return True
|
||||
except IOError, e:
|
||||
self.logger.warn("error while writing to file %s: %s" % (path, str(e)))
|
||||
return False
|
||||
|
||||
def write_to_file_dry_run(self, path, string):
|
||||
self.log_info_dry_run("writing \"%s\" to file %s" % (string, path))
|
||||
return True
|
||||
|
||||
def read_file_oneline(self, path):
|
||||
try:
|
||||
self.logger.info("reading '%s'" % path)
|
||||
with open(path, "r") as f:
|
||||
return f.readline().strip("\n")
|
||||
except:
|
||||
return None
|
||||
|
||||
def read_file_oneline_dry_run(self, path):
|
||||
self.log_info_dry_run("reading \"%s\"" % path)
|
||||
return None
|
||||
|
||||
def read_file(self, path):
|
||||
""" read file and return lines from the file """
|
||||
try:
|
||||
self.logger.info("reading '%s'" % path)
|
||||
with open(path, "r") as f:
|
||||
return f.readlines()
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
class SocketIO(object):
|
||||
"""
|
||||
Helper class to provide common TX/RX methods for socket
|
||||
communication to both client and daemon.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def tx_data(_socket, data):
|
||||
"""
|
||||
We don't send raw data over the socket, we pack it with the length
|
||||
(first 4 bytes) then with the data. That way the the transfer is more
|
||||
reliable
|
||||
"""
|
||||
ready = select.select([], [_socket], [])
|
||||
if ready and ready[1] and ready[1][0] == _socket:
|
||||
frmt = "=%ds" % len(data)
|
||||
packed_msg = struct.pack(frmt, data)
|
||||
packed_hdr = struct.pack("=I", len(packed_msg))
|
||||
_socket.sendall(packed_hdr + packed_msg)
|
||||
|
||||
@staticmethod
|
||||
def rx_json_packet(_socket):
|
||||
"""
|
||||
Reading data from socket. Unpacking the packets sent by "tx_data"
|
||||
first 4 bytes are the length of the following data. The data should
|
||||
be in json format
|
||||
"""
|
||||
ready = select.select([_socket], [], [])
|
||||
|
||||
if ready and ready[0] and ready[0][0] == _socket:
|
||||
|
||||
header_data = _socket.recv(4)
|
||||
|
||||
if not header_data:
|
||||
raise Exception("rx_json_packet: socket closed")
|
||||
if len(header_data) < 4:
|
||||
raise Exception("rx_json_packet: invalid data received")
|
||||
|
||||
data_len = struct.unpack("=I", header_data)[0]
|
||||
data = _socket.recv(data_len)
|
||||
|
||||
while len(data) < data_len:
|
||||
data = data + _socket.recv(data_len - len(data))
|
||||
|
||||
return json.loads(data)
|
||||
|
||||
return None
|
||||
|
||||
def get_socket_peer_cred(self, _socket):
|
||||
"""
|
||||
Returns tuple of (pid, uid, gid) of connected AF_UNIX stream socket
|
||||
:param _socket:
|
||||
:return:
|
||||
"""
|
||||
return struct.unpack("3i", _socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize("3i")))
|
743
ifupdown2/lib/iproute2.py
Normal file
743
ifupdown2/lib/iproute2.py
Normal file
@@ -0,0 +1,743 @@
|
||||
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# iproute2 -- contains all iproute2 related operation
|
||||
#
|
||||
|
||||
import re
|
||||
import shlex
|
||||
import signal
|
||||
import subprocess
|
||||
|
||||
from ipaddr import IPNetwork
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.sysfs import Sysfs
|
||||
from ifupdown2.lib.base_objects import Cache, Requirements
|
||||
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
from ifupdown2.ifupdown.iface import ifaceLinkPrivFlags
|
||||
from ifupdown2.nlmanager.nlpacket import Link
|
||||
except ImportError:
|
||||
from lib.sysfs import Sysfs
|
||||
from lib.base_objects import Cache, Requirements
|
||||
|
||||
from ifupdown.utils import utils
|
||||
from ifupdown.iface import ifaceLinkPrivFlags
|
||||
from nlmanager.nlpacket import Link
|
||||
|
||||
# WORK AROUND - Tunnel creation should be done via netlink and not iproute2 ####
|
||||
import struct #
|
||||
import socket #
|
||||
#
|
||||
try: #
|
||||
import ifupdown2.nlmanager.nlpacket as nlpacket #
|
||||
except: #
|
||||
import nlmanager.nlpacket as nlpacket #
|
||||
################################################################################
|
||||
|
||||
|
||||
class IPRoute2(Cache, Requirements):
|
||||
|
||||
VXLAN_UDP_PORT = 4789
|
||||
VXLAN_PEER_REGEX_PATTERN = re.compile("\s+dst\s+(\d+.\d+.\d+.\d+)\s+")
|
||||
|
||||
def __init__(self):
|
||||
Cache.__init__(self)
|
||||
Requirements.__init__(self)
|
||||
|
||||
self.sysfs = Sysfs
|
||||
|
||||
self.__batch = {}
|
||||
self.__batch_mode = False
|
||||
|
||||
# if bridge utils is not installed overrrides specific functions to
|
||||
# avoid constantly checking bridge_utils_is_installed
|
||||
if not Requirements.bridge_utils_is_installed:
|
||||
self.bridge_set_stp = lambda _, __: None
|
||||
self.bridge_del_mcqv4src = lambda _, __: None
|
||||
self.bridge_set_mcqv4src = lambda _, __, ___: None
|
||||
|
||||
############################################################################
|
||||
# WORK-AROUND
|
||||
############################################################################
|
||||
|
||||
def __update_cache_after_link_creation(self, ifname, kind):
|
||||
"""
|
||||
WORK AROUND - when creating tunnel via iproute2 we still need to fill
|
||||
our internal cache to keep track of this interface until we receive the
|
||||
NEWLINK notification. This code is a copy-paste from:
|
||||
nlcache.tx_nlpacket_get_response_with_error_and_cache_on_ack
|
||||
|
||||
:param ifname:
|
||||
:param kind:
|
||||
:return:
|
||||
"""
|
||||
packet = nlpacket.Link(nlpacket.RTM_NEWLINK, False, use_color=False)
|
||||
packet.flags = nlpacket.NLM_F_CREATE | nlpacket.NLM_F_REQUEST | nlpacket.NLM_F_ACK
|
||||
packet.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
|
||||
packet.add_attribute(nlpacket.Link.IFLA_IFNAME, ifname)
|
||||
packet.add_attribute(nlpacket.Link.IFLA_LINKINFO, {
|
||||
nlpacket.Link.IFLA_INFO_KIND: kind,
|
||||
nlpacket.Link.IFLA_INFO_DATA: {}
|
||||
})
|
||||
packet.build_message(0, 0)
|
||||
# When creating a new link via netlink, we don't always wait for the kernel
|
||||
# NEWLINK notification to be cached to continue. If our request is ACKed by
|
||||
# the OS we assume that the link was successfully created. Since we aren't
|
||||
# waiting for the kernel notification to continue we need to manually fill
|
||||
# our cache with the packet we just TX'ed. Once the NEWLINK notification
|
||||
# is received it will simply override the previous entry.
|
||||
# We need to keep track of those manually cached packets. We set a private
|
||||
# flag on the objects via the attribute priv_flags
|
||||
packet.priv_flags |= nlpacket.NLM_F_REQUEST
|
||||
try:
|
||||
# we need to decode the service header so all the attribute are properly
|
||||
# filled in the packet object that we are about to store in cache.
|
||||
# i.e.: packet.flags shouldn't contain NLM_F_* values but IFF_* (in case of Link object)
|
||||
# otherwise call to cache.link_is_up() will probably return True
|
||||
packet.decode_service_header()
|
||||
except:
|
||||
# we can ignore all errors
|
||||
pass
|
||||
|
||||
# Then we can use our normal "add_link" API call to cache the packet
|
||||
# and fill up our additional internal data structures.
|
||||
self.cache.add_link(packet)
|
||||
|
||||
############################################################################
|
||||
# BATCH
|
||||
############################################################################
|
||||
|
||||
def __add_to_batch(self, prefix, cmd):
|
||||
if prefix in self.__batch:
|
||||
self.__batch[prefix].append(cmd)
|
||||
else:
|
||||
self.__batch[prefix] = [cmd]
|
||||
|
||||
def __execute_or_batch(self, prefix, cmd):
|
||||
if self.__batch_mode:
|
||||
self.__add_to_batch(prefix, cmd)
|
||||
else:
|
||||
utils.exec_command("%s %s" % (prefix, cmd))
|
||||
|
||||
def __execute_or_batch_dry_run(self, prefix, cmd):
|
||||
"""
|
||||
The batch function has it's own dryrun handler so we only handle
|
||||
dryrun for non-batch mode. Which will be removed once the "utils"
|
||||
module has it's own dryrun handlers
|
||||
"""
|
||||
if self.__batch_mode:
|
||||
self.__add_to_batch(prefix, cmd)
|
||||
else:
|
||||
self.log_info_dry_run("executing: %s %s" % (prefix, cmd))
|
||||
|
||||
def batch_start(self):
|
||||
if not self.__batch_mode:
|
||||
self.__batch_mode = True
|
||||
self.__batch = {}
|
||||
|
||||
def batch_commit(self):
|
||||
try:
|
||||
if not self.__batch_mode or not self.__batch:
|
||||
return
|
||||
for prefix, commands in self.__batch.iteritems():
|
||||
utils.exec_command(
|
||||
"%s -force -batch -" % prefix,
|
||||
stdin="\n".join(commands)
|
||||
)
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
self.__batch_mode = False
|
||||
del self.__batch
|
||||
self.__batch = None
|
||||
|
||||
############################################################################
|
||||
# LINK
|
||||
############################################################################
|
||||
|
||||
def link_up(self, ifname):
|
||||
if not self.cache.link_is_up(ifname):
|
||||
self.link_up_force(ifname)
|
||||
|
||||
def link_down(self, ifname):
|
||||
if self.cache.link_is_up(ifname):
|
||||
self.link_down_force(ifname)
|
||||
|
||||
def link_up_dry_run(self, ifname):
|
||||
self.link_up_force(ifname)
|
||||
|
||||
def link_down_dry_run(self, ifname):
|
||||
self.link_down_force(ifname)
|
||||
|
||||
def link_up_force(self, ifname):
|
||||
self.__execute_or_batch(utils.ip_cmd, "link set dev %s up" % ifname)
|
||||
|
||||
def link_down_force(self, ifname):
|
||||
self.__execute_or_batch(utils.ip_cmd, "link set dev %s down" % ifname)
|
||||
|
||||
###
|
||||
|
||||
def link_set_master(self, ifname, master):
|
||||
if master != self.cache.get_master(ifname):
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"link set dev %s master %s" % (ifname, master)
|
||||
)
|
||||
|
||||
def link_set_master_dry_run(self, ifname, master):
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"link set dev %s master %s" % (ifname, master)
|
||||
)
|
||||
|
||||
###
|
||||
|
||||
def link_set_address(self, ifname, address):
|
||||
if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
|
||||
self.link_down(ifname)
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"link set dev %s address %s" % (ifname, address)
|
||||
)
|
||||
self.link_up(ifname)
|
||||
|
||||
def link_set_address_dry_run(self, ifname, address):
|
||||
self.link_down(ifname)
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"link set dev %s address %s" % (ifname, address)
|
||||
)
|
||||
self.link_up(ifname)
|
||||
|
||||
def link_set_address_and_keep_down(self, ifname, address, keep_down=False):
|
||||
if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
|
||||
self.link_down(ifname)
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"link set dev %s address %s" % (ifname, address)
|
||||
)
|
||||
if not keep_down:
|
||||
self.link_up(ifname)
|
||||
|
||||
def link_set_address_and_keep_down_dry_run(self, ifname, address, keep_down=False):
|
||||
self.link_down(ifname)
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"link set dev %s address %s" % (ifname, address)
|
||||
)
|
||||
if not keep_down:
|
||||
self.link_up(ifname)
|
||||
|
||||
###
|
||||
|
||||
def link_add_macvlan(self, ifname, macvlan_ifname, macvlan_mode):
|
||||
utils.exec_command(
|
||||
"%s link add link %s name %s type macvlan mode %s"
|
||||
% (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
|
||||
)
|
||||
|
||||
def link_add_macvlan_dry_run(self, ifname, macvlan_ifname, macvlan_mode):
|
||||
# this dryrun method can be removed once dryrun handlers
|
||||
# are added to the utils module
|
||||
self.log_info_ifname_dry_run(ifname, "executing %s link add link %s name %s type macvlan mode %s"
|
||||
% (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
|
||||
)
|
||||
|
||||
###
|
||||
|
||||
def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None,
|
||||
remoteips=None, learning='on', ageing=None, ttl=None, physdev=None):
|
||||
if svcnodeip and remoteips:
|
||||
raise Exception("svcnodeip and remoteip are mutually exclusive")
|
||||
|
||||
if self.cache.link_exists(name):
|
||||
cmd = [
|
||||
"link set dev %s type vxlan dstport %d"
|
||||
% (name, self.VXLAN_UDP_PORT)
|
||||
]
|
||||
else:
|
||||
cmd = [
|
||||
"link add dev %s type vxlan id %s dstport %d"
|
||||
% (name, vxlanid, self.VXLAN_UDP_PORT)
|
||||
]
|
||||
|
||||
if svcnodeip:
|
||||
if svcnodeip.is_multicast:
|
||||
cmd.append("group %s" % svcnodeip)
|
||||
else:
|
||||
cmd.append("remote %s" % svcnodeip)
|
||||
|
||||
if ageing:
|
||||
cmd.append("ageing %s" % ageing)
|
||||
|
||||
if learning == 'off':
|
||||
cmd.append("nolearning")
|
||||
|
||||
if ttl is not None:
|
||||
cmd.append("ttl %s" % ttl)
|
||||
|
||||
if physdev:
|
||||
cmd.append("dev %s" % physdev)
|
||||
|
||||
if localtunnelip:
|
||||
cmd.append("local %s" % localtunnelip)
|
||||
|
||||
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
|
||||
|
||||
def get_vxlan_peers(self, dev, svcnodeip):
|
||||
cmd = "%s fdb show brport %s" % (utils.bridge_cmd, dev)
|
||||
cur_peers = []
|
||||
try:
|
||||
ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
|
||||
utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
|
||||
output = subprocess.check_output(("grep", "00:00:00:00:00:00"), stdin=ps.stdout)
|
||||
ps.wait()
|
||||
utils.disable_subprocess_signal_forwarding(signal.SIGINT)
|
||||
try:
|
||||
for l in output.split('\n'):
|
||||
m = self.VXLAN_PEER_REGEX_PATTERN.search(l)
|
||||
if m and m.group(1) != svcnodeip:
|
||||
cur_peers.append(m.group(1))
|
||||
except:
|
||||
self.logger.warn('error parsing ip link output')
|
||||
except subprocess.CalledProcessError as e:
|
||||
if e.returncode != 1:
|
||||
self.logger.error(str(e))
|
||||
finally:
|
||||
utils.disable_subprocess_signal_forwarding(signal.SIGINT)
|
||||
|
||||
return cur_peers
|
||||
|
||||
###
|
||||
|
||||
@staticmethod
|
||||
def link_add_xfrm(ifname, xfrm_name, xfrm_id):
|
||||
utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id])
|
||||
|
||||
############################################################################
|
||||
# TUNNEL
|
||||
############################################################################
|
||||
|
||||
def tunnel_create(self, tunnelname, mode, attrs=None):
|
||||
if self.cache.link_exists(tunnelname):
|
||||
return
|
||||
|
||||
cmd = []
|
||||
if "6" in mode:
|
||||
cmd.append("-6")
|
||||
|
||||
if mode in ["gretap"]:
|
||||
cmd.append("link add %s type %s" % (tunnelname, mode))
|
||||
else:
|
||||
cmd.append("tunnel add %s mode %s" % (tunnelname, mode))
|
||||
|
||||
if attrs:
|
||||
for k, v in attrs.iteritems():
|
||||
cmd.append(k)
|
||||
if v:
|
||||
cmd.append(v)
|
||||
|
||||
utils.exec_command("%s %s" % (utils.ip_cmd, " ".join(cmd)))
|
||||
self.__update_cache_after_link_creation(tunnelname, mode)
|
||||
|
||||
def tunnel_change(self, tunnelname, attrs=None):
|
||||
""" tunnel change function """
|
||||
if not self.cache.link_exists(tunnelname):
|
||||
return
|
||||
cmd = ["tunnel change %s" % tunnelname]
|
||||
if attrs:
|
||||
for k, v in attrs.iteritems():
|
||||
cmd.append(k)
|
||||
if v:
|
||||
cmd.append(v)
|
||||
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
|
||||
|
||||
############################################################################
|
||||
# ADDRESS
|
||||
############################################################################
|
||||
|
||||
def addr_flush(self, ifname):
|
||||
if self.cache.link_has_ip(ifname):
|
||||
self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
|
||||
|
||||
def link_set_ipv6_addrgen_dry_run(self, ifname, addrgen, link_created):
|
||||
addrgen_str = "none" if addrgen else "eui64"
|
||||
self.link_down(ifname)
|
||||
self.__execute_or_batch(utils.ip_cmd, "link set dev %s addrgenmode %s" % (ifname, addrgen_str))
|
||||
self.link_up(ifname)
|
||||
|
||||
def link_set_ipv6_addrgen(self, ifname, addrgen, link_created):
|
||||
"""
|
||||
IFLA_INET6_ADDR_GEN_MODE values:
|
||||
0 = eui64
|
||||
1 = none
|
||||
|
||||
:param ifname:
|
||||
:param addrgen:
|
||||
:param link_created:
|
||||
:return:
|
||||
"""
|
||||
cached_ipv6_addr_gen_mode = self.cache.get_link_ipv6_addrgen_mode(ifname)
|
||||
|
||||
if cached_ipv6_addr_gen_mode == addrgen:
|
||||
return True
|
||||
|
||||
disabled_ipv6 = self.sysfs.get_ipv6_conf_disable_ipv6(ifname)
|
||||
|
||||
if disabled_ipv6:
|
||||
self.logger.info("%s: cannot set addrgen: ipv6 is disabled on this device" % ifname)
|
||||
return False
|
||||
|
||||
if link_created:
|
||||
link_mtu = self.sysfs.link_get_mtu(ifname)
|
||||
else:
|
||||
link_mtu = self.cache.get_link_mtu(ifname)
|
||||
|
||||
if link_mtu < 1280:
|
||||
self.logger.info("%s: ipv6 addrgen is disabled on device with MTU "
|
||||
"lower than 1280 (current mtu %s): cannot set addrgen %s"
|
||||
% (ifname, link_mtu, "off" if addrgen else "on"))
|
||||
return False
|
||||
|
||||
if not link_created:
|
||||
# When setting addrgenmode it is necessary to flap the macvlan
|
||||
# device. After flapping the device we also need to re-add all
|
||||
# the user configuration. The best way to add the user config
|
||||
# is to flush our internal address cache
|
||||
self.cache.address_flush_link(ifname)
|
||||
|
||||
is_link_up = self.cache.link_is_up(ifname)
|
||||
|
||||
if is_link_up:
|
||||
self.link_down_force(ifname)
|
||||
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"link set dev %s addrgenmode %s" % (ifname, Link.ifla_inet6_addr_gen_mode_dict.get(addrgen))
|
||||
)
|
||||
|
||||
if is_link_up:
|
||||
self.link_up_force(ifname)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def __compare_user_config_vs_running_state(running_addrs, user_addrs):
|
||||
ip4 = []
|
||||
ip6 = []
|
||||
|
||||
for ip in user_addrs or []:
|
||||
obj = IPNetwork(ip)
|
||||
|
||||
if obj.version == 6:
|
||||
ip6.append(str(obj))
|
||||
else:
|
||||
ip4.append(str(obj))
|
||||
|
||||
running_ipobj = []
|
||||
for ip in running_addrs or []:
|
||||
running_ipobj.append(str(ip))
|
||||
|
||||
return running_ipobj == (ip4 + ip6)
|
||||
|
||||
def add_addresses(self, ifacobj, ifname, address_list, purge_existing=False, metric=None, with_address_virtual=False):
|
||||
if purge_existing:
|
||||
running_address_list = self.cache.get_ifupdown2_addresses_list(
|
||||
[ifacobj],
|
||||
ifname,
|
||||
with_address_virtual=with_address_virtual
|
||||
)
|
||||
address_list = utils.get_normalized_ip_addr(ifname, address_list)
|
||||
|
||||
if self.__compare_user_config_vs_running_state(running_address_list, address_list):
|
||||
return
|
||||
|
||||
try:
|
||||
self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
|
||||
except Exception, e:
|
||||
self.logger.warning("%s: flushing all ip address failed: %s" % (ifname, str(e)))
|
||||
for addr in address_list:
|
||||
try:
|
||||
if metric:
|
||||
self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s metric %s" % (addr, ifname, metric))
|
||||
else:
|
||||
self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s" % (addr, ifname))
|
||||
except Exception as e:
|
||||
self.logger.error("%s: add_address: %s" % (ifname, str(e)))
|
||||
|
||||
############################################################################
|
||||
# BRIDGE
|
||||
############################################################################
|
||||
|
||||
@staticmethod
|
||||
def bridge_set_stp(bridge, stp_state):
|
||||
utils.exec_command("%s stp %s %s" % (utils.brctl_cmd, bridge, stp_state))
|
||||
|
||||
@staticmethod
|
||||
def bridge_fdb_show_dev(dev):
|
||||
try:
|
||||
fdbs = {}
|
||||
output = utils.exec_command("%s fdb show dev %s" % (utils.bridge_cmd, dev))
|
||||
if output:
|
||||
for fdb_entry in output.splitlines():
|
||||
try:
|
||||
entries = fdb_entry.split()
|
||||
fdbs.setdefault(entries[2], []).append(entries[0])
|
||||
except:
|
||||
pass
|
||||
return fdbs
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
|
||||
target = "self" if bridge else ""
|
||||
vlan_str = "vlan %s " % vlan if vlan else ""
|
||||
dst_str = "dst %s " % remote if remote else ""
|
||||
|
||||
utils.exec_command(
|
||||
"%s fdb replace %s dev %s %s %s %s"
|
||||
% (
|
||||
utils.bridge_cmd,
|
||||
address,
|
||||
dev,
|
||||
vlan_str,
|
||||
target,
|
||||
dst_str
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None):
|
||||
target = "self" if bridge else ""
|
||||
vlan_str = "vlan %s " % vlan if vlan else ""
|
||||
dst_str = "dst %s " % remote if remote else ""
|
||||
|
||||
utils.exec_command(
|
||||
"%s fdb append %s dev %s %s %s %s"
|
||||
% (
|
||||
utils.bridge_cmd,
|
||||
address,
|
||||
dev,
|
||||
vlan_str,
|
||||
target,
|
||||
dst_str
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None):
|
||||
target = "self" if bridge else ""
|
||||
vlan_str = "vlan %s " % vlan if vlan else ""
|
||||
dst_str = "dst %s " % remote if remote else ""
|
||||
|
||||
utils.exec_command(
|
||||
"%s fdb del %s dev %s %s %s %s"
|
||||
% (
|
||||
utils.bridge_cmd,
|
||||
address,
|
||||
dev,
|
||||
vlan_str,
|
||||
target,
|
||||
dst_str
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def bridge_vlan_del_vid_list(ifname, vids):
|
||||
if not vids:
|
||||
return
|
||||
for v in vids:
|
||||
utils.exec_command(
|
||||
"%s vlan del vid %s dev %s" % (utils.bridge_cmd, v, ifname)
|
||||
)
|
||||
|
||||
def bridge_vlan_del_vid_list_self(self, ifname, vids, is_bridge=True):
|
||||
target = "self" if is_bridge else ""
|
||||
for v in vids:
|
||||
self.__execute_or_batch(
|
||||
utils.bridge_cmd,
|
||||
"vlan del vid %s dev %s %s" % (v, ifname, target)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def bridge_vlan_add_vid_list(ifname, vids):
|
||||
for v in vids:
|
||||
utils.exec_command(
|
||||
"%s vlan add vid %s dev %s" % (utils.bridge_cmd, v, ifname)
|
||||
)
|
||||
|
||||
def bridge_vlan_add_vid_list_self(self, ifname, vids, is_bridge=True):
|
||||
target = "self" if is_bridge else ""
|
||||
for v in vids:
|
||||
self.__execute_or_batch(
|
||||
utils.bridge_cmd,
|
||||
"vlan add vid %s dev %s %s" % (v, ifname, target)
|
||||
)
|
||||
|
||||
def bridge_vlan_del_pvid(self, ifname, pvid):
|
||||
self.__execute_or_batch(
|
||||
utils.bridge_cmd,
|
||||
"vlan del vid %s untagged pvid dev %s" % (pvid, ifname)
|
||||
)
|
||||
|
||||
def bridge_vlan_add_pvid(self, ifname, pvid):
|
||||
self.__execute_or_batch(
|
||||
utils.bridge_cmd,
|
||||
"vlan add vid %s untagged pvid dev %s" % (pvid, ifname)
|
||||
)
|
||||
|
||||
def bridge_del_mcqv4src(self, bridge, vlan):
|
||||
try:
|
||||
vlan = int(vlan)
|
||||
except Exception as e:
|
||||
self.logger.info("%s: del mcqv4src vlan: invalid parameter %s: %s"
|
||||
% (bridge, vlan, str(e)))
|
||||
return
|
||||
utils.exec_command("%s delmcqv4src %s %d" % (utils.brctl_cmd, bridge, vlan))
|
||||
|
||||
def bridge_set_mcqv4src(self, bridge, vlan, mcquerier):
|
||||
try:
|
||||
vlan = int(vlan)
|
||||
except Exception as e:
|
||||
self.logger.info("%s: set mcqv4src vlan: invalid parameter %s: %s" % (bridge, vlan, str(e)))
|
||||
return
|
||||
if vlan == 0 or vlan > 4095:
|
||||
self.logger.warn("mcqv4src vlan '%d' invalid range" % vlan)
|
||||
return
|
||||
|
||||
ip = mcquerier.split(".")
|
||||
if len(ip) != 4:
|
||||
self.logger.warn("mcqv4src '%s' invalid IPv4 address" % mcquerier)
|
||||
return
|
||||
for k in ip:
|
||||
if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
|
||||
self.logger.warn("mcqv4src '%s' invalid IPv4 address" % mcquerier)
|
||||
return
|
||||
|
||||
utils.exec_command("%s setmcqv4src %s %d %s" % (utils.brctl_cmd, bridge, vlan, mcquerier))
|
||||
|
||||
############################################################################
|
||||
# ROUTE
|
||||
############################################################################
|
||||
|
||||
@staticmethod
|
||||
def route_add_gateway(ifname, gateway, vrf=None, metric=None, onlink=True):
|
||||
if not gateway:
|
||||
return
|
||||
|
||||
if not vrf:
|
||||
cmd = "%s route add default via %s proto kernel" % (utils.ip_cmd, gateway)
|
||||
else:
|
||||
cmd = "%s route add table %s default via %s proto kernel" % (utils.ip_cmd, vrf, gateway)
|
||||
|
||||
if metric:
|
||||
cmd += " metric %s" % metric
|
||||
|
||||
cmd += " dev %s" % ifname
|
||||
|
||||
if onlink:
|
||||
cmd += " onlink"
|
||||
|
||||
utils.exec_command(cmd)
|
||||
|
||||
@staticmethod
|
||||
def route_del_gateway(ifname, gateway, vrf=None, metric=None):
|
||||
"""
|
||||
delete default gw
|
||||
we don't need a DRYRUN handler here as utils.exec_command should have one
|
||||
"""
|
||||
if not gateway:
|
||||
return
|
||||
|
||||
if not vrf:
|
||||
cmd = "%s route del default via %s proto kernel" % (utils.ip_cmd, gateway)
|
||||
else:
|
||||
cmd = "%s route del table %s default via %s proto kernel" % (utils.ip_cmd, vrf, gateway)
|
||||
|
||||
if metric:
|
||||
cmd += " metric %s" % metric
|
||||
|
||||
cmd += " dev %s" % ifname
|
||||
utils.exec_command(cmd)
|
||||
|
||||
def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips):
|
||||
vrf_table = None
|
||||
|
||||
if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
|
||||
try:
|
||||
for upper_iface in ifaceobj.upperifaces:
|
||||
vrf_table = self.cache.get_vrf_table(upper_iface)
|
||||
if vrf_table:
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
ip_route_del = []
|
||||
for ip in ips:
|
||||
ip_network_obj = IPNetwork(ip)
|
||||
|
||||
if ip_network_obj.version == 6:
|
||||
route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
|
||||
|
||||
if vrf_table:
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"route del %s table %s dev %s" % (route_prefix, vrf_table, macvlan_ifacename)
|
||||
)
|
||||
else:
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"route del %s dev %s" % (route_prefix, macvlan_ifacename)
|
||||
)
|
||||
|
||||
ip_route_del.append((route_prefix, vrf_table))
|
||||
|
||||
for ip, vrf_table in ip_route_del:
|
||||
if vrf_table:
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"route add %s table %s dev %s proto kernel metric 9999" % (ip, vrf_table, macvlan_ifacename)
|
||||
)
|
||||
else:
|
||||
self.__execute_or_batch(
|
||||
utils.ip_cmd,
|
||||
"route add %s dev %s proto kernel metric 9999" % (ip, macvlan_ifacename)
|
||||
)
|
||||
|
||||
def ip_route_get_dev(self, prefix, vrf_master=None):
|
||||
try:
|
||||
if vrf_master:
|
||||
cmd = "%s route get %s vrf %s" % (utils.ip_cmd, prefix, vrf_master)
|
||||
else:
|
||||
cmd = "%s route get %s" % (utils.ip_cmd, prefix)
|
||||
|
||||
output = utils.exec_command(cmd)
|
||||
if output:
|
||||
rline = output.splitlines()[0]
|
||||
if rline:
|
||||
rattrs = rline.split()
|
||||
return rattrs[rattrs.index("dev") + 1]
|
||||
except Exception, e:
|
||||
self.logger.debug("ip_route_get_dev: failed .. %s" % str(e))
|
||||
return None
|
224
ifupdown2/lib/log.py
Normal file
224
ifupdown2/lib/log.py
Normal file
@@ -0,0 +1,224 @@
|
||||
# Copyright (C) 2016, 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
root_logger = logging.getLogger()
|
||||
|
||||
|
||||
class LogManager:
|
||||
LOGGER_NAME = "ifupdown2"
|
||||
LOGGER_NAME_DAEMON = "ifupdown2d"
|
||||
|
||||
DEFAULT_TCP_LOGGING_PORT = 42422
|
||||
DEFAULT_LOGGING_LEVEL_DAEMON = logging.INFO
|
||||
DEFAULT_LOGGING_LEVEL_NORMAL = logging.WARNING
|
||||
|
||||
__instance = None
|
||||
|
||||
@staticmethod
|
||||
def get_instance():
|
||||
if not LogManager.__instance:
|
||||
try:
|
||||
LogManager.__instance = LogManager()
|
||||
except Exception as e:
|
||||
sys.stderr.write("warning: ifupdown2.Log: %s\n" % str(e))
|
||||
traceback.print_exc()
|
||||
return LogManager.__instance
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Setup root logger and console handler (stderr). To enable daemon, client
|
||||
or standalone logging please call the proper function, see:
|
||||
"start_(daemon|client|standlone)_logging"
|
||||
"""
|
||||
if LogManager.__instance:
|
||||
raise RuntimeError("Log: invalid access. Please use Log.getInstance()")
|
||||
else:
|
||||
LogManager.__instance = self
|
||||
|
||||
self.__fmt = "%(levelname)s: %(message)s"
|
||||
|
||||
self.__debug_fmt = "%(asctime)s: %(threadName)s: %(name)s: " \
|
||||
"%(filename)s:%(lineno)d:%(funcName)s(): " \
|
||||
"%(levelname)s: %(message)s"
|
||||
|
||||
self.__root_logger = logging.getLogger()
|
||||
self.__root_logger.name = self.LOGGER_NAME
|
||||
|
||||
self.__socket_handler = None
|
||||
self.__syslog_handler = None
|
||||
self.__console_handler = None
|
||||
|
||||
self.daemon = None
|
||||
|
||||
# by default we attach a console handler that logs on stderr
|
||||
# the daemon can manually remove this handler on startup
|
||||
self.__console_handler = logging.StreamHandler(sys.stderr)
|
||||
self.__console_handler.setFormatter(logging.Formatter(self.__fmt))
|
||||
self.__root_logger.addHandler(self.__console_handler)
|
||||
|
||||
if os.path.exists("/dev/log"):
|
||||
try:
|
||||
self.__syslog_handler = logging.handlers.SysLogHandler(
|
||||
address="/dev/log",
|
||||
facility=logging.handlers.SysLogHandler.LOG_DAEMON
|
||||
)
|
||||
self.__syslog_handler.setFormatter(logging.Formatter(self.__fmt))
|
||||
except Exception as e:
|
||||
sys.stderr.write("warning: syslog: %s\n" % str(e))
|
||||
self.__syslog_handler = None
|
||||
|
||||
logging.addLevelName(logging.CRITICAL, "critical")
|
||||
logging.addLevelName(logging.WARNING, "warning")
|
||||
logging.addLevelName(logging.ERROR, "error")
|
||||
logging.addLevelName(logging.DEBUG, "debug")
|
||||
logging.addLevelName(logging.INFO, "info")
|
||||
|
||||
def set_level(self, default, error=False, warning=False, info=False, debug=False):
|
||||
"""
|
||||
Set root handler logging level
|
||||
:param default:
|
||||
:param error:
|
||||
:param warning:
|
||||
:param info:
|
||||
:param debug:
|
||||
"""
|
||||
if debug:
|
||||
log_level = logging.DEBUG
|
||||
elif info:
|
||||
log_level = logging.INFO
|
||||
elif warning:
|
||||
log_level = logging.WARNING
|
||||
elif error:
|
||||
log_level = logging.ERROR
|
||||
else:
|
||||
log_level = default
|
||||
|
||||
for handler in self.__root_logger.handlers:
|
||||
handler.setLevel(log_level)
|
||||
self.__root_logger.setLevel(log_level)
|
||||
|
||||
def enable_console(self):
|
||||
""" Add console handler to root logger """
|
||||
self.__root_logger.addHandler(self.__console_handler)
|
||||
|
||||
def disable_console(self):
|
||||
""" Remove console handler from root logger """
|
||||
self.__root_logger.removeHandler(self.__console_handler)
|
||||
|
||||
def enable_syslog(self):
|
||||
""" Add syslog handler to root logger """
|
||||
if self.__syslog_handler:
|
||||
self.__root_logger.addHandler(self.__syslog_handler)
|
||||
|
||||
def disable_syslog(self):
|
||||
""" Remove syslog handler from root logger """
|
||||
if self.__syslog_handler:
|
||||
self.__root_logger.removeHandler(self.__syslog_handler)
|
||||
|
||||
def close_log_stream(self):
|
||||
""" Close socket to disconnect client.
|
||||
We first have to perform this little hack: it seems like the socket is
|
||||
not opened until data (LogRecord) are transmitted. In our most basic use
|
||||
case (client sends "ifup -a") the daemon doesn't send back any LogRecord
|
||||
but we can't predict that in the client. The client is already in a
|
||||
blocking-select waiting for data on it's socket handler
|
||||
(StreamRequestHandler). For this special case we need to manually call
|
||||
"createSocket" to open the channel to the client so that we can properly
|
||||
close it. That way the client can exit cleanly.
|
||||
"""
|
||||
self.__root_logger.removeHandler(self.__socket_handler)
|
||||
self.__socket_handler.acquire()
|
||||
self.__socket_handler.retryTime = None
|
||||
try:
|
||||
if not self.__socket_handler.sock:
|
||||
self.__socket_handler.createSocket()
|
||||
finally:
|
||||
self.__socket_handler.close()
|
||||
self.__socket_handler.release()
|
||||
|
||||
def start_stream(self):
|
||||
self.__root_logger.addHandler(self.__socket_handler)
|
||||
|
||||
def set_daemon_logging_level(self, args):
|
||||
self.set_level(self.DEFAULT_LOGGING_LEVEL_DAEMON, info=args.verbose, debug=args.debug)
|
||||
|
||||
def set_request_logging_level(self, args):
|
||||
if not hasattr(args, "syslog") or not args.syslog:
|
||||
self.disable_syslog()
|
||||
else:
|
||||
self.__root_logger.removeHandler(self.__socket_handler)
|
||||
self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
|
||||
|
||||
def start_client_logging(self, args):
|
||||
""" Setup root logger name and client log level
|
||||
syslog is handled by the daemon directly
|
||||
"""
|
||||
self.__root_logger.name = self.LOGGER_NAME
|
||||
|
||||
if hasattr(args, "syslog") and args.syslog:
|
||||
self.enable_syslog()
|
||||
self.disable_console()
|
||||
|
||||
self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
|
||||
|
||||
def start_standalone_logging(self, args):
|
||||
self.__root_logger.name = self.LOGGER_NAME
|
||||
|
||||
if hasattr(args, "syslog") and args.syslog:
|
||||
self.enable_syslog()
|
||||
self.disable_console()
|
||||
|
||||
self.__root_logger.removeHandler(self.__console_handler)
|
||||
|
||||
self.set_level(self.DEFAULT_LOGGING_LEVEL_NORMAL, info=args.verbose, debug=args.debug)
|
||||
|
||||
def start_daemon_logging(self, args):
|
||||
"""
|
||||
Daemon mode initialize a socket handler to transmit logging to the
|
||||
client, we can also do syslog logging and/or console logging (probably
|
||||
just for debugging purpose)
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
self.__root_logger.name = self.LOGGER_NAME_DAEMON
|
||||
self.daemon = True
|
||||
|
||||
self.enable_syslog()
|
||||
|
||||
# Create SocketHandler for daemon-client communication
|
||||
self.__socket_handler = logging.handlers.SocketHandler(
|
||||
"localhost",
|
||||
port=self.DEFAULT_TCP_LOGGING_PORT
|
||||
)
|
||||
self.__root_logger.addHandler(self.__socket_handler)
|
||||
|
||||
if not args.console:
|
||||
self.disable_console()
|
||||
|
||||
self.set_daemon_logging_level(args)
|
3216
ifupdown2/lib/nlcache.py
Normal file
3216
ifupdown2/lib/nlcache.py
Normal file
File diff suppressed because it is too large
Load Diff
52
ifupdown2/lib/status.py
Normal file
52
ifupdown2/lib/status.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copyright (C) 2019 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
|
||||
|
||||
class Status(object):
|
||||
"""
|
||||
Defining client and daemon exit status to better identify
|
||||
client and daemon issue and exceptions.
|
||||
80 > unknown > 90 > client status > 100 > daemon status
|
||||
"""
|
||||
|
||||
class Client(object):
|
||||
STATUS_SUCCESS = 0
|
||||
STATUS_INIT = 91
|
||||
STATUS_COULD_NOT_CONNECT = 92
|
||||
STATUS_NO_PID = 93
|
||||
STATUS_EMPTY = 94
|
||||
STATUS_KEYBOARD_INTERRUPT = 95
|
||||
STATUS_NLERROR = 96
|
||||
STATUS_EXCEPTION_MAIN = 99
|
||||
|
||||
class Daemon(object):
|
||||
STATUS_SUCCESS = 0
|
||||
STATUS_INIT = 101
|
||||
STATUS_UNKNOWN = 102
|
||||
STATUS_SOCKET_ERROR = 103
|
||||
STATUS_PROCESS_REQUEST = 104
|
||||
STATUS_KEYBOARD_INTERRUPT = 105
|
||||
STATUS_NLERROR = 106
|
||||
|
||||
STATUS_REQUEST_PARSE_ERROR = 106
|
||||
STATUS_REQUEST_EXCEPTION = 107
|
||||
STATUS_REQUEST_BASE_EXCEPTION = 108
|
248
ifupdown2/lib/sysfs.py
Normal file
248
ifupdown2/lib/sysfs.py
Normal file
@@ -0,0 +1,248 @@
|
||||
# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
||||
#
|
||||
# Author:
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# sysfs -- contains all sysfs related operation
|
||||
#
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
try:
|
||||
from ifupdown2.lib.io import IO
|
||||
from ifupdown2.lib.base_objects import Requirements
|
||||
|
||||
from ifupdown2.ifupdown.utils import utils
|
||||
|
||||
from ifupdown2.nlmanager.nlpacket import Link
|
||||
except ImportError:
|
||||
from lib.io import IO
|
||||
from lib.base_objects import Requirements
|
||||
|
||||
from ifupdown.utils import utils
|
||||
|
||||
from nlmanager.nlpacket import Link
|
||||
|
||||
|
||||
class __Sysfs(IO, Requirements):
|
||||
|
||||
__bond_netlink_to_sysfs_attr_map = {
|
||||
Link.IFLA_BOND_MODE: "mode",
|
||||
Link.IFLA_BOND_MIIMON: "miimon",
|
||||
Link.IFLA_BOND_USE_CARRIER: "use_carrier",
|
||||
Link.IFLA_BOND_AD_LACP_RATE: "lacp_rate",
|
||||
Link.IFLA_BOND_XMIT_HASH_POLICY: "xmit_hash_policy",
|
||||
Link.IFLA_BOND_MIN_LINKS: "min_links",
|
||||
Link.IFLA_BOND_NUM_PEER_NOTIF: "num_grat_arp",
|
||||
Link.IFLA_BOND_AD_ACTOR_SYSTEM: "ad_actor_system",
|
||||
Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: "ad_actor_sys_prio",
|
||||
Link.IFLA_BOND_AD_LACP_BYPASS: "lacp_bypass",
|
||||
Link.IFLA_BOND_UPDELAY: "updelay",
|
||||
Link.IFLA_BOND_DOWNDELAY: "downdelay",
|
||||
Link.IFLA_BOND_PRIMARY: "primary",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
IO.__init__(self)
|
||||
Requirements.__init__(self)
|
||||
|
||||
# Temporary work around to solve the circular dependency with nlcache.
|
||||
# Once nlcache is created it will populate sysfs.cache
|
||||
self.cache = None
|
||||
|
||||
# if bridge utils is not installed overrrides specific functions to
|
||||
# avoid constantly checking bridge_utils_is_installed
|
||||
if not Requirements.bridge_utils_is_installed:
|
||||
self.bridge_get_mcqv4src = self.bridge_get_mcqv4src_dry_run
|
||||
|
||||
@staticmethod
|
||||
def link_get_uppers(ifname):
|
||||
try:
|
||||
uppers = glob.glob("/sys/class/net/%s/upper_*" % ifname)
|
||||
if not uppers:
|
||||
return []
|
||||
return [os.path.basename(u)[6:] for u in uppers]
|
||||
except:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def link_get_lowers(ifname):
|
||||
try:
|
||||
lowers = glob.glob("/sys/class/net/%s/lower_*" % ifname)
|
||||
if not lowers:
|
||||
return []
|
||||
return [os.path.basename(l)[6:] for l in lowers]
|
||||
except:
|
||||
return []
|
||||
|
||||
def link_is_up(self, ifname):
|
||||
"""
|
||||
Read sysfs operstate file
|
||||
"""
|
||||
return "up" == self.read_file_oneline("/sys/class/net/%s/operstate" % ifname)
|
||||
|
||||
def get_link_address(self, ifname):
|
||||
"""
|
||||
Read MAC hardware address from sysfs
|
||||
"""
|
||||
return self.read_file_oneline("/sys/class/net/%s/address" % ifname)
|
||||
|
||||
#
|
||||
# MTU
|
||||
#
|
||||
|
||||
def link_get_mtu(self, ifname):
|
||||
return int(self.read_file_oneline("/sys/class/net/%s/mtu" % ifname) or 0)
|
||||
|
||||
def link_set_mtu(self, ifname, mtu_str, mtu_int):
|
||||
if self.cache.get_link_mtu(ifname) != mtu_int:
|
||||
if self.write_to_file('/sys/class/net/%s/mtu' % ifname, mtu_str):
|
||||
self.cache.override_link_mtu(ifname, mtu_int)
|
||||
|
||||
def link_set_mtu_dry_run(self, ifname, mtu_str, mtu_int):
|
||||
# we can remove the cache check in DRYRUN mode
|
||||
self.write_to_file('/sys/class/net/%s/mtu' % ifname, mtu_str)
|
||||
|
||||
#
|
||||
# ALIAS
|
||||
#
|
||||
|
||||
def link_set_alias(self, ifname, alias):
|
||||
cached_alias = self.cache.get_link_alias(ifname)
|
||||
|
||||
if cached_alias == alias:
|
||||
return
|
||||
|
||||
if not alias:
|
||||
alias = "\n"
|
||||
|
||||
if self.write_to_file("/sys/class/net/%s/ifalias" % ifname, alias):
|
||||
pass # self.cache.override_link_mtu(ifname, mtu_int)
|
||||
|
||||
def link_set_alias_dry_run(self, ifname, alias):
|
||||
# we can remove the cache check in DRYRUN mode
|
||||
if not alias:
|
||||
alias = ""
|
||||
self.write_to_file("/sys/class/net/%s/ifalias" % ifname, alias)
|
||||
|
||||
############################################################################
|
||||
# BRIDGE
|
||||
############################################################################
|
||||
|
||||
def bridge_port_pvids_get(self, bridge_port_name):
|
||||
return self.read_file_oneline("/sys/class/net/%s/brport/pvid" % bridge_port_name)
|
||||
|
||||
def bridge_get_stp(self, bridge):
|
||||
stp_state_path = "/sys/class/net/%s/bridge/stp_state" % bridge
|
||||
|
||||
if not os.path.exists(stp_state_path):
|
||||
return "error"
|
||||
|
||||
stp_state = self.read_file_oneline(stp_state_path)
|
||||
|
||||
if not stp_state:
|
||||
return "error"
|
||||
|
||||
try:
|
||||
stp_state_int = int(stp_state)
|
||||
return "yes" if stp_state_int > 0 else "no"
|
||||
except:
|
||||
return "unknown"
|
||||
|
||||
def bridge_get_mcqv4src(self, bridge):
|
||||
mcqv4src = {}
|
||||
try:
|
||||
filename = "/sys/class/net/%s/bridge/multicast_v4_queriers" % bridge
|
||||
if os.path.exists(filename):
|
||||
for line in self.read_file(filename) or []:
|
||||
vlan_id, ip = line.split('=')
|
||||
mcqv4src[vlan_id] = ip.strip()
|
||||
return mcqv4src
|
||||
except Exception as e:
|
||||
self.logger.info("%s showmcqv4src: skipping unsupported command" % utils.brctl_cmd)
|
||||
self.bridge_get_mcqv4src = self.bridge_get_mcqv4src_dry_run
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def bridge_get_mcqv4src_dry_run(bridge):
|
||||
return {}
|
||||
|
||||
############################################################################
|
||||
# BOND
|
||||
############################################################################
|
||||
|
||||
def bond_remove_slave(self, bond_name, slave_name):
|
||||
if self.cache.is_link_enslaved_to(slave_name, bond_name):
|
||||
if self.write_to_file("/sys/class/net/%s/bonding/slaves" % bond_name, "-%s" % slave_name):
|
||||
# success we can manually update our cache to make sure we stay up-to-date
|
||||
self.cache.override_cache_unslave_link(slave=slave_name, master=bond_name)
|
||||
|
||||
def bond_remove_slave_dry_run(self, bond_name, slave_name):
|
||||
self.write_to_file("/sys/class/net/%s/bonding/slaves" % bond_name, "-%s" % slave_name)
|
||||
|
||||
###
|
||||
|
||||
def bond_create(self, bond_name):
|
||||
if self.cache.bond_exists(bond_name):
|
||||
return
|
||||
self.write_to_file("/sys/class/net/bonding_masters", "+%s" % bond_name)
|
||||
|
||||
def bond_create_dry_run(self, bond_name):
|
||||
self.write_to_file("/sys/class/net/bonding_masters", "+%s" % bond_name)
|
||||
|
||||
###
|
||||
|
||||
def bond_set_attrs_nl(self, bond_name, ifla_info_data):
|
||||
"""
|
||||
bond_set_attrs_nl doesn't need a _dry_run handler because each
|
||||
entry in ifla_info_data was checked against the cache already.
|
||||
Here write_to_file already has a dry_run handler.
|
||||
:param bond_name:
|
||||
:param ifla_info_data:
|
||||
:return:
|
||||
"""
|
||||
bond_attr_name = 'None' # for log purpose (in case an exception raised)
|
||||
|
||||
for nl_attr, value in ifla_info_data.items():
|
||||
try:
|
||||
bond_attr_name = self.__bond_netlink_to_sysfs_attr_map.get(nl_attr)
|
||||
|
||||
if bond_attr_name is None:
|
||||
self.logger.warning(
|
||||
"%s: sysfs configuration: unknown bond attribute %s (value %s)"
|
||||
% (bond_name, nl_attr, value)
|
||||
)
|
||||
continue
|
||||
|
||||
file_path = "/sys/class/net/%s/bonding/%s" % (bond_name, bond_attr_name)
|
||||
if os.path.exists(file_path):
|
||||
self.write_to_file(file_path, str(value))
|
||||
except Exception as e:
|
||||
self.logger.warning("%s: %s %s: %s" % (bond_name, bond_attr_name, value, str(e)))
|
||||
|
||||
############################################################################
|
||||
# /proc/sys/ipv6/conf
|
||||
############################################################################
|
||||
|
||||
def get_ipv6_conf_disable_ipv6(self, ifname):
|
||||
return int(self.read_file_oneline("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname) or 0)
|
||||
|
||||
|
||||
Sysfs = __Sysfs()
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
|
||||
# Copyright (C) 2015-2020 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
@@ -32,15 +32,21 @@ from struct import pack, unpack, calcsize
|
||||
from threading import Thread, Event, Lock
|
||||
from Queue import Queue
|
||||
import logging
|
||||
import signal
|
||||
import socket
|
||||
import errno
|
||||
import os
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NetlinkListener(Thread):
|
||||
# As defined in asm/socket.h
|
||||
_SO_ATTACH_FILTER = 26
|
||||
|
||||
def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvbuf_sz=10000000):
|
||||
RECV_BUFFER = 4096 # 1024 * 1024
|
||||
|
||||
def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvbuf_sz=10000000, bpf_filter=None):
|
||||
"""
|
||||
groups controls what types of messages we are interested in hearing
|
||||
To get everything pass:
|
||||
@@ -50,12 +56,15 @@ class NetlinkListener(Thread):
|
||||
RTMGRP_IPV6_IFADDR | \
|
||||
RTMGRP_IPV6_ROUTE
|
||||
"""
|
||||
Thread.__init__(self)
|
||||
Thread.__init__(self, name='NetlinkListener')
|
||||
self.manager = manager
|
||||
self.shutdown_event = Event()
|
||||
self.groups = groups
|
||||
self.pid_offset = pid_offset
|
||||
self.rcvbuf_sz = rcvbuf_sz
|
||||
self.bpf_filter = bpf_filter
|
||||
self.rx_socket = None
|
||||
self.rx_socket_prev_seq = {}
|
||||
|
||||
# if the app has requested for error notification socket errors will
|
||||
# be sent via the SERVICE_ERROR event
|
||||
@@ -63,7 +72,8 @@ class NetlinkListener(Thread):
|
||||
|
||||
self.supported_messages = [RTM_NEWLINK, RTM_DELLINK, RTM_NEWADDR,
|
||||
RTM_DELADDR, RTM_NEWNEIGH, RTM_DELNEIGH,
|
||||
RTM_NEWROUTE, RTM_DELROUTE]
|
||||
RTM_NEWROUTE, RTM_DELROUTE,
|
||||
RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB]
|
||||
self.ignore_messages = [RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH,
|
||||
RTM_GETROUTE, RTM_GETQDISC, NLMSG_ERROR, NLMSG_DONE]
|
||||
|
||||
@@ -86,34 +96,92 @@ class NetlinkListener(Thread):
|
||||
if msgtype not in self.ignore_messages:
|
||||
self.ignore_messages.append(msgtype)
|
||||
|
||||
def __bind_rx_socket(self, pid):
|
||||
"""
|
||||
bind rx_socket and retry mechanism in case of failure and collision
|
||||
i.e.: [Errno 98] Address already in use
|
||||
|
||||
We will retry NLMANAGER_BIND_RETRY times (defaults to 4242)
|
||||
|
||||
:param pid:
|
||||
:return:
|
||||
"""
|
||||
pid_offset = self.pid_offset
|
||||
for i in xrange(0, int(os.getenv("NLMANAGER_BIND_RETRY", 4242))):
|
||||
try:
|
||||
pid_offset += i
|
||||
self.rx_socket.bind((pid | (pid_offset << 22), self.groups))
|
||||
self.pid_offset = pid_offset
|
||||
return
|
||||
except:
|
||||
pass
|
||||
# if we reach this line it means we've reach NLMANAGER_BIND_RETRY limit
|
||||
# and couldn't successfully bind the rx_socket... We will try one more
|
||||
# time but without catching the related exception.
|
||||
self.rx_socket.bind((pid | (self.pid_offset << 22), self.groups))
|
||||
|
||||
def run(self):
|
||||
manager = self.manager
|
||||
header_PACK = 'IHHII'
|
||||
header_LEN = calcsize(header_PACK)
|
||||
try:
|
||||
header_PACK = 'IHHII'
|
||||
header_LEN = calcsize(header_PACK)
|
||||
|
||||
# The RX socket is used to listen to all netlink messages that fly by
|
||||
# as things change in the kernel. We need a very large SO_RCVBUF here
|
||||
# else we tend to miss messages.
|
||||
# PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to
|
||||
# use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
|
||||
# with the netlink manager which always attempts to bind with the pid.
|
||||
self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
|
||||
_SO_RCVBUFFORCE = socket.SO_RCVBUFFORCE if hasattr(socket, 'SO_RCVBUFFORCE') else 33
|
||||
self.rx_socket.setsockopt(socket.SOL_SOCKET, _SO_RCVBUFFORCE, self.rcvbuf_sz)
|
||||
self.rx_socket.bind((manager.pid | (self.pid_offset << 22), self.groups))
|
||||
self.rx_socket_prev_seq = {}
|
||||
# The RX socket is used to listen to all netlink messages that fly by
|
||||
# as things change in the kernel. We need a very large SO_RCVBUF here
|
||||
# else we tend to miss messages.
|
||||
# PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to
|
||||
# use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
|
||||
# with the netlink manager which always attempts to bind with the pid.
|
||||
self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
|
||||
try:
|
||||
self.rx_socket.setsockopt(
|
||||
socket.SOL_SOCKET,
|
||||
socket.SO_RCVBUFFORCE if hasattr(socket, 'SO_RCVBUFFORCE') else 33,
|
||||
self.rcvbuf_sz
|
||||
)
|
||||
if self.bpf_filter is not None:
|
||||
self.rx_socket.setsockopt(
|
||||
socket.SOL_SOCKET,
|
||||
NetlinkListener._SO_ATTACH_FILTER,
|
||||
self.bpf_filter
|
||||
)
|
||||
except Exception as e:
|
||||
log.debug("nllistener: rx socket: setsockopt: %s" % str(e))
|
||||
|
||||
manager.target_lock.acquire()
|
||||
if not manager.tx_socket:
|
||||
manager.tx_socket_allocate()
|
||||
manager.target_lock.release()
|
||||
self.__bind_rx_socket(manager.pid)
|
||||
|
||||
my_sockets = (manager.tx_socket, self.rx_socket)
|
||||
with manager.target_lock:
|
||||
if not manager.tx_socket:
|
||||
manager.tx_socket_allocate()
|
||||
|
||||
socket_string = {
|
||||
manager.tx_socket: "TX",
|
||||
self.rx_socket: "RX"
|
||||
}
|
||||
my_sockets = (manager.tx_socket, self.rx_socket)
|
||||
|
||||
socket_string = {
|
||||
manager.tx_socket: "TX",
|
||||
self.rx_socket: "RX"
|
||||
}
|
||||
except Exception as e:
|
||||
if self.rx_socket:
|
||||
self.rx_socket.close()
|
||||
self.rx_socket = None
|
||||
|
||||
# before notifying the main thread we need to set
|
||||
# manager.listener_ready properly to signal the failure
|
||||
manager.listener_ready = False
|
||||
manager.listener_event_ready.set()
|
||||
|
||||
if logging.root.level == logging.DEBUG:
|
||||
# in debug mode we raise the exception so it can be displayed
|
||||
# in the terminal: "Exception in thread NetlinkListener..."
|
||||
raise
|
||||
else:
|
||||
log.error("netlink: listener thread: rx socket: %s" % str(e))
|
||||
return
|
||||
|
||||
# Notify main thread that the NetlinkListener thread
|
||||
# has started and is ready to start processing data
|
||||
manager.listener_ready = True
|
||||
manager.listener_event_ready.set()
|
||||
|
||||
while True:
|
||||
|
||||
@@ -123,7 +191,8 @@ class NetlinkListener(Thread):
|
||||
|
||||
# Only block for 1 second so we can wake up to see if shutdown_event is set
|
||||
try:
|
||||
(readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 1)
|
||||
(readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 0.1)
|
||||
# when ifupdown2 is not running we could change the timeout to 1 sec or more
|
||||
except Exception as e:
|
||||
log.error('select() error: ' + str(e))
|
||||
continue
|
||||
@@ -139,7 +208,7 @@ class NetlinkListener(Thread):
|
||||
data = []
|
||||
|
||||
try:
|
||||
data = s.recv(4096)
|
||||
data = s.recv(self.RECV_BUFFER)
|
||||
except socket.error, e:
|
||||
log.error('recv() error: ' + str(e))
|
||||
data = []
|
||||
@@ -155,8 +224,15 @@ class NetlinkListener(Thread):
|
||||
# Extract the length, etc from the header
|
||||
(length, msgtype, flags, seq, pid) = unpack(header_PACK, data[:header_LEN])
|
||||
|
||||
msgtype_str = NetlinkPacket.type_to_string.get(msgtype)
|
||||
|
||||
if not msgtype_str:
|
||||
data = data[length:]
|
||||
log.debug('%s %s: RXed unknown/unsupported msg type %s skipping netlink message...' % (self, socket_string[s], msgtype))
|
||||
continue
|
||||
|
||||
log.debug('%s %s: RXed %s seq %d, pid %d, %d bytes (%d total)' %
|
||||
(self, socket_string[s], NetlinkPacket.type_to_string[msgtype],
|
||||
(self, socket_string[s], msgtype_str,
|
||||
seq, pid, length, total_length))
|
||||
possible_ack = False
|
||||
|
||||
@@ -172,7 +248,13 @@ class NetlinkListener(Thread):
|
||||
msg.decode_packet(length, flags, seq, pid, data)
|
||||
|
||||
if error_code:
|
||||
log.debug("%s %s: RXed NLMSG_ERROR code %s (%d)" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code))
|
||||
log.debug("%s %s: RXed NLMSG_ERROR code %s (%d): %s" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code, msg.error_to_human_readable_string.get(error_code)))
|
||||
else:
|
||||
log.debug("%s %s: RXed NLMSG_ERROR code %s (%d): %s... this is an ACK" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code, msg.error_to_human_readable_string.get(error_code)))
|
||||
|
||||
if manager.errorq_enabled:
|
||||
with manager.errorq_lock:
|
||||
manager.errorq.append(msg)
|
||||
|
||||
if possible_ack and seq == manager.target_seq and pid == manager.target_pid:
|
||||
log.debug("%s %s: Setting RXed ACK alarm for seq %d, pid %d" %
|
||||
@@ -213,17 +295,16 @@ class NetlinkListener(Thread):
|
||||
data = data[length:]
|
||||
|
||||
if set_tx_socket_rxed_ack_alarm:
|
||||
manager.target_lock.acquire()
|
||||
manager.target_seq = None
|
||||
manager.target_pid = None
|
||||
manager.target_lock.release()
|
||||
with manager.target_lock:
|
||||
manager.target_seq = None
|
||||
manager.target_pid = None
|
||||
manager.tx_socket_rxed_ack.set()
|
||||
|
||||
if set_alarm:
|
||||
manager.workq.put(('SERVICE_NETLINK_QUEUE', None))
|
||||
manager.workq.put((manager.WORKQ_SERVICE_NETLINK_QUEUE, None))
|
||||
|
||||
if set_overrun:
|
||||
manager.workq.put(('SERVICE_ERROR', "OVERFLOW"))
|
||||
manager.workq.put((manager.WORKQ_SERVICE_ERROR, "OVERFLOW"))
|
||||
|
||||
if set_alarm or set_overrun:
|
||||
manager.alarm.set()
|
||||
@@ -233,7 +314,10 @@ class NetlinkListener(Thread):
|
||||
|
||||
class NetlinkManagerWithListener(NetlinkManager):
|
||||
|
||||
def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, error_notification=False, rcvbuf_sz=10000000):
|
||||
WORKQ_SERVICE_NETLINK_QUEUE = 1
|
||||
WORKQ_SERVICE_ERROR = 2
|
||||
|
||||
def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, error_notification=False, rcvbuf_sz=10000000, bpf_filter=None):
|
||||
NetlinkManager.__init__(self, use_color=use_color, pid_offset=pid_offset)
|
||||
self.groups = groups
|
||||
self.workq = Queue()
|
||||
@@ -255,6 +339,14 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
self.rcvbuf_sz = rcvbuf_sz
|
||||
self.error_notification = error_notification
|
||||
self.pid_offset = pid_offset
|
||||
self.bpf_filter = bpf_filter
|
||||
|
||||
self.errorq = None
|
||||
self.errorq_lock = None
|
||||
self.errorq_enabled = False
|
||||
|
||||
self.listener_event_ready = None
|
||||
self.listener_ready = None
|
||||
|
||||
# Listen to netlink messages
|
||||
if start_listener:
|
||||
@@ -266,11 +358,26 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
return 'NetlinkManagerWithListener'
|
||||
|
||||
def restart_listener(self):
|
||||
self.listener = NetlinkListener(self, self.groups, self.pid_offset+1, self.error_notification, self.rcvbuf_sz)
|
||||
"""
|
||||
(re)Start Netlink listener thread and make sure to wait until
|
||||
the newly created thread is ready.
|
||||
:return:
|
||||
"""
|
||||
self.listener_event_ready = Event()
|
||||
self.listener_ready = False
|
||||
|
||||
self.listener = NetlinkListener(self, self.groups, self.pid_offset + 1, self.error_notification, self.rcvbuf_sz, self.bpf_filter)
|
||||
self.listener.start()
|
||||
|
||||
def signal_term_handler(self, signal, frame):
|
||||
log.info("NetlinkManagerWithListener: Caught SIGTERM")
|
||||
self.listener_event_ready.wait()
|
||||
if not self.listener_ready:
|
||||
self.listener.join()
|
||||
# TODO: add custom exception (easier to ignore and recognize)
|
||||
raise Exception()
|
||||
|
||||
def signal_term_handler(self, sig, frame):
|
||||
if sig == signal.SIGTERM:
|
||||
log.info("NetlinkManagerWithListener: Caught SIGTERM")
|
||||
|
||||
if self.listener:
|
||||
self.listener.shutdown_event.set()
|
||||
@@ -290,19 +397,23 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
self.alarm.set()
|
||||
|
||||
def tx_nlpacket_get_response(self, nlpacket):
|
||||
# WARNING: having multiple threads waiting for ACKs might result in
|
||||
# undefined behavior. To make this work we should probably have a
|
||||
# (thread-safe) list of all the target SEQs and PIDs along side a
|
||||
# reference to their alarms (thead.Event) to notify the right thread
|
||||
# of the RXed ACK.
|
||||
"""
|
||||
TX the message and wait for an ack
|
||||
"""
|
||||
|
||||
# NetlinkListener looks at the manager's target_seq and target_pid
|
||||
# to know when we've RXed the ack that we want
|
||||
self.target_lock.acquire()
|
||||
self.target_seq = nlpacket.seq
|
||||
self.target_pid = nlpacket.pid
|
||||
with self.target_lock:
|
||||
self.target_seq = nlpacket.seq
|
||||
self.target_pid = nlpacket.pid
|
||||
|
||||
if not self.tx_socket:
|
||||
self.tx_socket_allocate()
|
||||
self.target_lock.release()
|
||||
if not self.tx_socket:
|
||||
self.tx_socket_allocate()
|
||||
|
||||
log.debug('%s TX: TXed %s seq %d, pid %d, %d bytes' %
|
||||
(self, NetlinkPacket.type_to_string[nlpacket.msgtype],
|
||||
@@ -325,13 +436,29 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
log.debug("RXed RTM_DELLINK seq %d, pid %d, %d bytes, for %s, state %s" %
|
||||
(msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFLA_IFNAME), "up" if msg.is_up() else "down"))
|
||||
|
||||
def rx_rtm_newnetconf(self, msg):
|
||||
ifindex = msg.get_attribute_value(msg.NETCONFA_IFINDEX)
|
||||
ifname = self.ifname_by_index.get(ifindex)
|
||||
|
||||
if ifname:
|
||||
log.debug("RXed RTM_NEWNETCONF seq %d, pid %d, %d bytes on ifname %s" % (msg.seq, msg.pid, msg.length, ifname))
|
||||
else:
|
||||
log.debug("RXed RTM_NEWNETCONF seq %d, pid %d, %d bytes on ifindex %s" % (msg.seq, msg.pid, msg.length, ifindex))
|
||||
|
||||
def rx_rtm_delnetconf(self, msg):
|
||||
ifindex = msg.get_attribute_value(msg.NETCONFA_IFINDEX)
|
||||
ifname = self.ifname_by_index.get(ifindex)
|
||||
|
||||
if ifname:
|
||||
log.debug("RXed RTM_DELNETCONF seq %d, pid %d, %d bytes on ifname %s" % (msg.seq, msg.pid, msg.length, ifname))
|
||||
else:
|
||||
log.debug("RXed RTM_DELNETCONF seq %d, pid %d, %d bytes on ifindex %s" % (msg.seq, msg.pid, msg.length, ifindex))
|
||||
|
||||
def rx_rtm_newaddr(self, msg):
|
||||
log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
|
||||
(msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
|
||||
log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s on %s" % (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), self.ifname_by_index.get(msg.ifindex)))
|
||||
|
||||
def rx_rtm_deladdr(self, msg):
|
||||
log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
|
||||
(msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
|
||||
log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s on %s" % (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), self.ifname_by_index.get(msg.ifindex)))
|
||||
|
||||
def rx_rtm_newneigh(self, msg):
|
||||
log.debug("RXed RTM_NEWNEIGH seq %d, pid %d, %d bytes, for %s on %s" %
|
||||
@@ -349,6 +476,12 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
log.debug("RXed RTM_DELROUTE seq %d, pid %d, %d bytes, for %s%s" %
|
||||
(msg.seq, msg.pid, msg.length, msg.get_prefix_string(), msg.get_nexthops_string(self.ifname_by_index)))
|
||||
|
||||
def rx_rtm_newmdb(self, msg):
|
||||
log.debug("RXed RTM_NEWMDB")
|
||||
|
||||
def rx_rtm_delmdb(self, msg):
|
||||
log.debug("RXed RTM_DELMDB")
|
||||
|
||||
def rx_nlmsg_done(self, msg):
|
||||
log.debug("RXed NLMSG_DONE seq %d, pid %d, %d bytes" % (msg.seq, msg.pid, msg.length))
|
||||
|
||||
@@ -552,7 +685,7 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
def filter_by_nested_attribute(self, add, filter_type, msgtype, attr_filter):
|
||||
self._filter_update(add, filter_type, msgtype, ('NESTED_ATTRIBUTE', attr_filter))
|
||||
|
||||
def service_netlinkq(self):
|
||||
def service_netlinkq(self, notify_event=None):
|
||||
msg_count = {}
|
||||
processed = 0
|
||||
|
||||
@@ -577,6 +710,12 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
elif msgtype == RTM_NEWROUTE or msgtype == RTM_DELROUTE:
|
||||
msg = Route(msgtype, debug, use_color=self.use_color)
|
||||
|
||||
elif msgtype in (RTM_GETNETCONF, RTM_NEWNETCONF, RTM_DELNETCONF):
|
||||
msg = Netconf(msgtype, debug, use_color=self.use_color)
|
||||
|
||||
elif msgtype == RTM_NEWMDB or msgtype == RTM_DELMDB:
|
||||
msg = MDB(msgtype, debug, use_color=self.use_color)
|
||||
|
||||
elif msgtype == NLMSG_DONE:
|
||||
msg = Done(msgtype, debug, use_color=self.use_color)
|
||||
|
||||
@@ -630,6 +769,18 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
elif msg.msgtype == RTM_DELROUTE:
|
||||
self.rx_rtm_delroute(msg)
|
||||
|
||||
elif msg.msgtype == RTM_NEWNETCONF:
|
||||
self.rx_rtm_newnetconf(msg)
|
||||
|
||||
elif msg.msgtype == RTM_DELNETCONF:
|
||||
self.rx_rtm_delnetconf(msg)
|
||||
|
||||
elif msg.msgtype == RTM_NEWMDB:
|
||||
self.rx_rtm_newmdb(msg)
|
||||
|
||||
elif msg.msgtype == RTM_DELMDB:
|
||||
self.rx_rtm_delmdb(msg)
|
||||
|
||||
elif msg.msgtype == NLMSG_DONE:
|
||||
self.rx_nlmsg_done(msg)
|
||||
|
||||
@@ -639,6 +790,9 @@ class NetlinkManagerWithListener(NetlinkManager):
|
||||
if processed:
|
||||
self.netlinkq = self.netlinkq[processed:]
|
||||
|
||||
if notify_event:
|
||||
notify_event.set()
|
||||
|
||||
# too chatty
|
||||
# for msgtype in msg_count:
|
||||
# log.debug('RXed %d %s messages' % (msg_count[msgtype], NetlinkPacket.type_to_string[msgtype]))
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
|
||||
# Copyright (C) 2015-2020 Cumulus Networks, Inc. all rights reserved
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
@@ -130,7 +130,10 @@ class NetlinkManager(object):
|
||||
self._debug_set_clear((RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE), enabled)
|
||||
|
||||
def debug_netconf(self, enabled):
|
||||
self._debug_set_clear((RTM_GETNETCONF, RTM_NEWNETCONF), enabled)
|
||||
self._debug_set_clear((RTM_GETNETCONF, RTM_NEWNETCONF, RTM_DELNETCONF), enabled)
|
||||
|
||||
def debug_mdb(self, enabled):
|
||||
self._debug_set_clear((RTM_GETMDB, RTM_NEWMDB, RTM_DELMDB), enabled)
|
||||
|
||||
def debug_this_packet(self, mtype):
|
||||
if mtype in self.debug:
|
||||
@@ -353,6 +356,9 @@ class NetlinkManager(object):
|
||||
elif msgtype in (RTM_GETNETCONF, RTM_NEWNETCONF):
|
||||
msg = Netconf(msgtype, nlpacket.debug, use_color=self.use_color)
|
||||
|
||||
elif msgtype in (RTM_GETMDB, RTM_NEWMDB, RTM_DELMDB):
|
||||
msg = MDB(msgtype, nlpacket.debug, use_color=self.use_color)
|
||||
|
||||
else:
|
||||
raise Exception("RXed unknown netlink message type %s" % msgtype)
|
||||
|
||||
@@ -396,6 +402,10 @@ class NetlinkManager(object):
|
||||
msg = Route(rtm_type, debug, use_color=self.use_color)
|
||||
msg.body = pack('Bxxxii', family, 0, 0)
|
||||
|
||||
elif rtm_type == RTM_GETMDB:
|
||||
msg = MDB(rtm_type, debug, use_color=self.use_color)
|
||||
msg.body = pack('Bxxxii', family, 0, 0)
|
||||
|
||||
else:
|
||||
log.error("request_dump RTM_GET %s is not supported" % rtm_type)
|
||||
return None
|
||||
@@ -723,24 +733,24 @@ class NetlinkManager(object):
|
||||
|
||||
return self._link_add(ifindex, ifname, 'vlan', ifla_info_data)
|
||||
|
||||
def link_add_macvlan(self, ifindex, ifname):
|
||||
def link_add_macvlan(self, ifindex, ifname, macvlan_mode):
|
||||
"""
|
||||
ifindex is the index of the parent interface that this sub-interface
|
||||
is being added to
|
||||
"""
|
||||
return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE})
|
||||
|
||||
def link_add_xfrm(self, physdev, xfrm_ifname, xfrm_id):
|
||||
"""
|
||||
ifindex is the index of the parent interface that this sub-interface
|
||||
is being added to
|
||||
"""
|
||||
ifla_info_data = {
|
||||
Link.IFLA_XFRM_IF_ID: int(xfrm_id),
|
||||
Link.IFLA_XFRM_LINK: int(physdev)
|
||||
}
|
||||
|
||||
return self._link_add(ifindex=None, ifname=xfrm_ifname, kind='xfrm', ifla_info_data=ifla_info_data)
|
||||
return self._link_add(
|
||||
ifindex,
|
||||
ifname,
|
||||
'macvlan',
|
||||
{
|
||||
Link.IFLA_MACVLAN_MODE: {
|
||||
"private": Link.MACVLAN_MODE_PRIVATE,
|
||||
"vepa": Link.MACVLAN_MODE_VEPA,
|
||||
"bridge": Link.MACVLAN_MODE_BRIDGE,
|
||||
"passthru": Link.MACVLAN_MODE_PASSTHRU
|
||||
}.get(macvlan_mode, Link.MACVLAN_MODE_PRIVATE)
|
||||
}
|
||||
)
|
||||
|
||||
def vlan_get(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
|
||||
"""
|
||||
@@ -1077,3 +1087,14 @@ class NetlinkManager(object):
|
||||
msg.flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK
|
||||
msg.build_message(self.sequence.next(), self.pid)
|
||||
return self.tx_nlpacket_get_response(msg)
|
||||
|
||||
# ===
|
||||
# MDB
|
||||
# ===
|
||||
def mdb_dump(self):
|
||||
debug = RTM_GETMDB in self.debug
|
||||
msg = MDB(RTM_GETMDB, debug, use_color=self.use_color)
|
||||
msg.body = pack('Bxxxiii', socket.AF_BRIDGE, 0, 0, 0)
|
||||
msg.flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK
|
||||
msg.build_message(self.sequence.next(), self.pid)
|
||||
return self.tx_nlpacket_get_response(msg)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,288 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2017 Cumulus Networks, Inc. All rights reserved.
|
||||
# Authors:
|
||||
# Roopa Prabhu, roopa@cumulusnetworks.com
|
||||
# Julien Fortin, julien@cumulusnetworks.com
|
||||
#
|
||||
# ifupdown2 --
|
||||
# tool to configure network interfaces
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import fcntl
|
||||
import struct
|
||||
import signal
|
||||
import socket
|
||||
import daemon
|
||||
import select
|
||||
import datetime
|
||||
import threading
|
||||
|
||||
try:
|
||||
import ifupdown2.ifupdown.argv
|
||||
|
||||
from ifupdown2.ifupdown.log import log
|
||||
from ifupdown2.ifupdown.main import Ifupdown2
|
||||
except ImportError:
|
||||
import ifupdown.argv
|
||||
|
||||
from ifupdown.log import log
|
||||
from ifupdown.main import Ifupdown2
|
||||
|
||||
|
||||
class Daemon:
|
||||
shutdown_event = threading.Event()
|
||||
|
||||
def __init__(self):
|
||||
self.uds = None
|
||||
self.context = None
|
||||
self.working_directory = '/var/run/ifupdown2d/'
|
||||
self.server_address = '/var/run/ifupdown2d/uds'
|
||||
|
||||
if not os.path.exists(self.working_directory):
|
||||
log.info('creating %s' % self.working_directory)
|
||||
os.makedirs(self.working_directory, mode=0755)
|
||||
|
||||
if os.path.exists(self.server_address):
|
||||
log.info('removing uds %s' % self.server_address)
|
||||
os.remove(self.server_address)
|
||||
|
||||
self.context = daemon.DaemonContext(
|
||||
working_directory=self.working_directory,
|
||||
signal_map={
|
||||
signal.SIGINT: self.signal_handler,
|
||||
signal.SIGTERM: self.signal_handler,
|
||||
signal.SIGQUIT: self.signal_handler,
|
||||
},
|
||||
umask=0o22
|
||||
)
|
||||
|
||||
try:
|
||||
self.SO_PEERCRED = socket.SO_PEERCRED
|
||||
except AttributeError:
|
||||
# powerpc is the only non-generic we care about. alpha, mips,
|
||||
# sparc, and parisc also have non-generic values.
|
||||
machine = os.uname()[4]
|
||||
if re.search(r'^(ppc|powerpc)', machine):
|
||||
self.SO_PASSCRED = 20
|
||||
self.SO_PEERCRED = 21
|
||||
else:
|
||||
self.SO_PASSCRED = 16
|
||||
self.SO_PEERCRED = 17
|
||||
|
||||
log.info('daemonizing ifupdown2d...')
|
||||
self.context.open()
|
||||
|
||||
log.info('preloading all necessary modules')
|
||||
self.preload_imports()
|
||||
|
||||
try:
|
||||
log.info('opening UNIX socket')
|
||||
self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
fcntl.fcntl(self.uds.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
|
||||
except Exception as e:
|
||||
raise Exception('socket: %s' % str(e))
|
||||
try:
|
||||
self.uds.bind(self.server_address)
|
||||
except Exception as e:
|
||||
raise Exception('bind: %s' % str(e))
|
||||
try:
|
||||
self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
|
||||
except Exception as e:
|
||||
raise Exception('setsockopt: %s' % str(e))
|
||||
try:
|
||||
self.uds.listen(1)
|
||||
except Exception as e:
|
||||
raise Exception('listen: %s' % str(e))
|
||||
os.chmod(self.server_address, 0777)
|
||||
|
||||
def __del__(self):
|
||||
if self.context:
|
||||
self.context.close()
|
||||
if self.uds:
|
||||
self.uds.close()
|
||||
|
||||
@staticmethod
|
||||
def preload_imports():
|
||||
"""
|
||||
preloading all the necessary modules
|
||||
at first will increase performances
|
||||
"""
|
||||
try:
|
||||
import io
|
||||
import pdb
|
||||
import imp
|
||||
import sets
|
||||
import json
|
||||
import glob
|
||||
import time
|
||||
import copy
|
||||
import errno
|
||||
import pprint
|
||||
import atexit
|
||||
import ipaddr
|
||||
import cPickle
|
||||
import logging
|
||||
import argparse
|
||||
import StringIO
|
||||
import datetime
|
||||
import traceback
|
||||
import itertools
|
||||
import subprocess
|
||||
import argcomplete
|
||||
import collections
|
||||
import ConfigParser
|
||||
import pkg_resources
|
||||
|
||||
import ifupdown2.ifupdown.exceptions
|
||||
import ifupdown2.ifupdown.graph
|
||||
import ifupdown2.ifupdown.iface
|
||||
import ifupdown2.ifupdown.iff
|
||||
import ifupdown2.ifupdown.ifupdownbase
|
||||
import ifupdown2.ifupdown.ifupdownbase
|
||||
import ifupdown2.ifupdown.ifupdownconfig
|
||||
import ifupdown2.ifupdown.ifupdownflags
|
||||
import ifupdown2.ifupdown.ifupdownmain
|
||||
import ifupdown2.ifupdown.netlink
|
||||
import ifupdown2.ifupdown.networkinterfaces
|
||||
import ifupdown2.ifupdown.policymanager
|
||||
import ifupdown2.ifupdown.scheduler
|
||||
import ifupdown2.ifupdown.statemanager
|
||||
import ifupdown2.ifupdown.template
|
||||
import ifupdown2.ifupdown.utils
|
||||
|
||||
import ifupdown2.ifupdownaddons.cache
|
||||
import ifupdown2.ifupdownaddons.dhclient
|
||||
import ifupdown2.ifupdownaddons.mstpctlutil
|
||||
import ifupdown2.ifupdownaddons.LinkUtils
|
||||
import ifupdown2.ifupdownaddons.modulebase
|
||||
import ifupdown2.ifupdownaddons.systemutils
|
||||
import ifupdown2.ifupdownaddons.utilsbase
|
||||
except ImportError, e:
|
||||
raise ImportError('%s - required module not found' % str(e))
|
||||
|
||||
@staticmethod
|
||||
def signal_handler(sig, frame):
|
||||
log.info('received %s' % 'SIGINT' if sig == signal.SIGINT else 'SIGTERM')
|
||||
Daemon.shutdown_event.set()
|
||||
|
||||
@staticmethod
|
||||
def user_waiting_for_reply():
|
||||
return not log.is_syslog()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
while True:
|
||||
if Daemon.shutdown_event.is_set():
|
||||
log.info("shutdown signal RXed, breaking out loop")
|
||||
break
|
||||
|
||||
try:
|
||||
(client_socket, client_address) = self.uds.accept()
|
||||
except socket.error as e:
|
||||
log.error(str(e))
|
||||
break
|
||||
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
exit(self.ifupdown2(client_socket))
|
||||
else:
|
||||
log.tx_data(json.dumps({'pid': pid}), socket=client_socket)
|
||||
|
||||
start = datetime.datetime.now()
|
||||
status = os.WEXITSTATUS(os.waitpid(pid, 0)[1])
|
||||
end = datetime.datetime.now()
|
||||
|
||||
log.tx_data(json.dumps({'status': status}), socket=client_socket)
|
||||
client_socket.close()
|
||||
|
||||
log.info('exit status %d - in %ssecs'
|
||||
% (status, (end - start).total_seconds()))
|
||||
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
self.uds.close()
|
||||
|
||||
def get_client_uid(self, client_socket):
|
||||
creds = client_socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize('3i'))
|
||||
(pid, uid, gid) = struct.unpack('3i', creds)
|
||||
log.debug('client uid %d' % uid)
|
||||
return uid
|
||||
|
||||
@staticmethod
|
||||
def get_client_request(client_socket):
|
||||
"""
|
||||
This function handles requests of any length.
|
||||
|
||||
if the received json is longer than 65k it will be truncated
|
||||
several calls to recv will be needed, we store the data until
|
||||
we can decode them with the json library.
|
||||
"""
|
||||
data = []
|
||||
while True:
|
||||
log.debug('waiting for request on client socket')
|
||||
ready = select.select([client_socket], [], [])
|
||||
|
||||
if ready and ready[0] and ready[0][0] == client_socket:
|
||||
# data available start reading
|
||||
raw_data = client_socket.recv(65536)
|
||||
|
||||
try:
|
||||
return json.loads(raw_data)
|
||||
except ValueError:
|
||||
# the json is incomplete
|
||||
data.append(raw_data)
|
||||
|
||||
if len(data) > 1:
|
||||
try:
|
||||
return json.loads(''.join(data))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def ifupdown2(self, client_socket):
|
||||
try:
|
||||
fcntl.fcntl(client_socket.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
|
||||
|
||||
ifupdown2 = Ifupdown2(daemon=True, uid=self.get_client_uid(client_socket))
|
||||
ifupdown2.set_signal_handlers()
|
||||
|
||||
request = self.get_client_request(client_socket)
|
||||
log.info('request: %s' % request['argv'])
|
||||
|
||||
ifupdown2.parse_argv(request['argv'])
|
||||
# adjust the logger with argv
|
||||
ifupdown2.update_logger(socket=client_socket)
|
||||
|
||||
try:
|
||||
status = ifupdown2.main(request['stdin'])
|
||||
except Exception as e:
|
||||
log.error(str(e))
|
||||
status = 1
|
||||
|
||||
except ifupdown2.ifupdown.argv.ArgvParseError as e:
|
||||
log.update_current_logger(syslog=False, verbose=True, debug=False)
|
||||
log.set_socket(client_socket)
|
||||
e.log_error()
|
||||
status = 1
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
status = 1
|
||||
|
||||
log.flush()
|
||||
log.set_socket(None)
|
||||
client_socket.close()
|
||||
return status
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
Daemon().run()
|
||||
except Exception as e:
|
||||
print e
|
||||
log.error(str(e))
|
||||
import traceback
|
||||
log.error(traceback.format_exc())
|
||||
exit(1)
|
@@ -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
|
16
setup.py
16
setup.py
@@ -12,7 +12,6 @@ INSTALL_REQUIRES = [
|
||||
]
|
||||
|
||||
DATA_FILES = [
|
||||
('/etc/default/', ['etc/default/networking']),
|
||||
('/etc/network/ifupdown2/', ['etc/network/ifupdown2/addons.conf']),
|
||||
('/etc/network/ifupdown2/', ['etc/network/ifupdown2/ifupdown2.conf']),
|
||||
]
|
||||
@@ -25,13 +24,12 @@ ENTRY_POINTS = {}
|
||||
def build_deb_package():
|
||||
try:
|
||||
return sys.argv[sys.argv.index('--root') + 1].endswith('/debian/ifupdown2')
|
||||
except:
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
if build_deb_package():
|
||||
DATA_FILES.append(('/usr/share/ifupdown2/sbin/', ['ifupdown2/sbin/start-networking']))
|
||||
else:
|
||||
if not build_deb_package():
|
||||
ENTRY_POINTS = {
|
||||
'console_scripts': [
|
||||
'ifup = ifupdown2.__main__:main',
|
||||
@@ -42,8 +40,8 @@ else:
|
||||
}
|
||||
|
||||
setup(
|
||||
author='Roopa Prabhu',
|
||||
author_email='roopa@cumulusnetworks.com',
|
||||
author='Julien Fortin',
|
||||
author_email='julien@cumulusnetworks.com',
|
||||
maintainer='Julien Fortin',
|
||||
maintainer_email='julien@cumulusnetworks.com',
|
||||
classifiers=[
|
||||
@@ -65,7 +63,7 @@ setup(
|
||||
name='ifupdown2',
|
||||
packages=find_packages(),
|
||||
url='https://github.com/CumulusNetworks/ifupdown2',
|
||||
version='1.2.9',
|
||||
version='2.0.0',
|
||||
data_files=DATA_FILES,
|
||||
setup_requires=['setuptools'],
|
||||
scripts=SCRIPTS,
|
||||
|
Reference in New Issue
Block a user