Merge "Add sanity_check for keepalived ipv6 support"
This commit is contained in:
commit
d1f768e064
|
@ -14,16 +14,24 @@
|
|||
# under the License.
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import netaddr
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
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.plugins.common import constants as const
|
||||
|
@ -166,6 +174,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
|
||||
|
||||
|
||||
|
@ -35,6 +36,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):
|
||||
|
@ -105,6 +107,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:
|
||||
|
@ -181,6 +192,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')),
|
||||
]
|
||||
|
||||
|
||||
|
@ -214,6 +227,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