From: JUN JIE NAN Date: Fri, 10 Jan 2014 07:01:13 +0000 (+0800) Subject: Check vxlan enablement via modinfo X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=d583d55c6a4e6c01c49411b7e5b519031bce0699;p=openstack-build%2Fneutron-build.git 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 --- diff --git a/neutron/common/exceptions.py b/neutron/common/exceptions.py index c7c57642d..0a8922107 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 22e8c5d5f..66b442742 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 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: + 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): - 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_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 (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')) + elif self.vxlan_mcast_supported(): + self.vxlan_mode = lconst.VXLAN_MCAST 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')) + 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 05180f592..6dee88f40 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 256707b5b..a15a1ee68 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.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_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) + + 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 _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("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) - - 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_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) - - 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) + 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):