From 7330c5badfa72ac2112389838f6e4b64d3f45e3c Mon Sep 17 00:00:00 2001 From: Gabriel Cocenza Date: Fri, 17 Feb 2023 15:27:55 -0300 Subject: [PATCH] Add support for HAProxy L7 checks This change add several configuration options to enable HTTP checks to the HAProxy configuration, instead of the default TCP connection checks (which continue to be the default). It also enables /healthcheck endpoint for neutron-api. Closes-Bug: #1880610 Change-Id: Ia820d8c2ca709d6b358b1c80d770624568d9a85b --- hooks/neutron_api_context.py | 12 ++++++++++++ templates/rocky/api-paste.ini | 6 ++++++ tox.ini | 2 +- unit_tests/test_neutron_api_context.py | 24 ++++++++++++++++++++---- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/hooks/neutron_api_context.py b/hooks/neutron_api_context.py index 2c2faaae..6577c092 100644 --- a/hooks/neutron_api_context.py +++ b/hooks/neutron_api_context.py @@ -33,6 +33,7 @@ from charmhelpers.contrib.openstack import context from charmhelpers.contrib.hahelpers.cluster import ( determine_api_port, determine_apache_port, + https, ) from charmhelpers.contrib.openstack.utils import ( os_release, @@ -762,6 +763,15 @@ class HAProxyContext(context.HAProxyContext): from neutron_api_utils import api_port ctxt = super(HAProxyContext, self).__call__() + healthcheck = [{ + 'option': 'httpchk GET /healthcheck', + 'http-check': 'expect status 200', + }] + + backend_options = { + 'neutron-server': healthcheck, + } + # Apache ports a_neutron_api = determine_apache_port(api_port('neutron-server'), singlenode_mode=True) @@ -778,6 +788,8 @@ class HAProxyContext(context.HAProxyContext): # for haproxy.conf ctxt['service_ports'] = port_mapping + ctxt['backend_options'] = backend_options + ctxt['https'] = https() return ctxt diff --git a/templates/rocky/api-paste.ini b/templates/rocky/api-paste.ini index e45e7a9e..711b63f5 100644 --- a/templates/rocky/api-paste.ini +++ b/templates/rocky/api-paste.ini @@ -1,6 +1,7 @@ [composite:neutron] use = egg:Paste#urlmap /: neutronversions_composite +/healthcheck: healthcheck /v2.0: neutronapi_v2_0 [composite:neutronapi_v2_0] @@ -50,3 +51,8 @@ paste.app_factory = neutron.api.v2.router:APIRouter.factory [filter:osprofiler] paste.filter_factory = osprofiler.web:WsgiMiddleware.factory + +[app:healthcheck] +paste.app_factory = oslo_middleware:Healthcheck.app_factory +backends = disable_by_file +disable_by_file_path = /var/lib/neutron/healthcheck_disable diff --git a/tox.ini b/tox.ini index ae4d124c..2cb6ca16 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,7 @@ setenv = VIRTUAL_ENV={envdir} commands = stestr run --slowest {posargs} allowlist_externals = charmcraft - rename.sh + {toxinidir}/rename.sh passenv = HOME TERM diff --git a/unit_tests/test_neutron_api_context.py b/unit_tests/test_neutron_api_context.py index 4be95c22..4ebb89e0 100644 --- a/unit_tests/test_neutron_api_context.py +++ b/unit_tests/test_neutron_api_context.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import collections import json from unittest.mock import MagicMock, patch @@ -30,6 +31,7 @@ TO_PATCH = [ 'relation_get', 'relation_ids', 'related_units', + 'https' ] @@ -377,6 +379,7 @@ class HAProxyContextTest(CharmTestCase): with patch('builtins.__import__'): self.assertTrue('units' not in hap_ctxt()) + @patch.object(charmhelpers.contrib.network.ip, 'is_ipv6_disabled') @patch.object(charmhelpers.contrib.openstack.context, 'get_relation_ip') @patch.object(charmhelpers.contrib.openstack.context, 'mkdir') @patch.object( @@ -395,11 +398,21 @@ class HAProxyContextTest(CharmTestCase): def test_context_peers(self, _open, _import, _kv, _log, _rids, _runits, _rget, _lunit, _config, _get_address_in_network, _get_netmask_for_address, - _mkdir, _get_relation_ip): - unit_addresses = { - 'neutron-api-0': '10.10.10.10', - 'neutron-api-1': '10.10.10.11', + _mkdir, _get_relation_ip, _is_ipv6_disabled): + healthcheck = [{ + 'option': 'httpchk GET /healthcheck', + 'http-check': 'expect status 200', + }] + backend_options = { + 'neutron-server': healthcheck, } + self.https.return_value = False + unit_addresses = collections.OrderedDict( + [ + ('neutron-api-1', '10.10.10.11'), + ('neutron-api-0', '10.10.10.10'), + ] + ) _rids.return_value = ['rid1'] _runits.return_value = ['neutron-api/0'] _rget.return_value = unit_addresses['neutron-api-0'] @@ -409,6 +422,7 @@ class HAProxyContextTest(CharmTestCase): _get_address_in_network.return_value = None _get_netmask_for_address.return_value = '255.255.255.0' _kv().get.return_value = 'abcdefghijklmnopqrstuvwxyz123456' + _is_ipv6_disabled.return_value = True service_ports = {'neutron-server': [9696, 9686]} ctxt_data = { 'local_host': '127.0.0.1', @@ -426,6 +440,8 @@ class HAProxyContextTest(CharmTestCase): 'service_ports': service_ports, 'neutron_bind_port': 9686, 'ipv6_enabled': True, + 'https': False, + 'backend_options': backend_options, } _import().api_port.return_value = 9696 hap_ctxt = context.HAProxyContext()