Add sanity_check for keepalived ipv6 support
Currently Neutron does not validate the version of Keepalived. In order to support configuring IPv6 default route, the minimum version [1] of keepalived that is required is 1.2.10 This patch validates the required support in keepalived. Although keepalived changelog mentions that IPv6 virtual_routes and static_routes are supported in version 1.2.8, it is seen that in v1.2.8 IPv6 default route is not supported. Support for default route is added in keepalived v1.2.10. Observations on v1.2.8 are captured at the following link - http://paste.openstack.org/show/165403/ [1] - http://www.keepalived.org/changelog.html Closes-Bug: #1415756 Change-Id: I768ae233d2c2e24df93a4736ef6d8f4005770fa5
This commit is contained in:
parent
345ffb99ef
commit
a04f44412b
|
@ -14,15 +14,23 @@
|
|||
# under the License.
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import netaddr
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.l3 import ha_router
|
||||
from neutron.agent.l3 import namespaces
|
||||
from neutron.agent.linux import external_process
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.agent.linux import ip_link_support
|
||||
from neutron.agent.linux import keepalived
|
||||
from neutron.agent.linux import utils as agent_utils
|
||||
from neutron.common import constants as n_consts
|
||||
from neutron.common import utils
|
||||
from neutron.i18n import _LE
|
||||
from neutron.openstack.common import uuidutils
|
||||
|
@ -165,6 +173,124 @@ def dnsmasq_version_supported():
|
|||
return True
|
||||
|
||||
|
||||
class KeepalivedIPv6Test(object):
|
||||
def __init__(self, ha_port, gw_port, gw_vip, default_gw):
|
||||
self.ha_port = ha_port
|
||||
self.gw_port = gw_port
|
||||
self.gw_vip = gw_vip
|
||||
self.default_gw = default_gw
|
||||
self.manager = None
|
||||
self.config = None
|
||||
self.config_path = None
|
||||
self.nsname = "keepalivedtest-" + uuidutils.generate_uuid()
|
||||
self.pm = external_process.ProcessMonitor(cfg.CONF, 'router')
|
||||
self.orig_interval = cfg.CONF.AGENT.check_child_processes_interval
|
||||
|
||||
def configure(self):
|
||||
config = keepalived.KeepalivedConf()
|
||||
instance1 = keepalived.KeepalivedInstance('MASTER', self.ha_port, 1,
|
||||
['169.254.192.0/18'],
|
||||
advert_int=5)
|
||||
instance1.track_interfaces.append(self.ha_port)
|
||||
|
||||
# Configure keepalived with an IPv6 address (gw_vip) on gw_port.
|
||||
vip_addr1 = keepalived.KeepalivedVipAddress(self.gw_vip, self.gw_port)
|
||||
instance1.vips.append(vip_addr1)
|
||||
|
||||
# Configure keepalived with an IPv6 default route on gw_port.
|
||||
gateway_route = keepalived.KeepalivedVirtualRoute(n_consts.IPv6_ANY,
|
||||
self.default_gw,
|
||||
self.gw_port)
|
||||
instance1.virtual_routes.gateway_routes = [gateway_route]
|
||||
config.add_instance(instance1)
|
||||
self.config = config
|
||||
|
||||
def start_keepalived_process(self):
|
||||
# Disable process monitoring for Keepalived process.
|
||||
cfg.CONF.set_override('check_child_processes_interval', 0, 'AGENT')
|
||||
|
||||
# Create a temp directory to store keepalived configuration.
|
||||
self.config_path = tempfile.mkdtemp()
|
||||
|
||||
# Instantiate keepalived manager with the IPv6 configuration.
|
||||
self.manager = keepalived.KeepalivedManager('router1', self.config,
|
||||
namespace=self.nsname, process_monitor=self.pm,
|
||||
conf_path=self.config_path)
|
||||
self.manager.spawn()
|
||||
|
||||
def verify_ipv6_address_assignment(self, gw_dev):
|
||||
process = self.manager.get_process()
|
||||
agent_utils.wait_until_true(lambda: process.active)
|
||||
|
||||
def _gw_vip_assigned():
|
||||
iface_ip = gw_dev.addr.list(ip_version=6, scope='global')
|
||||
if iface_ip:
|
||||
return self.gw_vip == iface_ip[0]['cidr']
|
||||
|
||||
agent_utils.wait_until_true(_gw_vip_assigned)
|
||||
|
||||
def __enter__(self):
|
||||
ip_lib.IPWrapper().netns.add(self.nsname)
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||
self.pm.stop()
|
||||
if self.manager:
|
||||
self.manager.disable()
|
||||
if self.config_path:
|
||||
shutil.rmtree(self.config_path, ignore_errors=True)
|
||||
ip_lib.IPWrapper().netns.delete(self.nsname)
|
||||
cfg.CONF.set_override('check_child_processes_interval',
|
||||
self.orig_interval, 'AGENT')
|
||||
|
||||
|
||||
def keepalived_ipv6_supported():
|
||||
"""Check if keepalived supports IPv6 functionality.
|
||||
|
||||
Validation is done as follows.
|
||||
1. Create a namespace.
|
||||
2. Create OVS bridge with two ports (ha_port and gw_port)
|
||||
3. Move the ovs ports to the namespace.
|
||||
4. Spawn keepalived process inside the namespace with IPv6 configuration.
|
||||
5. Verify if IPv6 address is assigned to gw_port.
|
||||
6. Verify if IPv6 default route is configured by keepalived.
|
||||
"""
|
||||
|
||||
random_str = utils.get_random_string(6)
|
||||
br_name = "ka-test-" + random_str
|
||||
ha_port = ha_router.HA_DEV_PREFIX + random_str
|
||||
gw_port = namespaces.INTERNAL_DEV_PREFIX + random_str
|
||||
gw_vip = 'fdf8:f53b:82e4::10/64'
|
||||
expected_default_gw = 'fe80:f816::1'
|
||||
|
||||
with ovs_lib.OVSBridge(br_name) as br:
|
||||
with KeepalivedIPv6Test(ha_port, gw_port, gw_vip,
|
||||
expected_default_gw) as ka:
|
||||
br.add_port(ha_port, ('type', 'internal'))
|
||||
br.add_port(gw_port, ('type', 'internal'))
|
||||
|
||||
ha_dev = ip_lib.IPDevice(ha_port)
|
||||
gw_dev = ip_lib.IPDevice(gw_port)
|
||||
|
||||
ha_dev.link.set_netns(ka.nsname)
|
||||
gw_dev.link.set_netns(ka.nsname)
|
||||
|
||||
ha_dev.link.set_up()
|
||||
gw_dev.link.set_up()
|
||||
|
||||
ka.configure()
|
||||
|
||||
ka.start_keepalived_process()
|
||||
|
||||
ka.verify_ipv6_address_assignment(gw_dev)
|
||||
|
||||
default_gw = gw_dev.route.get_gateway(ip_version=6)
|
||||
if default_gw:
|
||||
default_gw = default_gw['gateway']
|
||||
|
||||
return expected_default_gw == default_gw
|
||||
|
||||
|
||||
def ovsdb_native_supported():
|
||||
# Running the test should ensure we are configured for OVSDB native
|
||||
try:
|
||||
|
|
|
@ -21,6 +21,7 @@ from oslo_log import log as logging
|
|||
from neutron.agent import dhcp_agent
|
||||
from neutron.cmd.sanity import checks
|
||||
from neutron.common import config
|
||||
from neutron.db import l3_hamode_db
|
||||
from neutron.i18n import _LE, _LW
|
||||
|
||||
|
||||
|
@ -32,6 +33,7 @@ cfg.CONF.import_group('ml2', 'neutron.plugins.ml2.config')
|
|||
cfg.CONF.import_group('ml2_sriov',
|
||||
'neutron.plugins.ml2.drivers.mech_sriov.mech_driver')
|
||||
dhcp_agent.register_options()
|
||||
cfg.CONF.register_opts(l3_hamode_db.L3_HA_OPTS)
|
||||
|
||||
|
||||
class BoolOptCallback(cfg.BoolOpt):
|
||||
|
@ -102,6 +104,15 @@ def check_dnsmasq_version():
|
|||
return result
|
||||
|
||||
|
||||
def check_keepalived_ipv6_support():
|
||||
result = checks.keepalived_ipv6_supported()
|
||||
if not result:
|
||||
LOG.error(_LE('The installed version of keepalived does not support '
|
||||
'IPv6. Please update to at least version 1.2.10 for '
|
||||
'IPv6 support.'))
|
||||
return result
|
||||
|
||||
|
||||
def check_nova_notify():
|
||||
result = checks.nova_notify_supported()
|
||||
if not result:
|
||||
|
@ -178,6 +189,8 @@ OPTS = [
|
|||
help=_('Check ovsdb native interface support')),
|
||||
BoolOptCallback('ebtables_installed', check_ebtables,
|
||||
help=_('Check ebtables installation')),
|
||||
BoolOptCallback('keepalived_ipv6_support', check_keepalived_ipv6_support,
|
||||
help=_('Check keepalived IPv6 support')),
|
||||
]
|
||||
|
||||
|
||||
|
@ -211,6 +224,8 @@ def enable_tests_from_config():
|
|||
cfg.CONF.set_override('dnsmasq_version', True)
|
||||
if cfg.CONF.OVS.ovsdb_interface == 'native':
|
||||
cfg.CONF.set_override('ovsdb_native', True)
|
||||
if cfg.CONF.l3_ha:
|
||||
cfg.CONF.set_override('keepalived_ipv6_support', True)
|
||||
|
||||
|
||||
def all_tests_passed():
|
||||
|
|
|
@ -67,3 +67,6 @@ class SanityTestCaseRoot(functional_base.BaseSudoTestCase):
|
|||
|
||||
def test_ovsdb_native_supported_runs(self):
|
||||
checks.ovsdb_native_supported()
|
||||
|
||||
def test_keepalived_ipv6_support(self):
|
||||
checks.keepalived_ipv6_supported()
|
||||
|
|
Loading…
Reference in New Issue