Keepalived global_defs configuration entries required to avoid DNS lookup

This changeset addresses a particular L3-HA Neutron deployment scenario
in which the DNS server configured for the management network is not
also accessible from the virtual router namespace (i.e. over the
external network).
Keepalived uses the hostname against getaddrinfo twice to set default
values for the router_id and notification_email_from global configuration
attributes. If the hostname cannot be resolved through /etc/hosts and
if the nameserver is not reachable, long delays are incurred during
keepalived startup and configuration reload, causing VRRP state flapping
and dropped traffic over floating IPs.

Setting router_id and notification_email_from in the keepalived
configuration avoids unnecessary DNS lookups. However, this solution
is only effective with keepalived >= 1.2.17. Older versions still
exhibit the same problem with or without this patch.

Closes-Bug: #1511722
Change-Id: If6e31d164bd6ade52997bc0073ef50cdbc99ec93
(cherry picked from commit b3af52e738)
This commit is contained in:
Stefan Nica 2016-07-17 16:36:08 +03:00 committed by Miguel Angel Ajo
parent 20bb7be828
commit 58180e6d90
3 changed files with 157 additions and 130 deletions

View File

@ -31,6 +31,8 @@ VALID_AUTH_TYPES = ['AH', 'PASS']
HA_DEFAULT_PRIORITY = 50
PRIMARY_VIP_RANGE_SIZE = 24
KEEPALIVED_SERVICE_NAME = 'keepalived'
KEEPALIVED_EMAIL_FROM = 'neutron@openstack.local'
KEEPALIVED_ROUTER_ID = 'neutron'
GARP_MASTER_DELAY = 60
LOG = logging.getLogger(__name__)
@ -317,7 +319,11 @@ class KeepalivedConf(object):
return self.instances.get(vrouter_id)
def build_config(self):
config = []
config = ['global_defs {',
' notification_email_from %s' % KEEPALIVED_EMAIL_FROM,
' router_id %s' % KEEPALIVED_ROUTER_ID,
'}'
]
for instance in self.instances.values():
config.extend(instance.build_config())

View File

