From a792d8906c6c39a1097e9c8c0ab9fb0ce5aedbe3 Mon Sep 17 00:00:00 2001 From: Dustin Lundquist Date: Wed, 13 Jul 2016 13:54:44 -0700 Subject: [PATCH] haproxy ns driver: set global haproxy maxconn Haproxy uses two separate maxconn values: the maxconn setting in the global context is used to adjust file descriptor limits and allocate resources, while the maxconn setting in the frontend context controls if additional connections are accepted on that frontend. This patch adds a maxconn to the global context equal to the sum of the connection limit on each listener. For listeners which no connection limit specified, the haproxy default global connection limit (2000 connections) is used. This way we provide the same behavior as a default haproxy configuraiton which no connection limit specified in the case of a single load balancer. Change-Id: Icc63771ed15a7f718dab0389c1fb63d628896e0a Partial-Bug: 1544861 --- neutron_lbaas/drivers/haproxy/jinja_cfg.py | 11 +++++++++++ .../drivers/haproxy/templates/haproxy.loadbalancer.j2 | 1 + .../drivers/haproxy/templates/haproxy_base.j2 | 1 + .../drivers/haproxy/sample_configs/sample_configs.py | 9 +++++++-- .../tests/unit/drivers/haproxy/test_jinja_cfg.py | 8 ++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/neutron_lbaas/drivers/haproxy/jinja_cfg.py b/neutron_lbaas/drivers/haproxy/jinja_cfg.py index 5b8c36a39..a18ed277e 100644 --- a/neutron_lbaas/drivers/haproxy/jinja_cfg.py +++ b/neutron_lbaas/drivers/haproxy/jinja_cfg.py @@ -248,14 +248,25 @@ def _transform_loadbalancer(loadbalancer, haproxy_base_dir): listeners = [_transform_listener(x, haproxy_base_dir) for x in loadbalancer.listeners if x.admin_state_up] pools = [_transform_pool(x) for x in loadbalancer.pools] + connection_limit = _compute_global_connection_limit(listeners) return { 'name': loadbalancer.name, 'vip_address': loadbalancer.vip_address, + 'connection_limit': connection_limit, 'listeners': listeners, 'pools': pools } +def _compute_global_connection_limit(listeners): + # NOTE(dlundquist): HAProxy has a global default connection limit + # of 2000, so we will include 2000 connections for each listener + # without a connection limit specified. This way we provide the + # same behavior as a default haproxy configuration without + # connection limit specified in the case of a single load balancer. + return sum([x.get('connection_limit', 2000) for x in listeners]) + + def _transform_listener(listener, haproxy_base_dir): """Transforms listener object diff --git a/neutron_lbaas/drivers/haproxy/templates/haproxy.loadbalancer.j2 b/neutron_lbaas/drivers/haproxy/templates/haproxy.loadbalancer.j2 index 21250d6fa..2cedb326e 100644 --- a/neutron_lbaas/drivers/haproxy/templates/haproxy.loadbalancer.j2 +++ b/neutron_lbaas/drivers/haproxy/templates/haproxy.loadbalancer.j2 @@ -17,6 +17,7 @@ {% set loadbalancer_name = loadbalancer.name %} {% set usergroup = user_group %} {% set sock_path = stats_sock %} +{% set connection_limit = loadbalancer.connection_limit %} {% block proxies %} {% from 'haproxy_proxies.j2' import frontend_macro as frontend_macro, backend_macro%} diff --git a/neutron_lbaas/drivers/haproxy/templates/haproxy_base.j2 b/neutron_lbaas/drivers/haproxy/templates/haproxy_base.j2 index e405c959b..830580140 100644 --- a/neutron_lbaas/drivers/haproxy/templates/haproxy_base.j2 +++ b/neutron_lbaas/drivers/haproxy/templates/haproxy_base.j2 @@ -20,6 +20,7 @@ global group {{ usergroup }} log /dev/log local0 log /dev/log local1 notice + maxconn {{ connection_limit }} stats socket {{ sock_path }} mode 0666 level user defaults diff --git a/neutron_lbaas/tests/unit/drivers/haproxy/sample_configs/sample_configs.py b/neutron_lbaas/tests/unit/drivers/haproxy/sample_configs/sample_configs.py index c23176b6f..dbb3bc7a6 100644 --- a/neutron_lbaas/tests/unit/drivers/haproxy/sample_configs/sample_configs.py +++ b/neutron_lbaas/tests/unit/drivers/haproxy/sample_configs/sample_configs.py @@ -102,18 +102,21 @@ RET_LB = { 'name': 'test-lb', 'vip_address': '10.0.0.2', 'listeners': [RET_LISTENER], + 'connection_limit': RET_LISTENER['connection_limit'], 'pools': [RET_POOL]} RET_LB_TLS = { 'name': 'test-lb', 'vip_address': '10.0.0.2', 'listeners': [RET_LISTENER_TLS], + 'connection_limit': RET_LISTENER_TLS['connection_limit'], 'pools': [RET_POOL]} RET_LB_TLS_SNI = { 'name': 'test-lb', 'vip_address': '10.0.0.2', 'listeners': [RET_LISTENER_TLS_SNI], + 'connection_limit': RET_LISTENER_TLS_SNI['connection_limit'], 'pools': [RET_POOL]} @@ -148,7 +151,8 @@ def sample_vip_port_tuple(): def sample_listener_tuple(proto=None, monitor=True, persistence=True, - persistence_type=None, tls=False, sni=False): + persistence_type=None, tls=False, sni=False, + connection_limit=98): proto = 'HTTP' if proto is None else proto port = '443' if proto is 'HTTPS' or proto is 'TERMINATED_HTTPS' else '80' in_listener = collections.namedtuple( @@ -164,7 +168,7 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True, default_pool=sample_pool_tuple( proto=proto, monitor=monitor, persistence=persistence, persistence_type=persistence_type), - connection_limit=98, + connection_limit=connection_limit, admin_state_up=True, default_tls_container_id='cont_id_1' if tls else '', sni_container_ids=['cont_id_2', 'cont_id_3'] if sni else [], @@ -284,6 +288,7 @@ def sample_base_expected_config(backend, frontend=None): " group nogroup\n" " log /dev/log local0\n" " log /dev/log local1 notice\n" + " maxconn 98\n" " stats socket /sock_path mode 0666 level user\n\n" "defaults\n" " log global\n" diff --git a/neutron_lbaas/tests/unit/drivers/haproxy/test_jinja_cfg.py b/neutron_lbaas/tests/unit/drivers/haproxy/test_jinja_cfg.py index b06bb8fe9..f384e529f 100644 --- a/neutron_lbaas/tests/unit/drivers/haproxy/test_jinja_cfg.py +++ b/neutron_lbaas/tests/unit/drivers/haproxy/test_jinja_cfg.py @@ -447,6 +447,14 @@ class TestHaproxyCfg(base.BaseTestCase): ret = jinja_cfg._transform_loadbalancer(in_lb, '/v2') self.assertEqual(sample_configs.RET_LB, ret) + def test_compute_global_connection_limit(self): + lts = [ + sample_configs.sample_listener_tuple(connection_limit=None), + sample_configs.sample_listener_tuple()] + in_listeners = [jinja_cfg._transform_listener(x, '/v2') for x in lts] + ret = jinja_cfg._compute_global_connection_limit(in_listeners) + self.assertEqual(2098, ret) + def test_include_member(self): ret = jinja_cfg._include_member( sample_configs.sample_member_tuple('sample_member_id_1',