diff --git a/octavia/amphorae/backends/utils/keepalivedlvs_query.py b/octavia/amphorae/backends/utils/keepalivedlvs_query.py index 1a36f1b5ca..777910a5ad 100644 --- a/octavia/amphorae/backends/utils/keepalivedlvs_query.py +++ b/octavia/amphorae/backends/utils/keepalivedlvs_query.py @@ -35,8 +35,8 @@ 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") +DISABLED_CONFIG_COMMENT_REGEX = re.compile( + r"#\s(\w+)\s(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}) is disabled") CHECKER_REGEX = re.compile(r"MISC_CHECK") @@ -152,6 +152,14 @@ def get_udp_listener_resource_ipports_nsname(listener_id): listener_ip_port = V6_VS_REGEX.findall(cfg) listener_ip_port = listener_ip_port[0] if listener_ip_port else [] + disabled_resource_ids = DISABLED_CONFIG_COMMENT_REGEX.findall(cfg) + + listener_disabled = any(True + for resource in disabled_resource_ids + if resource[0] == 'Listener') + if listener_disabled: + return None, ns_name + if not listener_ip_port: # If not get listener_ip_port from the lvs config file, # that means the udp listener's default pool have no enabled member @@ -181,7 +189,11 @@ 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) + disabled_member_ids = [ + resource[1] + for resource in disabled_resource_ids + if resource[0] == 'Member' + ] resource_type = 'Members' for member_id in disabled_member_ids: @@ -363,6 +375,10 @@ def get_udp_listeners_stats(): (resource_ipport_mapping, ns_name) = get_udp_listener_resource_ipports_nsname(check_listener_id) + # Listener is disabled, we don't need to send an update + if resource_ipport_mapping is None: + continue + # Since we found the keepalived running, acknowledge the listener # in the heartbeat. If this listener has a pool and members, # the stats will be updated later in the code flow. diff --git a/octavia/common/jinja/lvs/templates/base.j2 b/octavia/common/jinja/lvs/templates/base.j2 index 6ee5016da9..b3a5d71773 100644 --- a/octavia/common/jinja/lvs/templates/base.j2 +++ b/octavia/common/jinja/lvs/templates/base.j2 @@ -14,7 +14,11 @@ # #} # Configuration for Loadbalancer {{ loadbalancer.id }} +{% if loadbalancer.listener.enabled %} # Configuration for Listener {{ udp_listener_id }} +{% else %} +# Listener {{ udp_listener_id }} is disabled +{% endif %} {% block global_definitions %}{% endblock global_definitions %} diff --git a/octavia/tests/unit/amphorae/backends/utils/test_keepalivedlvs_query.py b/octavia/tests/unit/amphorae/backends/utils/test_keepalivedlvs_query.py index 0ed767f644..06f06f090d 100644 --- a/octavia/tests/unit/amphorae/backends/utils/test_keepalivedlvs_query.py +++ b/octavia/tests/unit/amphorae/backends/utils/test_keepalivedlvs_query.py @@ -126,6 +126,11 @@ CFG_FILE_TEMPLATE_v6 = ( " # Member %(member_id4)s is disabled\n\n" "}") +CFG_FILE_TEMPLATE_DISABLED_LISTENER = ( + "# Listener %(listener_id)s is disabled \n\n" + "net_namespace %(ns_name)s\n\n" +) + IPVSADM_OUTPUT_TEMPLATE = ( "IP Virtual Server version 1.2.1 (size=4096)\n" "Prot LocalAddress:Port Scheduler Flags\n" @@ -162,6 +167,7 @@ class LvsQueryTestCase(base.TestCase): self.member_id2_v6 = uuidutils.generate_uuid() self.member_id3_v6 = uuidutils.generate_uuid() self.member_id4_v6 = uuidutils.generate_uuid() + self.disabled_listener_id = uuidutils.generate_uuid() cfg_content_v4 = CFG_FILE_TEMPLATE_v4 % { 'listener_id': self.listener_id_v4, 'ns_name': constants.AMPHORA_NAMESPACE, @@ -180,10 +186,19 @@ class LvsQueryTestCase(base.TestCase): 'member_id3': self.member_id3_v6, 'member_id4': self.member_id4_v6 } + cfg_content_disabled_listener = ( + CFG_FILE_TEMPLATE_DISABLED_LISTENER % { + 'listener_id': self.listener_id_v6, + 'ns_name': constants.AMPHORA_NAMESPACE, + } + ) self.useFixture(test_utils.OpenFixture( util.keepalived_lvs_cfg_path(self.listener_id_v4), cfg_content_v4)) self.useFixture(test_utils.OpenFixture( util.keepalived_lvs_cfg_path(self.listener_id_v6), cfg_content_v6)) + self.useFixture(test_utils.OpenFixture( + util.keepalived_lvs_cfg_path(self.disabled_listener_id), + cfg_content_disabled_listener)) @mock.patch('subprocess.check_output') def test_get_listener_realserver_mapping(self, mock_check_output): @@ -278,6 +293,11 @@ class LvsQueryTestCase(base.TestCase): 'ipport': None}]} self.assertEqual((expected, constants.AMPHORA_NAMESPACE), res) + # disabled + res = lvs_query.get_udp_listener_resource_ipports_nsname( + self.disabled_listener_id) + self.assertEqual((None, constants.AMPHORA_NAMESPACE), res) + @mock.patch('subprocess.check_output') def test_get_udp_listener_pool_status(self, mock_check_output): # test with ipv4 and ipv6 @@ -456,3 +476,14 @@ class LvsQueryTestCase(base.TestCase): mock_is_running.return_value = False res = lvs_query.get_udp_listeners_stats() self.assertEqual({}, res) + + @mock.patch('subprocess.check_output') + @mock.patch("octavia.amphorae.backends.agent.api_server.util." + "is_udp_listener_running", return_value=True) + @mock.patch("octavia.amphorae.backends.agent.api_server.util." + "get_udp_listeners") + def test_get_udp_listeners_stats_disabled_listener( + self, mock_get_listener, mock_is_running, mock_check_output): + mock_get_listener.return_value = [self.disabled_listener_id] + res = lvs_query.get_udp_listeners_stats() + self.assertEqual({}, res) diff --git a/octavia/tests/unit/common/jinja/lvs/test_lvs_jinja_cfg.py b/octavia/tests/unit/common/jinja/lvs/test_lvs_jinja_cfg.py index 83a0c3bd71..35a9dbc8d9 100644 --- a/octavia/tests/unit/common/jinja/lvs/test_lvs_jinja_cfg.py +++ b/octavia/tests/unit/common/jinja/lvs/test_lvs_jinja_cfg.py @@ -297,3 +297,18 @@ class TestLvsCfg(base.TestCase): ret = self.udp_jinja_cfg._transform_listener(in_listener) sample_configs_combined.RET_UDP_LISTENER.pop('connection_limit') self.assertEqual(sample_configs_combined.RET_UDP_LISTENER, ret) + + def test_render_template_disabled_udp_listener(self): + exp = ("# Configuration for Loadbalancer sample_loadbalancer_id_1\n" + "# Listener sample_listener_id_1 is disabled\n\n" + "net_namespace amphora-haproxy\n\n") + rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj( + sample_configs_combined.sample_listener_tuple( + enabled=False, + proto=constants.PROTOCOL_UDP, + persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP, + persistence_timeout=33, + persistence_granularity='255.255.0.0', + monitor_proto=constants.HEALTH_MONITOR_UDP_CONNECT, + connection_limit=98)) + self.assertEqual(exp, rendered_obj) diff --git a/releasenotes/notes/fix-disable-udp-listener-status-3d34a5596e62da1c.yaml b/releasenotes/notes/fix-disable-udp-listener-status-3d34a5596e62da1c.yaml new file mode 100644 index 0000000000..f2cb52425c --- /dev/null +++ b/releasenotes/notes/fix-disable-udp-listener-status-3d34a5596e62da1c.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fix operational status for disabled UDP listeners. The operating status of + disabled UDP listeners is now OFFLINE instead of ONLINE, the behavior is now + similary to the behavior of HTTP/HTTPS/TCP/... listeners.