From d583d55c6a4e6c01c49411b7e5b519031bce0699 Mon Sep 17 00:00:00 2001 From: JUN JIE NAN Date: Fri, 10 Jan 2014 15:01:13 +0800 Subject: [PATCH] Check vxlan enablement via modinfo On some linux distribution, for RHEL 6.5 as an example, vxlan is enabled but the kernel version is still 2.6. And some linux kernel version is higher than 3.8 or even 3.11, its vxlan mod may be disabled. Under both situation, vxlan enablement checking via linux kernel version may not be correct. In this patch, we check vxlan enablement via modinfo: if vxlan module exists and functional test pass, vxlan is enabled. Closes-Bug: #1267682 Change-Id: Id52c04b4739d2d11fe52d4b1631cb4f39e6b577f --- neutron/common/exceptions.py | 4 + .../agent/linuxbridge_neutron_agent.py | 92 ++++++++--- .../plugins/linuxbridge/common/constants.py | 3 - .../unit/linuxbridge/test_lb_neutron_agent.py | 147 ++++++++++++------ 4 files changed, 170 insertions(+), 76 deletions(-) diff --git a/neutron/common/exceptions.py b/neutron/common/exceptions.py index c7c57642d1f..0a892210708 100644 --- a/neutron/common/exceptions.py +++ b/neutron/common/exceptions.py @@ -308,5 +308,9 @@ class NetworkVxlanPortRangeError(NeutronException): message = _("Invalid network VXLAN port range: '%(vxlan_range)s'") +class VxlanNetworkUnsupported(NeutronException): + message = _("VXLAN Network unsupported.") + + class DuplicatedExtension(NeutronException): message = _("Found duplicate extension: %(alias)s") diff --git a/neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py b/neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py index 22e8c5d5f64..66b4427420d 100755 --- a/neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -22,9 +22,7 @@ # Neutron OpenVSwitch Plugin. # @author: Sumit Naiksatam, Cisco Systems, Inc. -import distutils.version as dist_version import os -import platform import sys import time @@ -38,6 +36,7 @@ from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as sg_rpc from neutron.common import config as logging_config from neutron.common import constants +from neutron.common import exceptions from neutron.common import topics from neutron.common import utils as q_utils from neutron import context @@ -514,29 +513,74 @@ class LinuxBridgeManager: devices.add(device) return devices - def check_vxlan_support(self): - kernel_version = dist_version.LooseVersion(platform.release()) - if cfg.CONF.VXLAN.l2_population and ( - kernel_version > dist_version.LooseVersion( - lconst.MIN_VXLAN_KVER[lconst.VXLAN_UCAST])) and ( - ip_lib.iproute_arg_supported(['bridge', 'fdb'], - 'append', self.root_helper)): - self.vxlan_mode = lconst.VXLAN_UCAST - elif (kernel_version > dist_version.LooseVersion( - lconst.MIN_VXLAN_KVER[lconst.VXLAN_MCAST])) and ( - ip_lib.iproute_arg_supported(['ip', 'link', 'add', - 'type', 'vxlan'], 'proxy', - self.root_helper)): - if cfg.CONF.VXLAN.vxlan_group: - self.vxlan_mode = lconst.VXLAN_MCAST - else: - self.vxlan_mode = lconst.VXLAN_NONE - LOG.warning(_('VXLAN muticast group must be provided in ' - 'vxlan_group option to enable VXLAN')) + def vxlan_ucast_supported(self): + if not cfg.CONF.VXLAN.l2_population: + return False + if not ip_lib.iproute_arg_supported( + ['bridge', 'fdb'], 'append', self.root_helper): + LOG.warning(_('Option "%(option)s" must be supported by command ' + '"%(command)s" to enable %(mode)s mode') % + {'option': 'append', + 'command': 'bridge fdb', + 'mode': 'VXLAN UCAST'}) + return False + for segmentation_id in range(1, constants.MAX_VXLAN_VNI + 1): + if not self.device_exists( + self.get_vxlan_device_name(segmentation_id)): + break else: - self.vxlan_mode = lconst.VXLAN_NONE - LOG.warning(_('Unable to use VXLAN, it requires at least 3.8 ' - 'linux kernel and iproute2 3.8')) + LOG.error(_('No valid Segmentation ID to perform UCAST test.')) + return False + + test_iface = self.ensure_vxlan(segmentation_id) + try: + utils.execute( + cmd=['bridge', 'fdb', 'append', constants.FLOODING_ENTRY[0], + 'dev', test_iface, 'dst', '1.1.1.1'], + root_helper=self.root_helper) + return True + except RuntimeError: + return False + finally: + self.delete_vxlan(test_iface) + + def vxlan_mcast_supported(self): + if not cfg.CONF.VXLAN.vxlan_group: + LOG.warning(_('VXLAN muticast group must be provided in ' + 'vxlan_group option to enable VXLAN MCAST mode')) + return False + if not ip_lib.iproute_arg_supported( + ['ip', 'link', 'add', 'type', 'vxlan'], + 'proxy', self.root_helper): + LOG.warning(_('Option "%(option)s" must be supported by command ' + '"%(command)s" to enable %(mode)s mode') % + {'option': 'proxy', + 'command': 'ip link add type vxlan', + 'mode': 'VXLAN MCAST'}) + + return False + return True + + def vxlan_module_supported(self): + try: + utils.execute(cmd=['modinfo', 'vxlan']) + return True + except RuntimeError: + return False + + def check_vxlan_support(self): + self.vxlan_mode = lconst.VXLAN_NONE + if not self.vxlan_module_supported(): + LOG.error(_('Linux kernel vxlan module and iproute2 3.8 or above ' + 'are required to enable VXLAN.')) + raise exceptions.VxlanNetworkUnsupported() + + if self.vxlan_ucast_supported(): + self.vxlan_mode = lconst.VXLAN_UCAST + elif self.vxlan_mcast_supported(): + self.vxlan_mode = lconst.VXLAN_MCAST + else: + raise exceptions.VxlanNetworkUnsupported() LOG.debug(_('Using %s VXLAN mode'), self.vxlan_mode) def fdb_ip_entry_exists(self, mac, ip, interface): diff --git a/neutron/plugins/linuxbridge/common/constants.py b/neutron/plugins/linuxbridge/common/constants.py index 05180f592cc..6dee88f406c 100644 --- a/neutron/plugins/linuxbridge/common/constants.py +++ b/neutron/plugins/linuxbridge/common/constants.py @@ -28,9 +28,6 @@ VXLAN_NONE = 'not_supported' VXLAN_MCAST = 'multicast_flooding' VXLAN_UCAST = 'unicast_flooding' -# Corresponding minimal kernel versions requirements -MIN_VXLAN_KVER = {VXLAN_MCAST: '3.8', VXLAN_UCAST: '3.11'} - # TODO(rkukura): Eventually remove this function, which provides # temporary backward compatibility with pre-Havana RPC and DB vlan_id diff --git a/neutron/tests/unit/linuxbridge/test_lb_neutron_agent.py b/neutron/tests/unit/linuxbridge/test_lb_neutron_agent.py index 256707b5bb1..a15a1ee685f 100644 --- a/neutron/tests/unit/linuxbridge/test_lb_neutron_agent.py +++ b/neutron/tests/unit/linuxbridge/test_lb_neutron_agent.py @@ -24,6 +24,7 @@ import testtools from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import constants +from neutron.common import exceptions from neutron.openstack.common.rpc import common as rpc_common from neutron.plugins.common import constants as p_const from neutron.plugins.linuxbridge.agent import linuxbridge_neutron_agent @@ -646,59 +647,107 @@ class TestLinuxBridgeManager(base.BaseTestCase): "removed": set(["dev3"]) }) - def _check_vxlan_support(self, kernel_version, vxlan_proxy_supported, - fdb_append_supported, l2_population, - expected_mode): - def iproute_supported_side_effect(*args): - if args[1] == 'proxy': - return vxlan_proxy_supported - elif args[1] == 'append': - return fdb_append_supported - + def _check_vxlan_support(self, expected, vxlan_module_supported, + vxlan_ucast_supported, vxlan_mcast_supported): with contextlib.nested( - mock.patch("platform.release", return_value=kernel_version), - mock.patch.object(ip_lib, 'iproute_arg_supported', - side_effect=iproute_supported_side_effect), - ) as (kver_fn, ip_arg_fn): - self.lbm.check_vxlan_support() - self.assertEqual(self.lbm.vxlan_mode, expected_mode) + mock.patch.object(self.lbm, 'vxlan_module_supported', + return_value=vxlan_module_supported), + mock.patch.object(self.lbm, 'vxlan_ucast_supported', + return_value=vxlan_ucast_supported), + mock.patch.object(self.lbm, 'vxlan_mcast_supported', + return_value=vxlan_mcast_supported)): + if expected == lconst.VXLAN_NONE: + self.assertRaises(exceptions.VxlanNetworkUnsupported, + self.lbm.check_vxlan_support) + self.assertEqual(expected, self.lbm.vxlan_mode) + else: + self.lbm.check_vxlan_support() + self.assertEqual(expected, self.lbm.vxlan_mode) - def test_vxlan_mode_ucast(self): - self._check_vxlan_support(kernel_version='3.12', - vxlan_proxy_supported=True, - fdb_append_supported=True, - l2_population=True, - expected_mode=lconst.VXLAN_MCAST) + def test_check_vxlan_support(self): + self._check_vxlan_support(expected=lconst.VXLAN_UCAST, + vxlan_module_supported=True, + vxlan_ucast_supported=True, + vxlan_mcast_supported=True) + self._check_vxlan_support(expected=lconst.VXLAN_MCAST, + vxlan_module_supported=True, + vxlan_ucast_supported=False, + vxlan_mcast_supported=True) - def test_vxlan_mode_mcast(self): - self._check_vxlan_support(kernel_version='3.12', - vxlan_proxy_supported=True, - fdb_append_supported=False, - l2_population=True, - expected_mode=lconst.VXLAN_MCAST) - self._check_vxlan_support(kernel_version='3.10', - vxlan_proxy_supported=True, - fdb_append_supported=True, - l2_population=True, - expected_mode=lconst.VXLAN_MCAST) + self._check_vxlan_support(expected=lconst.VXLAN_NONE, + vxlan_module_supported=False, + vxlan_ucast_supported=False, + vxlan_mcast_supported=False) + self._check_vxlan_support(expected=lconst.VXLAN_NONE, + vxlan_module_supported=True, + vxlan_ucast_supported=False, + vxlan_mcast_supported=False) - def test_vxlan_mode_unsupported(self): - self._check_vxlan_support(kernel_version='3.7', - vxlan_proxy_supported=True, - fdb_append_supported=True, - l2_population=False, - expected_mode=lconst.VXLAN_NONE) - self._check_vxlan_support(kernel_version='3.10', - vxlan_proxy_supported=False, - fdb_append_supported=False, - l2_population=False, - expected_mode=lconst.VXLAN_NONE) - cfg.CONF.set_override('vxlan_group', '', 'VXLAN') - self._check_vxlan_support(kernel_version='3.12', - vxlan_proxy_supported=True, - fdb_append_supported=True, - l2_population=True, - expected_mode=lconst.VXLAN_NONE) + def _check_vxlan_module_supported(self, expected, execute_side_effect): + with mock.patch.object( + utils, 'execute', + side_effect=execute_side_effect): + self.assertEqual(expected, self.lbm.vxlan_module_supported()) + + def test_vxlan_module_supported(self): + self._check_vxlan_module_supported( + expected=True, + execute_side_effect=None) + self._check_vxlan_module_supported( + expected=False, + execute_side_effect=RuntimeError()) + + def _check_vxlan_ucast_supported( + self, expected, l2_population, iproute_arg_supported, fdb_append): + cfg.CONF.set_override('l2_population', l2_population, 'VXLAN') + with contextlib.nested( + mock.patch.object( + self.lbm, 'device_exists', return_value=False), + mock.patch.object(self.lbm, 'delete_vxlan', return_value=None), + mock.patch.object(self.lbm, 'ensure_vxlan', return_value=None), + mock.patch.object( + utils, 'execute', + side_effect=None if fdb_append else RuntimeError()), + mock.patch.object( + ip_lib, 'iproute_arg_supported', + return_value=iproute_arg_supported)): + self.assertEqual(expected, self.lbm.vxlan_ucast_supported()) + + def test_vxlan_ucast_supported(self): + self._check_vxlan_ucast_supported( + expected=False, + l2_population=False, iproute_arg_supported=True, fdb_append=True) + self._check_vxlan_ucast_supported( + expected=False, + l2_population=True, iproute_arg_supported=False, fdb_append=True) + self._check_vxlan_ucast_supported( + expected=False, + l2_population=True, iproute_arg_supported=True, fdb_append=False) + self._check_vxlan_ucast_supported( + expected=True, + l2_population=True, iproute_arg_supported=True, fdb_append=True) + + def _check_vxlan_mcast_supported( + self, expected, vxlan_group, iproute_arg_supported): + cfg.CONF.set_override('vxlan_group', vxlan_group, 'VXLAN') + with mock.patch.object( + ip_lib, 'iproute_arg_supported', + return_value=iproute_arg_supported): + self.assertEqual(expected, self.lbm.vxlan_mcast_supported()) + + def test_vxlan_mcast_supported(self): + self._check_vxlan_mcast_supported( + expected=False, + vxlan_group='', + iproute_arg_supported=True) + self._check_vxlan_mcast_supported( + expected=False, + vxlan_group='224.0.0.1', + iproute_arg_supported=False) + self._check_vxlan_mcast_supported( + expected=True, + vxlan_group='224.0.0.1', + iproute_arg_supported=True) class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):