Fixed pool and members status with UDP loadbalancers

This commit fixes pool and members status when using UDP loadbalancers.

Story: 2005736
Task: 33394

Change-Id: I75cde3ff820f085aebbdffd1e40c5ff40f16835d
(cherry picked from commit 4decb6d53c)
This commit is contained in:
Gregory Thiemonge 2019-05-20 15:53:52 +02:00
parent 7f683dc2c5
commit bf50e3a14b
4 changed files with 128 additions and 19 deletions

View File

@ -35,6 +35,10 @@ V6_VS_REGEX = re.compile(r"virtual_server\s([\w*:]+\b)\s(\d{1,5})")
V6_RS_REGEX = re.compile(r"real_server\s([\w*:]+\b)\s(\d{1,5})")
CONFIG_COMMENT_REGEX = re.compile(
r"#\sConfiguration\sfor\s(\w+)\s(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})")
DISABLED_MEMBER_COMMENT_REGEX = re.compile(
r"#\sMember\s(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}) is disabled")
CHECKER_REGEX = re.compile(r"MISC_CHECK")
def read_kernel_file(ns_name, file_path):
@ -55,7 +59,8 @@ def read_kernel_file(ns_name, file_path):
return output
def get_listener_realserver_mapping(ns_name, listener_ip_port):
def get_listener_realserver_mapping(ns_name, listener_ip_port,
health_monitor_enabled):
# returned result:
# actual_member_result = {'rs_ip:listened_port': {
# 'status': 'UP',
@ -74,6 +79,11 @@ def get_listener_realserver_mapping(ns_name, listener_ip_port):
port_hex_format = "%.4X" % int(listener_port)
idex = ip_to_hex_format + ':' + port_hex_format
if health_monitor_enabled:
member_status = constants.UP
else:
member_status = constants.NO_CHECK
actual_member_result = {}
find_target_block = False
result_keys = []
@ -111,7 +121,7 @@ def get_listener_realserver_mapping(ns_name, listener_ip_port):
for index in range(result_key_count):
if member_ip_port_string not in actual_member_result:
actual_member_result[
member_ip_port_string] = {'status': constants.UP,
member_ip_port_string] = {'status': member_status,
result_keys[index]:
result_values[index]}
else:
@ -171,6 +181,16 @@ def get_udp_listener_resource_ipports_nsname(listener_id):
elif resource_type == 'Members':
resource_ipport_mapping[resource_type].append(value)
disabled_member_ids = DISABLED_MEMBER_COMMENT_REGEX.findall(cfg)
resource_type = 'Members'
for member_id in disabled_member_ids:
value = {'id': member_id,
'ipport': None}
if resource_type not in resource_ipport_mapping:
resource_ipport_mapping[resource_type] = []
resource_ipport_mapping[resource_type].append(value)
if rs_ip_port_list:
rs_ip_port_count = len(rs_ip_port_list)
for index in range(rs_ip_port_count):
@ -205,8 +225,13 @@ def get_udp_listener_pool_status(listener_id):
'members': {}
}}
with open(util.keepalived_lvs_cfg_path(listener_id), 'r') as f:
cfg = f.read()
hm_enabled = len(CHECKER_REGEX.findall(cfg)) > 0
_, realserver_result = get_listener_realserver_mapping(
ns_name, resource_ipport_mapping['Listener']['ipport'])
ns_name, resource_ipport_mapping['Listener']['ipport'],
hm_enabled)
pool_status = constants.UP
member_results = {}
if realserver_result:
@ -220,7 +245,9 @@ def get_udp_listener_pool_status(listener_id):
for member in resource_ipport_mapping['Members']:
if member['ipport'] == member_ip_port:
member_id = member['id']
if member_ip_port in down_member_ip_port_set:
if member_ip_port is None:
status = constants.MAINT
elif member_ip_port in down_member_ip_port_set:
status = constants.DOWN
elif int(realserver_result[member_ip_port]['Weight']) == 0:
status = constants.DRAIN
@ -230,9 +257,16 @@ def get_udp_listener_pool_status(listener_id):
if member_id:
member_results[member_id] = status
else:
pool_status = constants.DOWN
if hm_enabled:
pool_status = constants.DOWN
for member in resource_ipport_mapping['Members']:
member_results[member['id']] = constants.DOWN
if member['ipport'] is None:
member_results[member['id']] = constants.MAINT
elif hm_enabled:
member_results[member['id']] = constants.DOWN
else:
member_results[member['id']] = constants.NO_CHECK
return {
'lvs':

View File

@ -44,6 +44,7 @@ MISC_CHECK {
{% endmacro %}
{% macro realserver_macro(constants, pool, member, listener) %}
{% if member.enabled %}
# Configuration for Member {{ member.id }}
real_server {{ member.address }} {{ member.protocol_port }} {
weight {{ member.weight }}
@ -52,6 +53,9 @@ MISC_CHECK {
{% endif %}
{{- health_monitor_rs_macro(constants, pool, member) }}
}
{% else %}
# Member {{ member.id }} is disabled
{% endif %}
{% endmacro %}
{% macro health_monitor_vs_macro(default_pool) %}
@ -71,7 +75,7 @@ MISC_CHECK {
{% macro virtualserver_macro(constants, listener, lb_vip_address, default_pool) %}
{% set need_render = [] %}
{% if default_pool and default_pool.enabled and default_pool.members %}
{% for member in default_pool.members if member.enabled %}
{% for member in default_pool.members %}
{% do need_render.append(member.enabled) %}
{% endfor %}
{% endif %}
@ -101,7 +105,7 @@ virtual_server {{ lb_vip_address }} {{ listener.protocol_port }} {
{% if default_pool.health_monitor and default_pool.health_monitor.enabled %}
# Configuration for HealthMonitor {{ default_pool.health_monitor.id }}
{% endif %}
{% for member in default_pool.members if member.enabled %}
{% for member in default_pool.members %}
{{- realserver_macro(constants, default_pool, member, listener) }}
{% endfor %}
{% endif %}

View File

@ -22,6 +22,7 @@ from octavia.tests.unit import base
# Kernal_file_sample which is in /proc/net/ip_vs
# The realservers and the listened ports are
# 10.0.0.25:2222, 10.0.0.35:3333.
# Realserver 10.0.0.45:4444 is not listed because healthcheck failed.
# The virtual server and the listened port is
# 10.0.0.37:7777.
KERNAL_FILE_SAMPLE_V4 = (
@ -35,6 +36,8 @@ KERNAL_FILE_SAMPLE_V4 = (
# The realservers and the listened ports are
# [fd79:35e2:9963:0:f816:3eff:feca:b7bf]:2222,
# [fd79:35e2:9963:0:f816:3eff:fe9d:94df]:3333.
# Readlserver [fd79:35e2:9963:0:f816:3eff:fe9d:8f3f]:4444 is not listed
# because healthcheck failed.
# The virtual server and the listened port is
# [fd79:35e2:9963:0:f816:3eff:fe6d:7a2a]:7777.
KERNAL_FILE_SAMPLE_V6 = (
@ -60,13 +63,32 @@ CFG_FILE_TEMPLATE_v4 = (
" weight 3\n"
" persistence_timeout 5\n"
" persistence_granularity 255.0.0.0\n\n"
" MISC_CHECK {\n\n"
" misc_path \"/usr/bin/check_script.sh\"\n\n"
" misc_timeout 5\n\n"
" }\n\n"
" }\n\n"
" # Configuration for Member %(member_id2)s\n"
" real_server 10.0.0.35 3333 {\n"
" weight 2\n"
" persistence_timeout 5\n"
" persistence_granularity 255.0.0.0\n\n"
" MISC_CHECK {\n\n"
" misc_path \"/usr/bin/check_script.sh\"\n\n"
" misc_timeout 5\n\n"
" }\n\n"
" }\n\n"
" # Configuration for Member %(member_id3)s\n"
" real_server 10.0.0.45 4444 {\n"
" weight 2\n"
" persistence_timeout 5\n"
" persistence_granularity 255.0.0.0\n\n"
" MISC_CHECK {\n\n"
" misc_path \"/usr/bin/check_script.sh\"\n\n"
" misc_timeout 5\n\n"
" }\n\n"
" }\n\n"
" # Member %(member_id4)s is disabled\n\n"
"}")
CFG_FILE_TEMPLATE_v6 = (
@ -80,11 +102,28 @@ CFG_FILE_TEMPLATE_v6 = (
" # Configuration for Member %(member_id1)s\n"
" real_server fd79:35e2:9963:0:f816:3eff:feca:b7bf 2222 {\n"
" weight 3\n"
" MISC_CHECK {\n\n"
" misc_path \"/usr/bin/check_script.sh\"\n\n"
" misc_timeout 5\n\n"
" }\n\n"
" }\n\n"
" # Configuration for Member %(member_id2)s\n"
" real_server fd79:35e2:9963:0:f816:3eff:fe9d:94df 3333 {\n"
" weight 2\n"
" MISC_CHECK {\n\n"
" misc_path \"/usr/bin/check_script.sh\"\n\n"
" misc_timeout 5\n\n"
" }\n\n"
" }\n\n"
" # Configuration for Member %(member_id3)s\n"
" real_server fd79:35e2:9963:0:f816:3eff:fe9d:8f3f 4444 {\n"
" weight 2\n"
" MISC_CHECK {\n\n"
" misc_path \"/usr/bin/check_script.sh\"\n\n"
" misc_timeout 5\n\n"
" }\n\n"
" }\n\n"
" # Member %(member_id4)s is disabled\n\n"
"}")
IPVSADM_OUTPUT_TEMPLATE = (
@ -115,23 +154,31 @@ class LvsQueryTestCase(base.TestCase):
self.pool_id_v4 = uuidutils.generate_uuid()
self.member_id1_v4 = uuidutils.generate_uuid()
self.member_id2_v4 = uuidutils.generate_uuid()
self.member_id3_v4 = uuidutils.generate_uuid()
self.member_id4_v4 = uuidutils.generate_uuid()
self.listener_id_v6 = uuidutils.generate_uuid()
self.pool_id_v6 = uuidutils.generate_uuid()
self.member_id1_v6 = uuidutils.generate_uuid()
self.member_id2_v6 = uuidutils.generate_uuid()
self.member_id3_v6 = uuidutils.generate_uuid()
self.member_id4_v6 = uuidutils.generate_uuid()
cfg_content_v4 = CFG_FILE_TEMPLATE_v4 % {
'listener_id': self.listener_id_v4,
'ns_name': constants.AMPHORA_NAMESPACE,
'pool_id': self.pool_id_v4,
'member_id1': self.member_id1_v4,
'member_id2': self.member_id2_v4
'member_id2': self.member_id2_v4,
'member_id3': self.member_id3_v4,
'member_id4': self.member_id4_v4,
}
cfg_content_v6 = CFG_FILE_TEMPLATE_v6 % {
'listener_id': self.listener_id_v6,
'ns_name': constants.AMPHORA_NAMESPACE,
'pool_id': self.pool_id_v6,
'member_id1': self.member_id1_v6,
'member_id2': self.member_id2_v6
'member_id2': self.member_id2_v6,
'member_id3': self.member_id3_v6,
'member_id4': self.member_id4_v6
}
self.useFixture(test_utils.OpenFixture(
util.keepalived_lvs_cfg_path(self.listener_id_v4), cfg_content_v4))
@ -145,7 +192,8 @@ class LvsQueryTestCase(base.TestCase):
target_ns = constants.AMPHORA_NAMESPACE
mock_check_output.return_value = KERNAL_FILE_SAMPLE_V4
result = lvs_query.get_listener_realserver_mapping(
target_ns, input_listener_ip_port)
target_ns, input_listener_ip_port,
health_monitor_enabled=True)
expected = {'10.0.0.25:2222': {'status': 'UP',
'Forward': 'Masq',
'Weight': '3',
@ -162,7 +210,8 @@ class LvsQueryTestCase(base.TestCase):
input_listener_ip_port = '[fd79:35e2:9963:0:f816:3eff:fe6d:7a2a]:7777'
mock_check_output.return_value = KERNAL_FILE_SAMPLE_V6
result = lvs_query.get_listener_realserver_mapping(
target_ns, input_listener_ip_port)
target_ns, input_listener_ip_port,
health_monitor_enabled=True)
expected = {'[fd79:35e2:9963:0:f816:3eff:feca:b7bf]:2222':
{'status': constants.UP,
'Forward': 'Masq',
@ -181,7 +230,8 @@ class LvsQueryTestCase(base.TestCase):
mock_check_output.return_value = KERNAL_FILE_SAMPLE_V4
for listener_ip_port in ['10.0.0.37:7776', '10.0.0.31:7777']:
result = lvs_query.get_listener_realserver_mapping(
target_ns, listener_ip_port)
target_ns, listener_ip_port,
health_monitor_enabled=True)
self.assertEqual((False, {}), result)
mock_check_output.return_value = KERNAL_FILE_SAMPLE_V6
@ -189,7 +239,8 @@ class LvsQueryTestCase(base.TestCase):
'[fd79:35e2:9963:0:f816:3eff:fe6d:7a2a]:7776',
'[fd79:35e2:9973:0:f816:3eff:fe6d:7a2a]:7777']:
result = lvs_query.get_listener_realserver_mapping(
target_ns, listener_ip_port)
target_ns, listener_ip_port,
health_monitor_enabled=True)
self.assertEqual((False, {}), result)
def test_get_udp_listener_resource_ipports_nsname(self):
@ -202,7 +253,11 @@ class LvsQueryTestCase(base.TestCase):
'Members': [{'id': self.member_id1_v4,
'ipport': '10.0.0.25:2222'},
{'id': self.member_id2_v4,
'ipport': '10.0.0.35:3333'}]}
'ipport': '10.0.0.35:3333'},
{'id': self.member_id3_v4,
'ipport': '10.0.0.45:4444'},
{'id': self.member_id4_v4,
'ipport': None}]}
self.assertEqual((expected, constants.AMPHORA_NAMESPACE), res)
# ipv6
@ -216,7 +271,11 @@ class LvsQueryTestCase(base.TestCase):
{'id': self.member_id1_v6,
'ipport': '[fd79:35e2:9963:0:f816:3eff:feca:b7bf]:2222'},
{'id': self.member_id2_v6,
'ipport': '[fd79:35e2:9963:0:f816:3eff:fe9d:94df]:3333'}]}
'ipport': '[fd79:35e2:9963:0:f816:3eff:fe9d:94df]:3333'},
{'id': self.member_id3_v6,
'ipport': '[fd79:35e2:9963:0:f816:3eff:fe9d:8f3f]:4444'},
{'id': self.member_id4_v6,
'ipport': None}]}
self.assertEqual((expected, constants.AMPHORA_NAMESPACE), res)
@mock.patch('subprocess.check_output')
@ -229,7 +288,9 @@ class LvsQueryTestCase(base.TestCase):
{'uuid': self.pool_id_v4,
'status': constants.UP,
'members': {self.member_id1_v4: constants.UP,
self.member_id2_v4: constants.UP}}}
self.member_id2_v4: constants.UP,
self.member_id3_v4: constants.DOWN,
self.member_id4_v4: constants.MAINT}}}
self.assertEqual(expected, res)
mock_check_output.return_value = KERNAL_FILE_SAMPLE_V6
@ -239,7 +300,9 @@ class LvsQueryTestCase(base.TestCase):
{'uuid': self.pool_id_v6,
'status': constants.UP,
'members': {self.member_id1_v6: constants.UP,
self.member_id2_v6: constants.UP}}}
self.member_id2_v6: constants.UP,
self.member_id3_v6: constants.DOWN,
self.member_id4_v6: constants.MAINT}}}
self.assertEqual(expected, res)
@mock.patch('octavia.amphorae.backends.utils.keepalivedlvs_query.'
@ -298,7 +361,9 @@ class LvsQueryTestCase(base.TestCase):
{'uuid': self.pool_id_v4,
'status': constants.DOWN,
'members': {self.member_id1_v4: constants.DOWN,
self.member_id2_v4: constants.DOWN}}}
self.member_id2_v4: constants.DOWN,
self.member_id3_v4: constants.DOWN,
self.member_id4_v4: constants.MAINT}}}
self.assertEqual(expected, res)
@mock.patch('subprocess.check_output')

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fix operating_status for pools and members that use UDP protocol.
operating_status values are now consistant with the values of non-UDP
load balancers.