@ -22,6 +22,7 @@ from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import uuidutils
import testtools
import textwrap
from neutron.agent.common import config as agent_config
from neutron.agent.common import ovs_lib
@ -29,6 +30,7 @@ from neutron.agent.l3 import agent as neutron_l3_agent
from neutron.agent import l3_agent as l3_agent_main
from neutron.agent.linux import external_process
from neutron.agent.linux import ip_lib
from neutron.agent.linux import keepalived
from neutron.common import utils as common_utils
from neutron.conf import common as common_config
from neutron.tests.common import l3_test_common
@ -348,7 +350,7 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
ha_device_name = router.get_ha_device_name()
external_port = router.get_ex_gw_port()
ex_port_ipv6 = ip_lib.get_ipv6_lladdr(external_port['mac_address'])
external_device_name = router.get_external_device_name(
ex_device_name = router.get_external_device_name(
external_port['id'])
external_device_cidr = self._port_first_ip_cidr(external_port)
internal_port = router.router[constants.INTERFACE_KEY][0]
@ -360,35 +362,42 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
router.get_floating_ips()[0]['floating_ip_address'])
default_gateway_ip = external_port['subnets'][0].get('gateway_ip')
extra_subnet_cidr = external_port['extra_subnets'][0].get('cidr')
return """vrrp_instance VR_1 {
state BACKUP
interface %(ha_device_name)s
virtual_router_id 1
priority 50
garp_master_delay 60
nopreempt
advert_int 2
track_interface {
%(ha_device_name)s
}
virtual_ipaddress {
169.254.0.1/24 dev %(ha_device_name)s
}
virtual_ipaddress_excluded {
%(floating_ip_cidr)s dev %(external_device_name)s
%(external_device_cidr)s dev %(external_device_name)s
%(internal_device_cidr)s dev %(internal_device_name)s
%(ex_port_ipv6)s dev %(external_device_name)s scope link
%(int_port_ipv6)s dev %(internal_device_name)s scope link
}
virtual_routes {
0.0.0.0/0 via %(default_gateway_ip)s dev %(external_device_name)s
8.8.8.0/24 via 19.4.4.4
%(extra_subnet_cidr)s dev %(external_device_name)s scope link
}
}""" % {
return textwrap.dedent("""\
global_defs {
notification_email_from %(email_from)s
router_id %(router_id)s
}
vrrp_instance VR_1 {
state BACKUP
interface %(ha_device_name)s
virtual_router_id 1
priority 50
garp_master_delay 60
nopreempt
advert_int 2
track_interface {
%(ha_device_name)s
}
virtual_ipaddress {
169.254.0.1/24 dev %(ha_device_name)s
}
virtual_ipaddress_excluded {
%(floating_ip_cidr)s dev %(ex_device_name)s
%(external_device_cidr)s dev %(ex_device_name)s
%(internal_device_cidr)s dev %(internal_device_name)s
%(ex_port_ipv6)s dev %(ex_device_name)s scope link
%(int_port_ipv6)s dev %(internal_device_name)s scope link
}
virtual_routes {
0.0.0.0/0 via %(default_gateway_ip)s dev %(ex_device_name)s
8.8.8.0/24 via 19.4.4.4
%(extra_subnet_cidr)s dev %(ex_device_name)s scope link
}
}""") % {
'email_from': keepalived.KEEPALIVED_EMAIL_FROM,
'router_id': keepalived.KEEPALIVED_ROUTER_ID,
'ha_device_name': ha_device_name,
'external_device_name': external_device_name,
'ex_device_name': ex_device_name,
'external_device_cidr': external_device_cidr,
'internal_device_name': internal_device_name,
'internal_device_cidr': internal_device_cidr,

View File

@ -14,6 +14,7 @@
from neutron_lib import constants as n_consts
import testtools
import textwrap
from neutron.agent.linux import keepalived
from neutron.tests import base
@ -21,6 +22,14 @@ from neutron.tests import base
# Keepalived user guide:
# http://www.keepalived.org/pdf/UserGuide.pdf
KEEPALIVED_GLOBAL_CONFIG = textwrap.dedent("""\
global_defs {
notification_email_from %(email_from)s
router_id %(router_id)s
}""") % dict(
email_from=keepalived.KEEPALIVED_EMAIL_FROM,
router_id=keepalived.KEEPALIVED_ROUTER_ID)
class KeepalivedGetFreeRangeTestCase(base.BaseTestCase):
def test_get_free_range(self):
@ -110,52 +119,53 @@ class KeepalivedConfBaseMixin(object):
class KeepalivedConfTestCase(base.BaseTestCase,
KeepalivedConfBaseMixin):
expected = """vrrp_instance VR_1 {
state MASTER
interface eth0
virtual_router_id 1
priority 50
garp_master_delay 60
advert_int 5
authentication {
auth_type AH
auth_pass pass123
}
track_interface {
eth0
}
virtual_ipaddress {
169.254.0.1/24 dev eth0
}
virtual_ipaddress_excluded {
192.168.1.0/24 dev eth1
192.168.2.0/24 dev eth2
192.168.3.0/24 dev eth2
192.168.55.0/24 dev eth10
}
virtual_routes {
0.0.0.0/0 via 192.168.1.1 dev eth1
}
}
vrrp_instance VR_2 {
state MASTER
interface eth4
virtual_router_id 2
priority 50
garp_master_delay 60
mcast_src_ip 224.0.0.1
track_interface {
eth4
}
virtual_ipaddress {
169.254.0.2/24 dev eth4
}
virtual_ipaddress_excluded {
192.168.2.0/24 dev eth2
192.168.3.0/24 dev eth6
192.168.55.0/24 dev eth10
}
}"""
expected = KEEPALIVED_GLOBAL_CONFIG + textwrap.dedent("""
vrrp_instance VR_1 {
state MASTER
interface eth0
virtual_router_id 1
priority 50
garp_master_delay 60
advert_int 5
authentication {
auth_type AH
auth_pass pass123
}
track_interface {
eth0
}
virtual_ipaddress {
169.254.0.1/24 dev eth0
}
virtual_ipaddress_excluded {
192.168.1.0/24 dev eth1
192.168.2.0/24 dev eth2
192.168.3.0/24 dev eth2
192.168.55.0/24 dev eth10
}
virtual_routes {
0.0.0.0/0 via 192.168.1.1 dev eth1
}
}
vrrp_instance VR_2 {
state MASTER
interface eth4
virtual_router_id 2
priority 50
garp_master_delay 60
mcast_src_ip 224.0.0.1
track_interface {
eth4
}
virtual_ipaddress {
169.254.0.2/24 dev eth4
}
virtual_ipaddress_excluded {
192.168.2.0/24 dev eth2
192.168.3.0/24 dev eth6
192.168.55.0/24 dev eth10
}
}""")
def test_config_generation(self):
config = self._get_config()
@ -166,7 +176,7 @@ vrrp_instance VR_2 {
self.assertEqual(self.expected, config.get_config_str())
config.reset()
self.assertEqual('', config.get_config_str())
self.assertEqual(KEEPALIVED_GLOBAL_CONFIG, config.get_config_str())
def test_get_existing_vip_ip_addresses_returns_list(self):
config = self._get_config()
@ -246,63 +256,65 @@ class KeepalivedInstanceTestCase(base.BaseTestCase,
instance.remove_vips_vroutes_by_interface('eth2')
instance.remove_vips_vroutes_by_interface('eth10')
expected = """vrrp_instance VR_1 {
state MASTER
interface eth0
virtual_router_id 1
priority 50
garp_master_delay 60
advert_int 5
authentication {
auth_type AH
auth_pass pass123
}
track_interface {
eth0
}
virtual_ipaddress {
169.254.0.1/24 dev eth0
}
virtual_ipaddress_excluded {
192.168.1.0/24 dev eth1
}
virtual_routes {
0.0.0.0/0 via 192.168.1.1 dev eth1
}
}
vrrp_instance VR_2 {
state MASTER
interface eth4
virtual_router_id 2
priority 50
garp_master_delay 60
mcast_src_ip 224.0.0.1
track_interface {
eth4
}
virtual_ipaddress {
169.254.0.2/24 dev eth4
}
virtual_ipaddress_excluded {
192.168.2.0/24 dev eth2
192.168.3.0/24 dev eth6
192.168.55.0/24 dev eth10
}
}"""
expected = KEEPALIVED_GLOBAL_CONFIG + textwrap.dedent("""
vrrp_instance VR_1 {
state MASTER
interface eth0
virtual_router_id 1
priority 50
garp_master_delay 60
advert_int 5
authentication {
auth_type AH
auth_pass pass123
}
track_interface {
eth0
}
virtual_ipaddress {
169.254.0.1/24 dev eth0
}
virtual_ipaddress_excluded {
192.168.1.0/24 dev eth1
}
virtual_routes {
0.0.0.0/0 via 192.168.1.1 dev eth1
}
}
vrrp_instance VR_2 {
state MASTER
interface eth4
virtual_router_id 2
priority 50
garp_master_delay 60
mcast_src_ip 224.0.0.1
track_interface {
eth4
}
virtual_ipaddress {
169.254.0.2/24 dev eth4
}
virtual_ipaddress_excluded {
192.168.2.0/24 dev eth2
192.168.3.0/24 dev eth6
192.168.55.0/24 dev eth10
}
}""")
self.assertEqual(expected, config.get_config_str())
def test_build_config_no_vips(self):
expected = """vrrp_instance VR_1 {
state MASTER
interface eth0
virtual_router_id 1
priority 50
garp_master_delay 60
virtual_ipaddress {
169.254.0.1/24 dev eth0
}
}"""
expected = textwrap.dedent("""\
vrrp_instance VR_1 {
state MASTER
interface eth0
virtual_router_id 1
priority 50
garp_master_delay 60
virtual_ipaddress {
169.254.0.1/24 dev eth0
}
}""")
instance = keepalived.KeepalivedInstance(
'MASTER', 'eth0', 1, ['169.254.192.0/18'])
self.assertEqual(expected, '\n'.join(instance.build_config()))