1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Julien Fortin db4371de3c addons: vxlan: error out when an unsupported mix of SVD and TVDs are configured
Currently mixing SVDs and TVDs on the same system is not supported,
regardless of whether they are in the same vlan-aware bridge or
across different ones. NVUE will configure all VNIs as SVDs by
default so this would hopefully only arise if a user edits
/etc/network/interfaces and manages their configuration with ifupdown2.

Signed-off-by: Julien Fortin <jfortin@nvidia.com>
2021-07-01 19:08:04 +02:00

228 lines
8.2 KiB
Python

# Copyright (C) 2017, 2018 Cumulus Networks, Inc. all rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
#
# Author:
# Julien Fortin, julien@cumulusnetworks.com
#
# addon -- Addon base class
#
import logging
from collections import OrderedDict
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
from ifupdown2.ifupdown.iface import ifaceLinkPrivFlags, ifaceLinkKind
import ifupdown2.ifupdown.policymanager as policymanager
import ifupdown2.nlmanager.ipnetwork as ipnetwork
except (ImportError, ModuleNotFoundError):
from lib.io import IO
from lib.sysfs import Sysfs
from lib.iproute2 import IPRoute2
from lib.base_objects import Netlink, Cache, Requirements
from ifupdown.iface import ifaceLinkPrivFlags, ifaceLinkKind
import ifupdown.policymanager as policymanager
import nlmanager.ipnetwork as ipnetwork
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()
self.__alias_to_attribute = {}
for attribute_name, attribute_object in self.__get_modinfo().get("attrs", {}).items():
for alias in attribute_object.get("aliases", []):
self.__alias_to_attribute[alias] = attribute_name
def __get_modinfo(self) -> dict:
try:
return self._modinfo
except AttributeError:
return {}
def translate(self, ifaceobjs):
"""
Replace attribute aliases from user configuration with real attribute name
"""
for ifaceobj in ifaceobjs:
ifaceobj.config = OrderedDict(
[
(self.__alias_to_attribute[user_attr], user_value)
if user_attr in self.__alias_to_attribute
else (user_attr, user_value)
for user_attr, user_value in ifaceobj.config.items()
]
)
class Vxlan(Addon):
single_vxlan_configured = set()
traditional_vxlan_configured = set()
def __int__(self):
super(Vxlan, self).__int__()
class Bridge(Addon):
bridge_vlan_aware_list = set()
def __init__(self):
super(Bridge, self).__init__()
def _re_evaluate_bridge_vxlan(self, ifaceobj, ifaceobj_getfunc=None):
"""
Quick fix for BRIDGE_VXLAN
BRIDGE_VXLAN is not set on the bridge because the VXLAN hasn't been processed yet
(because its defined after the bridge in /e/n/i), here is what happens:
- ifupdownmain:populate_dependency_info()
- loops over all the intf from /e/n/i (with the example config:
['lo', 'eth0', 'swp1', 'swp2', 'bridge', 'vni-10', 'bridge.100', 'vlan100'])
----> bridge is first in the list of interface (that we care about)
- ifupdownmain:query_lowerifaces()
- bridge:get_dependent is called (debug: bridge: evaluating port expr '['swp1', 'swp2', 'vni-10']')
- ifupdownmain:preprocess_dependency_list()
- calls ifupdownmain:_set_iface_role_n_kind() on all the brports:
in _set_iface_role_n_kind:
ifaceobj is the brport
upperifaceobj is the bridge
it tries to see if the bridge has a VXLAN:
if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
but because the bridge is first in the /e/n/i ifupdown2 didn't
call vxlan:get_dependent_ifacenames so VXLAN is not set on ifaceobj
:return:
"""
if not ifaceobj_getfunc:
return
if ifaceobj.link_kind & ifaceLinkKind.BRIDGE and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
for port in self._get_bridge_port_list(ifaceobj) or []:
for brport_ifaceobj in ifaceobj_getfunc(port):
if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
self.__check_l3vni_bridge(ifaceobj)
return
elif ifaceobj.link_kind & ifaceLinkKind.BRIDGE:
self.__check_l3vni_bridge(ifaceobj)
elif ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
for bridge_ifaceobj in ifaceobj_getfunc(iface) or []:
bridge_ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
self.__check_l3vni_bridge(bridge_ifaceobj)
def __check_l3vni_bridge(self, ifaceobj):
# the calling function needs to make sure that the following checks were performed:
# ifaceobj.link_kind & ifaceLinkKind.BRIDGE
# ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE \
and len(self._get_ifaceobj_bridge_ports(ifaceobj, as_list=True)) == 1:
ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_l3VNI
@staticmethod
def _get_ifaceobj_bridge_ports(ifaceobj, as_list=False):
bridge_ports = []
for brport in ifaceobj.get_attr_value('bridge-ports') or []:
if brport != 'none':
bridge_ports.extend(brport.split())
if as_list:
return bridge_ports
return ' '.join(bridge_ports)
def _get_bridge_port_list(self, ifaceobj):
# port list is also available in the previously
# parsed dependent list. Use that if available, instead
# of parsing port expr again
port_list = ifaceobj.lowerifaces
if port_list:
return port_list
ports = self._get_ifaceobj_bridge_ports(ifaceobj)
if ports:
return self.parse_port_list(ifaceobj.name, ports)
else:
return None
class AddonWithIpBlackList(Addon):
try:
ip_blacklist = [ipnetwork.IPNetwork(ip).ip for ip in policymanager.policymanager_api.get_module_globals(
module_name="address",
attr="ip_blacklist"
) or []]
__ip_blacklist_exception = None
except Exception as e:
__ip_blacklist_exception = e
ip_blacklist = []
def __init__(self):
"""
If an exception occurred during the ip blacklist parsing we need to display it (once)
Also we keep this as a class variable to share it between the address and addressvirtual module
"""
super(AddonWithIpBlackList, self).__init__()
if AddonWithIpBlackList.__ip_blacklist_exception:
self.logger.warning("policy.d: address: 'ip_blacklist': %s" % AddonWithIpBlackList.__ip_blacklist_exception)
AddonWithIpBlackList.__ip_blacklist_exception = None
def ip_blacklist_check(self, ifname, ip):
"""
Check if the ip address is not blacklisted (in ip_blacklist)
:param ifname:
:param ip:
:return:
"""
if ip.ip in AddonWithIpBlackList.ip_blacklist:
raise Exception("%s: blacklisted ip address in use: %s" % (ifname, ip.ip))