From bb0447e98b362b388a18b7ecd9ee0264bc5743c0 Mon Sep 17 00:00:00 2001 From: Adam Harwell Date: Fri, 23 Mar 2018 05:58:12 +0900 Subject: [PATCH] Expose timeout options Various timeout options need to be exposed to enable use-cases more complex than standard HTTP requests. In this patch we expose four new timeout values: * timeout_client_data * timeout_member_connect * timeout_member_data * timeout_tcp_inspect Change-Id: Id4667201c1bfaa06f7af9060c936ba00c2f314f9 Story: 1457556 Task: 5453 --- api-ref/source/parameters.yaml | 50 +++++++ .../source/v2/examples/listener-create-curl | 2 +- .../v2/examples/listener-create-request.json | 6 +- .../v2/examples/listener-create-response.json | 6 +- .../v2/examples/listener-show-response.json | 6 +- .../source/v2/examples/listener-update-curl | 2 +- .../v2/examples/listener-update-request.json | 6 +- .../v2/examples/listener-update-response.json | 6 +- .../v2/examples/listeners-list-response.json | 6 +- api-ref/source/v2/listener.inc | 24 ++++ octavia/api/root_controller.py | 2 +- octavia/api/v2/types/listener.py | 48 +++++++ octavia/common/constants.py | 9 ++ octavia/common/data_models.py | 8 +- octavia/common/jinja/haproxy/jinja_cfg.py | 27 ++-- .../common/jinja/haproxy/templates/base.j2 | 3 - .../common/jinja/haproxy/templates/macros.j2 | 6 + ...c131923f_add_timeout_fields_to_listener.py | 50 +++++++ octavia/db/models.py | 4 + .../tests/functional/api/v2/test_listener.py | 63 ++++++++- .../functional/api/v2/test_load_balancer.py | 6 +- octavia/tests/functional/db/test_models.py | 29 ++++ .../api_server/test_haproxy_compatibility.py | 16 ++- .../common/jinja/haproxy/test_jinja_cfg.py | 125 +++++++++++++++++- .../common/sample_configs/sample_configs.py | 56 +++++--- ...tener-timeout-values-9a7600c4e21364e3.yaml | 11 ++ 26 files changed, 512 insertions(+), 65 deletions(-) create mode 100644 octavia/db/migration/alembic_migrations/versions/0fd2c131923f_add_timeout_fields_to_listener.py create mode 100644 releasenotes/notes/Allow-configuration-of-listener-timeout-values-9a7600c4e21364e3.yaml diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 33b441c078..2697e13426 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -1029,6 +1029,56 @@ subnet_id-optional: in: body required: false type: uuid +timeout_client_data: + description: | + Frontend client inactivity timeout in milliseconds. Default: 50000. + in: body + required: true + type: integer +timeout_client_data-optional: + description: | + Frontend client inactivity timeout in milliseconds. Default: 50000. + in: body + required: false + type: integer +timeout_member_connect: + description: | + Backend member connection timeout in milliseconds. Default: 5000. + in: body + required: true + type: integer +timeout_member_connect-optional: + description: | + Backend member connection timeout in milliseconds. Default: 5000. + in: body + required: false + type: integer +timeout_member_data: + description: | + Backend member inactivity timeout in milliseconds. Default: 50000. + in: body + required: true + type: integer +timeout_member_data-optional: + description: | + Backend member inactivity timeout in milliseconds. Default: 50000. + in: body + required: false + type: integer +timeout_tcp_inspect: + description: | + Time, in milliseconds, to wait for additional TCP packets for content + inspection. Default: 0. + in: body + required: true + type: integer +timeout_tcp_inspect-optional: + description: | + Time, in milliseconds, to wait for additional TCP packets for content + inspection. Default: 0. + in: body + required: false + type: integer total_connections: description: | The total connections handled. diff --git a/api-ref/source/v2/examples/listener-create-curl b/api-ref/source/v2/examples/listener-create-curl index 88b3376a15..5ce5810e18 100644 --- a/api-ref/source/v2/examples/listener-create-curl +++ b/api-ref/source/v2/examples/listener-create-curl @@ -1 +1 @@ -curl -X POST -H "Content-Type: application/json" -H "X-Auth-Token: " -d '{"listener": {"protocol": "TERMINATED_HTTPS", "description": "A great TLS listener", "admin_state_up": true, "connection_limit": 200, "protocol_port": "443", "loadbalancer_id": "607226db-27ef-4d41-ae89-f2a800e9c2db", "name": "great_tls_listener", "insert_headers": {"X-Forwarded-For": "true", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"]}}' http://198.51.100.10:9876/v2.0/lbaas/listeners +curl -X POST -H "Content-Type: application/json" -H "X-Auth-Token: " -d '{"listener": {"protocol": "TERMINATED_HTTPS", "description": "A great TLS listener", "admin_state_up": true, "connection_limit": 200, "protocol_port": "443", "loadbalancer_id": "607226db-27ef-4d41-ae89-f2a800e9c2db", "name": "great_tls_listener", "insert_headers": {"X-Forwarded-For": "true", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"], "timeout_client_data": 50000, "timeout_member_connect": 5000, "timeout_member_data": 50000, "timeout_tcp_inspect": 0}}' http://198.51.100.10:9876/v2.0/lbaas/listeners diff --git a/api-ref/source/v2/examples/listener-create-request.json b/api-ref/source/v2/examples/listener-create-request.json index 43c918fe1a..1107ea2835 100644 --- a/api-ref/source/v2/examples/listener-create-request.json +++ b/api-ref/source/v2/examples/listener-create-request.json @@ -15,6 +15,10 @@ "sni_container_refs": [ "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee" - ] + ], + "timeout_client_data": 50000, + "timeout_member_connect": 5000, + "timeout_member_data": 50000, + "timeout_tcp_inspect": 0 } } diff --git a/api-ref/source/v2/examples/listener-create-response.json b/api-ref/source/v2/examples/listener-create-response.json index d8609cfd1f..b66b69cd3a 100644 --- a/api-ref/source/v2/examples/listener-create-response.json +++ b/api-ref/source/v2/examples/listener-create-response.json @@ -30,6 +30,10 @@ "id": "5e618272-339d-4a80-8d14-dbc093091bb1" } ], - "name": "great_tls_listener" + "name": "great_tls_listener", + "timeout_client_data": 50000, + "timeout_member_connect": 5000, + "timeout_member_data": 50000, + "timeout_tcp_inspect": 0 } } diff --git a/api-ref/source/v2/examples/listener-show-response.json b/api-ref/source/v2/examples/listener-show-response.json index acd7f7e5f1..87ca2aaf73 100644 --- a/api-ref/source/v2/examples/listener-show-response.json +++ b/api-ref/source/v2/examples/listener-show-response.json @@ -30,6 +30,10 @@ "id": "5e618272-339d-4a80-8d14-dbc093091bb1" } ], - "name": "great_tls_listener" + "name": "great_tls_listener", + "timeout_client_data": 50000, + "timeout_member_connect": 5000, + "timeout_member_data": 50000, + "timeout_tcp_inspect": 0 } } diff --git a/api-ref/source/v2/examples/listener-update-curl b/api-ref/source/v2/examples/listener-update-curl index 69938d33e5..96bc1895ab 100644 --- a/api-ref/source/v2/examples/listener-update-curl +++ b/api-ref/source/v2/examples/listener-update-curl @@ -1 +1 @@ -curl -X PUT -H "Content-Type: application/json" -H "X-Auth-Token: " -d '{"listener": {"description": "An updated great TLS listener", "admin_state_up": true, "connection_limit": 200, "name": "great_updated_tls_listener", "insert_headers": {"X-Forwarded-For": "false", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"]}}' http://198.51.100.10:9876/v2.0/lbaas/listeners/023f2e34-7806-443b-bfae-16c324569a3d +curl -X PUT -H "Content-Type: application/json" -H "X-Auth-Token: " -d '{"listener": {"description": "An updated great TLS listener", "admin_state_up": true, "connection_limit": 200, "name": "great_updated_tls_listener", "insert_headers": {"X-Forwarded-For": "false", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"], "timeout_client_data": 100000, "timeout_member_connect": 1000, "timeout_member_data": 100000, "timeout_tcp_inspect": 5}}' http://198.51.100.10:9876/v2.0/lbaas/listeners/023f2e34-7806-443b-bfae-16c324569a3d diff --git a/api-ref/source/v2/examples/listener-update-request.json b/api-ref/source/v2/examples/listener-update-request.json index 7e43938ef6..0bdbe061a4 100644 --- a/api-ref/source/v2/examples/listener-update-request.json +++ b/api-ref/source/v2/examples/listener-update-request.json @@ -13,6 +13,10 @@ "sni_container_refs": [ "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee" - ] + ], + "timeout_client_data": 100000, + "timeout_member_connect": 1000, + "timeout_member_data": 100000, + "timeout_tcp_inspect": 5 } } diff --git a/api-ref/source/v2/examples/listener-update-response.json b/api-ref/source/v2/examples/listener-update-response.json index 9afefbfe28..6978349138 100644 --- a/api-ref/source/v2/examples/listener-update-response.json +++ b/api-ref/source/v2/examples/listener-update-response.json @@ -30,6 +30,10 @@ "id": "5e618272-339d-4a80-8d14-dbc093091bb1" } ], - "name": "great_updated_tls_listener" + "name": "great_updated_tls_listener", + "timeout_client_data": 100000, + "timeout_member_connect": 1000, + "timeout_member_data": 100000, + "timeout_tcp_inspect": 5 } } diff --git a/api-ref/source/v2/examples/listeners-list-response.json b/api-ref/source/v2/examples/listeners-list-response.json index caa9f80b98..a890e194b8 100644 --- a/api-ref/source/v2/examples/listeners-list-response.json +++ b/api-ref/source/v2/examples/listeners-list-response.json @@ -32,7 +32,11 @@ "id": "5e618272-339d-4a80-8d14-dbc093091bb1" } ], - "name": "great_tls_listener" + "name": "great_tls_listener", + "timeout_client_data": 50000, + "timeout_member_connect": 5000, + "timeout_member_data": 50000, + "timeout_tcp_inspect": 0 } ] } diff --git a/api-ref/source/v2/listener.inc b/api-ref/source/v2/listener.inc index 71953534cd..953d033b5f 100644 --- a/api-ref/source/v2/listener.inc +++ b/api-ref/source/v2/listener.inc @@ -63,6 +63,10 @@ Response Parameters - protocol_port: protocol_port - provisioning_status: provisioning_status - sni_container_refs: sni_container_refs + - timeout_client_data: timeout_client_data + - timeout_member_connect: timeout_member_connect + - timeout_member_data: timeout_member_data + - timeout_tcp_inspect: timeout_tcp_inspect - updated_at: updated_at Response Example @@ -145,6 +149,10 @@ Request - protocol: protocol - protocol_port: protocol_port - sni_container_refs: sni_container_refs-optional + - timeout_client_data: timeout_client_data-optional + - timeout_member_connect: timeout_member_connect-optional + - timeout_member_data: timeout_member_data-optional + - timeout_tcp_inspect: timeout_tcp_inspect-optional .. _header_insertions: @@ -204,6 +212,10 @@ Response Parameters - protocol_port: protocol_port - provisioning_status: provisioning_status - sni_container_refs: sni_container_refs + - timeout_client_data: timeout_client_data + - timeout_member_connect: timeout_member_connect + - timeout_member_data: timeout_member_data + - timeout_tcp_inspect: timeout_tcp_inspect - updated_at: updated_at Response Example @@ -273,6 +285,10 @@ Response Parameters - protocol_port: protocol_port - provisioning_status: provisioning_status - sni_container_refs: sni_container_refs + - timeout_client_data: timeout_client_data + - timeout_member_connect: timeout_member_connect + - timeout_member_data: timeout_member_data + - timeout_tcp_inspect: timeout_tcp_inspect - updated_at: updated_at Response Example @@ -323,6 +339,10 @@ Request - listener_id: path-listener-id - name: name-optional - sni_container_refs: sni_container_refs-optional + - timeout_client_data: timeout_client_data-optional + - timeout_member_connect: timeout_member_connect-optional + - timeout_member_data: timeout_member_data-optional + - timeout_tcp_inspect: timeout_tcp_inspect-optional Request Example --------------- @@ -359,6 +379,10 @@ Response Parameters - protocol_port: protocol_port - provisioning_status: provisioning_status - sni_container_refs: sni_container_refs + - timeout_client_data: timeout_client_data + - timeout_member_connect: timeout_member_connect + - timeout_member_data: timeout_member_data + - timeout_tcp_inspect: timeout_tcp_inspect - updated_at: updated_at Response Example diff --git a/octavia/api/root_controller.py b/octavia/api/root_controller.py index 466e36e2d6..9a23c32dee 100644 --- a/octavia/api/root_controller.py +++ b/octavia/api/root_controller.py @@ -49,7 +49,7 @@ class RootController(rest.RestController): self._versions.append( { 'status': 'CURRENT', - 'updated': '2018-03-14T00:00:00Z', + 'updated': '2018-03-23T00:00:00Z', 'id': 'v2.0' }) if not (v1_enabled or v2_enabled): diff --git a/octavia/api/v2/types/listener.py b/octavia/api/v2/types/listener.py index afff963299..f8378d2be2 100644 --- a/octavia/api/v2/types/listener.py +++ b/octavia/api/v2/types/listener.py @@ -46,6 +46,10 @@ class ListenerResponse(BaseListenerType): created_at = wtypes.wsattr(wtypes.datetime.datetime) updated_at = wtypes.wsattr(wtypes.datetime.datetime) loadbalancers = wtypes.wsattr([types.IdOnlyType]) + timeout_client_data = wtypes.wsattr(wtypes.IntegerType()) + timeout_member_connect = wtypes.wsattr(wtypes.IntegerType()) + timeout_member_data = wtypes.wsattr(wtypes.IntegerType()) + timeout_tcp_inspect = wtypes.wsattr(wtypes.IntegerType()) @classmethod def from_data_model(cls, data_model, children=False): @@ -109,6 +113,22 @@ class ListenerPOST(BaseListenerType): insert_headers = wtypes.wsattr( wtypes.DictType(str, wtypes.StringType(max_length=255))) loadbalancer_id = wtypes.wsattr(wtypes.UuidType(), mandatory=True) + timeout_client_data = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_CLIENT_DATA) + timeout_member_connect = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_MEMBER_CONNECT) + timeout_member_data = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_MEMBER_DATA) + timeout_tcp_inspect = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_TCP_INSPECT) class ListenerRootPOST(types.BaseType): @@ -128,6 +148,18 @@ class ListenerPUT(BaseListenerType): default_pool_id = wtypes.wsattr(wtypes.UuidType()) insert_headers = wtypes.wsattr( wtypes.DictType(str, wtypes.StringType(max_length=255))) + timeout_client_data = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT)) + timeout_member_connect = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT)) + timeout_member_data = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT)) + timeout_tcp_inspect = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT)) class ListenerRootPUT(types.BaseType): @@ -154,6 +186,22 @@ class ListenerSingleCreate(BaseListenerType): l7policies = wtypes.wsattr([l7policy.L7PolicySingleCreate], default=[]) insert_headers = wtypes.wsattr( wtypes.DictType(str, wtypes.StringType(max_length=255))) + timeout_client_data = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_CLIENT_DATA) + timeout_member_connect = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_MEMBER_CONNECT) + timeout_member_data = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_MEMBER_DATA) + timeout_tcp_inspect = wtypes.wsattr( + wtypes.IntegerType(minimum=constants.MIN_TIMEOUT, + maximum=constants.MAX_TIMEOUT), + default=constants.DEFAULT_TIMEOUT_TCP_INSPECT) class ListenerStatusResponse(BaseListenerType): diff --git a/octavia/common/constants.py b/octavia/common/constants.py index 558413510c..d42ed763fd 100644 --- a/octavia/common/constants.py +++ b/octavia/common/constants.py @@ -76,6 +76,15 @@ MAX_WEIGHT = 256 MIN_HM_RETRIES = 1 MAX_HM_RETRIES = 10 +# 1 year: y d h m ms +MAX_TIMEOUT = 365 * 24 * 60 * 60 * 1000 +MIN_TIMEOUT = 0 + +DEFAULT_TIMEOUT_CLIENT_DATA = 50000 +DEFAULT_TIMEOUT_MEMBER_CONNECT = 5000 +DEFAULT_TIMEOUT_MEMBER_DATA = 50000 +DEFAULT_TIMEOUT_TCP_INSPECT = 0 + # Note: The database Amphora table has a foreign key constraint against # the provisioning_status table # Amphora has been allocated to a load balancer diff --git a/octavia/common/data_models.py b/octavia/common/data_models.py index 4458d1f315..50dfec36cf 100644 --- a/octavia/common/data_models.py +++ b/octavia/common/data_models.py @@ -351,7 +351,9 @@ class Listener(BaseDataModel): tls_certificate_id=None, stats=None, default_pool=None, load_balancer=None, sni_containers=None, peer_port=None, l7policies=None, pools=None, insert_headers=None, - created_at=None, updated_at=None): + created_at=None, updated_at=None, + timeout_client_data=None, timeout_member_connect=None, + timeout_member_data=None, timeout_tcp_inspect=None): self.id = id self.project_id = project_id self.name = name @@ -375,6 +377,10 @@ class Listener(BaseDataModel): self.pools = pools or [] self.created_at = created_at self.updated_at = updated_at + self.timeout_client_data = timeout_client_data + self.timeout_member_connect = timeout_member_connect + self.timeout_member_data = timeout_member_data + self.timeout_tcp_inspect = timeout_tcp_inspect def update(self, update_dict): for key, value in update_dict.items(): diff --git a/octavia/common/jinja/haproxy/jinja_cfg.py b/octavia/common/jinja/haproxy/jinja_cfg.py index e9061114e6..262b4fbfb9 100644 --- a/octavia/common/jinja/haproxy/jinja_cfg.py +++ b/octavia/common/jinja/haproxy/jinja_cfg.py @@ -58,10 +58,7 @@ class JinjaTemplater(object): base_crt_dir=None, haproxy_template=None, log_http=None, - log_server=None, - timeout_client=None, - timeout_server=None, - timeout_connect=None): + log_server=None): """HaProxy configuration generation :param base_amp_path: Base path for amphora data @@ -69,9 +66,6 @@ class JinjaTemplater(object): :param haproxy_template: Absolute path to Jinja template :param log_http: Haproxy HTTP logging path :param log_server: Haproxy Server logging path - :param timeout_client: Timeout client - :param timeout_server: Timeout server - :param timeout_connect: Timeout connect """ self.base_amp_path = base_amp_path or BASE_PATH @@ -79,9 +73,6 @@ class JinjaTemplater(object): self.haproxy_template = haproxy_template or HAPROXY_TEMPLATE self.log_http = log_http self.log_server = log_server - self.timeout_client = timeout_client - self.timeout_server = timeout_server - self.timeout_connect = timeout_connect def build_config(self, host_amphora, listener, tls_cert, socket_path=None, @@ -139,10 +130,7 @@ class JinjaTemplater(object): 'user_group': user_group, 'stats_sock': socket_path, 'log_http': self.log_http, - 'log_server': self.log_server, - 'timeout_client': self.timeout_client, - 'timeout_server': self.timeout_server, - 'timeout_connect': self.timeout_connect}, + 'log_server': self.log_server}, constants=constants) def _transform_loadbalancer(self, host_amphora, loadbalancer, listener, @@ -200,7 +188,16 @@ class JinjaTemplater(object): 'insert_headers': listener.insert_headers, 'topology': listener.load_balancer.topology, 'amphorae': listener.load_balancer.amphorae, - 'enabled': listener.enabled + 'enabled': listener.enabled, + 'timeout_client_data': (listener.timeout_client_data or + constants.DEFAULT_TIMEOUT_CLIENT_DATA), + 'timeout_member_connect': (listener.timeout_member_connect or + constants.DEFAULT_TIMEOUT_MEMBER_CONNECT + ), + 'timeout_member_data': (listener.timeout_member_data or + constants.DEFAULT_TIMEOUT_MEMBER_DATA), + 'timeout_tcp_inspect': (listener.timeout_tcp_inspect or + constants.DEFAULT_TIMEOUT_TCP_INSPECT), } if listener.connection_limit and listener.connection_limit > -1: ret_value['connection_limit'] = listener.connection_limit diff --git a/octavia/common/jinja/haproxy/templates/base.j2 b/octavia/common/jinja/haproxy/templates/base.j2 index 4680b82a19..84631345b2 100644 --- a/octavia/common/jinja/haproxy/templates/base.j2 +++ b/octavia/common/jinja/haproxy/templates/base.j2 @@ -38,9 +38,6 @@ defaults log global retries 3 option redispatch - timeout connect {{ timeout_connect | default('5000', true) }} - timeout client {{ timeout_client | default('50000', true) }} - timeout server {{ timeout_server | default('50000', true) }} {% block peers %}{% endblock peers %} diff --git a/octavia/common/jinja/haproxy/templates/macros.j2 b/octavia/common/jinja/haproxy/templates/macros.j2 index e6b106dd09..60d75512e2 100644 --- a/octavia/common/jinja/haproxy/templates/macros.j2 +++ b/octavia/common/jinja/haproxy/templates/macros.j2 @@ -136,6 +136,10 @@ frontend {{ listener.id }} {% if listener.default_pool and listener.default_pool.enabled %} default_backend {{ listener.default_pool.id }} {% endif %} + timeout client {{ listener.timeout_client_data }} + {% if listener.timeout_tcp_inspect %} + tcp-request inspect-delay {{ listener.timeout_tcp_inspect }} + {% endif %} {% endmacro %} @@ -251,6 +255,8 @@ backend {{ pool.id }} fullconn {{ listener.connection_limit }} {% endif %} option allbackups + timeout connect {{ listener.timeout_member_connect }} + timeout server {{ listener.timeout_member_data }} {% for member in pool.members if member.enabled %} {{- member_macro(constants, pool, member) -}} {% endfor %} diff --git a/octavia/db/migration/alembic_migrations/versions/0fd2c131923f_add_timeout_fields_to_listener.py b/octavia/db/migration/alembic_migrations/versions/0fd2c131923f_add_timeout_fields_to_listener.py new file mode 100644 index 0000000000..40ffcf615d --- /dev/null +++ b/octavia/db/migration/alembic_migrations/versions/0fd2c131923f_add_timeout_fields_to_listener.py @@ -0,0 +1,50 @@ +# Copyright 2018 GoDaddy +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""add timeout fields to listener + +Revision ID: 0fd2c131923f +Revises: ba35e0fb88e1 +Create Date: 2018-03-23 03:34:26.657254 + +""" + +from alembic import op +import sqlalchemy as sa + +from octavia.common import constants + +# revision identifiers, used by Alembic. +revision = '0fd2c131923f' +down_revision = 'ba35e0fb88e1' + + +def upgrade(): + op.add_column('listener', + sa.Column('timeout_client_data', + sa.Integer(), nullable=True, + default=constants.DEFAULT_TIMEOUT_CLIENT_DATA)) + op.add_column('listener', + sa.Column('timeout_member_connect', + sa.Integer(), nullable=True, + default=constants.DEFAULT_TIMEOUT_MEMBER_CONNECT)) + op.add_column('listener', + sa.Column('timeout_member_data', + sa.Integer(), nullable=True, + default=constants.DEFAULT_TIMEOUT_MEMBER_DATA)) + op.add_column('listener', + sa.Column('timeout_tcp_inspect', + sa.Integer(), nullable=True, + default=constants.DEFAULT_TIMEOUT_TCP_INSPECT)) diff --git a/octavia/db/models.py b/octavia/db/models.py index f17791617b..75495f3436 100644 --- a/octavia/db/models.py +++ b/octavia/db/models.py @@ -442,6 +442,10 @@ class Listener(base_models.BASE, base_models.IdMixin, peer_port = sa.Column(sa.Integer(), nullable=True) insert_headers = sa.Column(sa.PickleType()) + timeout_client_data = sa.Column(sa.Integer, nullable=True) + timeout_member_connect = sa.Column(sa.Integer, nullable=True) + timeout_member_data = sa.Column(sa.Integer, nullable=True) + timeout_tcp_inspect = sa.Column(sa.Integer, nullable=True) # This property should be a unique list of the default_pool and anything # referenced by enabled L7Policies with at least one rule that also diff --git a/octavia/tests/functional/api/v2/test_listener.py b/octavia/tests/functional/api/v2/test_listener.py index 91a7450b36..0428f6b24f 100644 --- a/octavia/tests/functional/api/v2/test_listener.py +++ b/octavia/tests/functional/api/v2/test_listener.py @@ -424,7 +424,7 @@ class TestListener(base.BaseAPITest): listener_path = self.listener_path self.get(listener_path.format(listener_id='SEAN-CONNERY'), status=404) - def test_create(self, **optionals): + def test_create(self, response_status=201, **optionals): sni1 = uuidutils.generate_uuid() sni2 = uuidutils.generate_uuid() lb_listener = {'name': 'listener1', 'default_pool_id': None, @@ -439,7 +439,9 @@ class TestListener(base.BaseAPITest): 'loadbalancer_id': self.lb_id} lb_listener.update(optionals) body = self._build_body(lb_listener) - response = self.post(self.LISTENERS_PATH, body) + response = self.post(self.LISTENERS_PATH, body, status=response_status) + if response_status >= 300: + return response listener_api = response.json['listener'] extra_expects = {'provisioning_status': constants.PENDING_CREATE, 'operating_status': constants.OFFLINE} @@ -460,6 +462,52 @@ class TestListener(base.BaseAPITest): self.assert_correct_lb_status(self.lb_id, constants.ONLINE, constants.PENDING_UPDATE) self.assert_final_listener_statuses(self.lb_id, listener_api.get('id')) + return listener_api + + def test_create_with_timeouts(self): + optionals = { + 'timeout_client_data': 1, + 'timeout_member_connect': 2, + 'timeout_member_data': constants.MIN_TIMEOUT, + 'timeout_tcp_inspect': constants.MAX_TIMEOUT, + } + listener_api = self.test_create(**optionals) + self.assertEqual(1, listener_api.get('timeout_client_data')) + self.assertEqual(2, listener_api.get('timeout_member_connect')) + self.assertEqual(constants.MIN_TIMEOUT, + listener_api.get('timeout_member_data')) + self.assertEqual(constants.MAX_TIMEOUT, + listener_api.get('timeout_tcp_inspect')) + + def test_create_with_timeouts_too_high(self): + optionals = { + 'timeout_client_data': 1, + 'timeout_member_connect': 2, + 'timeout_member_data': 3, + 'timeout_tcp_inspect': constants.MAX_TIMEOUT + 1, + } + resp = self.test_create(response_status=400, **optionals).json + fault = resp.get('faultstring') + self.assertIn( + 'Invalid input for field/attribute timeout_tcp_inspect', fault) + self.assertIn( + 'Value should be lower or equal to {0}'.format( + constants.MAX_TIMEOUT), fault) + + def test_create_with_timeouts_too_low(self): + optionals = { + 'timeout_client_data': 1, + 'timeout_member_connect': 2, + 'timeout_member_data': 3, + 'timeout_tcp_inspect': constants.MIN_TIMEOUT - 1, + } + resp = self.test_create(response_status=400, **optionals).json + fault = resp.get('faultstring') + self.assertIn( + 'Invalid input for field/attribute timeout_tcp_inspect', fault) + self.assertIn( + 'Value should be greater or equal to {0}'.format( + constants.MIN_TIMEOUT), fault) def test_create_duplicate_fails(self): self.create_listener(constants.PROTOCOL_HTTP, 80, self.lb_id) @@ -720,15 +768,18 @@ class TestListener(base.BaseAPITest): default_pool_id=None).get(self.root_tag) self.set_lb_status(self.lb_id) new_listener = {'name': 'listener2', 'admin_state_up': True, - 'default_pool_id': self.pool_id} + 'default_pool_id': self.pool_id, + 'timeout_client_data': 1, + 'timeout_member_connect': 2, + 'timeout_member_data': 3, + 'timeout_tcp_inspect': 4} body = self._build_body(new_listener) listener_path = self.LISTENER_PATH.format( listener_id=listener['id']) api_listener = self.put(listener_path, body).json.get(self.root_tag) - update_expect = {'name': 'listener2', 'admin_state_up': True, - 'default_pool_id': self.pool_id, - 'provisioning_status': constants.PENDING_UPDATE, + update_expect = {'provisioning_status': constants.PENDING_UPDATE, 'operating_status': constants.ONLINE} + update_expect.update(new_listener) listener.update(update_expect) self.assertEqual(listener['created_at'], api_listener['created_at']) self.assertNotEqual(listener['updated_at'], api_listener['updated_at']) diff --git a/octavia/tests/functional/api/v2/test_load_balancer.py b/octavia/tests/functional/api/v2/test_load_balancer.py index ee616fa7ee..fc50b80997 100644 --- a/octavia/tests/functional/api/v2/test_load_balancer.py +++ b/octavia/tests/functional/api/v2/test_load_balancer.py @@ -1941,7 +1941,11 @@ class TestLoadBalancerGraph(base.BaseAPITest): 'provisioning_status': constants.PENDING_CREATE, 'operating_status': constants.OFFLINE, 'insert_headers': {}, - 'project_id': self._project_id + 'project_id': self._project_id, + 'timeout_client_data': constants.DEFAULT_TIMEOUT_CLIENT_DATA, + 'timeout_member_connect': constants.DEFAULT_TIMEOUT_MEMBER_CONNECT, + 'timeout_member_data': constants.DEFAULT_TIMEOUT_MEMBER_DATA, + 'timeout_tcp_inspect': constants.DEFAULT_TIMEOUT_TCP_INSPECT, } if create_sni_containers: create_listener['sni_container_refs'] = create_sni_containers diff --git a/octavia/tests/functional/db/test_models.py b/octavia/tests/functional/db/test_models.py index 3a85b4d4d2..3857e2dada 100644 --- a/octavia/tests/functional/db/test_models.py +++ b/octavia/tests/functional/db/test_models.py @@ -335,6 +335,17 @@ class ListenerModelTest(base.OctaviaDBTestBase, ModelTestMixin): self.assertIsNotNone(listener.created_at) self.assertIsNone(listener.updated_at) + def test_create_with_timeouts(self): + timeouts = { + 'timeout_client_data': 1, + 'timeout_member_connect': 2, + 'timeout_member_data': constants.MIN_TIMEOUT, + 'timeout_tcp_inspect': constants.MAX_TIMEOUT, + } + listener = self.create_listener(self.session, **timeouts) + for item in timeouts: + self.assertEqual(timeouts[item], getattr(listener, item)) + def test_update(self): listener = self.create_listener(self.session) self.assertIsNone(listener.updated_at) @@ -346,6 +357,24 @@ class ListenerModelTest(base.OctaviaDBTestBase, ModelTestMixin): self.assertEqual('test1', new_listener.name) self.assertIsNotNone(new_listener.updated_at) + def test_update_with_timeouts(self): + listener = self.create_listener(self.session) + listener_id = listener.id + + timeouts = { + 'timeout_client_data': 1, + 'timeout_member_connect': 2, + 'timeout_member_data': 3, + 'timeout_tcp_inspect': 4, + } + + for item in timeouts: + setattr(listener, item, timeouts[item]) + new_listener = self.session.query( + models.Listener).filter_by(id=listener_id).first() + for item in timeouts: + self.assertEqual(timeouts[item], getattr(new_listener, item)) + def test_delete(self): listener = self.create_listener(self.session) listener_id = listener.id diff --git a/octavia/tests/unit/amphorae/backends/agent/api_server/test_haproxy_compatibility.py b/octavia/tests/unit/amphorae/backends/agent/api_server/test_haproxy_compatibility.py index 93814f09f7..bb9de2a5bf 100644 --- a/octavia/tests/unit/amphorae/backends/agent/api_server/test_haproxy_compatibility.py +++ b/octavia/tests/unit/amphorae/backends/agent/api_server/test_haproxy_compatibility.py @@ -36,16 +36,14 @@ class HAProxyCompatTestCase(base.TestCase): "defaults\n" " log global\n" " retries 3\n" - " option redispatch\n" - " timeout connect 5000\n" - " timeout client 50000\n" - " timeout server 50000\n\n\n\n" + " option redispatch\n\n\n\n" "frontend sample_listener_id_1\n" " option httplog\n" " maxconn 98\n" " bind 10.0.0.2:80\n" " mode http\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") self.backend_without_external = ( "backend sample_pool_id_1\n" " mode http\n" @@ -55,6 +53,9 @@ class HAProxyCompatTestCase(base.TestCase): " option httpchk GET /index.html\n" " http-check expect rstatus 418\n" " fullconn 98\n" + " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13 " "check inter 30s fall 3 rise 2 cookie sample_member_id_1\n" " server sample_member_id_2 10.0.0.98:82 weight 13 " @@ -67,9 +68,12 @@ class HAProxyCompatTestCase(base.TestCase): " timeout check 31\n" " option httpchk GET /index.html\n" " http-check expect rstatus 418\n" - " fullconn 98\n" " option external-check\n" " external-check command /var/lib/octavia/ping-wrapper.sh\n" + " fullconn 98\n" + " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13 " "check inter 30s fall 3 rise 2 cookie sample_member_id_1\n" " server sample_member_id_2 10.0.0.98:82 weight 13 " diff --git a/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py b/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py index e73a7b5014..4d08720512 100644 --- a/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py +++ b/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py @@ -39,7 +39,8 @@ class TestHaproxyCfg(base.TestCase): "sample_listener_id_1/FakeCN.pem " "crt /var/lib/octavia/certs/sample_listener_id_1\n" " mode http\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") be = ("backend sample_pool_id_1\n" " mode http\n" " balance roundrobin\n" @@ -49,6 +50,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -77,7 +80,8 @@ class TestHaproxyCfg(base.TestCase): "ssl crt /var/lib/octavia/certs/" "sample_listener_id_1/FakeCN.pem\n" " mode http\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") be = ("backend sample_pool_id_1\n" " mode http\n" " balance roundrobin\n" @@ -87,6 +91,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -116,6 +122,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -139,6 +147,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "addr 192.168.1.1 port 9000 " @@ -155,6 +165,72 @@ class TestHaproxyCfg(base.TestCase): sample_configs.sample_base_expected_config(backend=be), rendered_obj) + def test_render_template_custom_timeouts(self): + fe = ("frontend sample_listener_id_1\n" + " option httplog\n" + " maxconn 98\n" + " bind 10.0.0.2:80\n" + " mode http\n" + " default_backend sample_pool_id_1\n" + " timeout client 2\n\n") + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " timeout check 31s\n" + " option httpchk GET /index.html\n" + " http-check expect rstatus 418\n" + " fullconn 98\n" + " option allbackups\n" + " timeout connect 1\n" + " timeout server 3\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 " + "check inter 30s fall 3 rise 2 cookie sample_member_id_1\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 " + "check inter 30s fall 3 rise 2 cookie sample_member_id_2\n\n") + rendered_obj = self.jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_amphora_tuple(), + sample_configs.sample_listener_tuple(timeout_member_connect=1, + timeout_client_data=2, + timeout_member_data=3)) + self.assertEqual( + sample_configs.sample_base_expected_config(frontend=fe, + backend=be), + rendered_obj) + + def test_render_template_null_timeouts(self): + fe = ("frontend sample_listener_id_1\n" + " option httplog\n" + " maxconn 98\n" + " bind 10.0.0.2:80\n" + " mode http\n" + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " timeout check 31s\n" + " option httpchk GET /index.html\n" + " http-check expect rstatus 418\n" + " fullconn 98\n" + " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 " + "check inter 30s fall 3 rise 2 cookie sample_member_id_1\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 " + "check inter 30s fall 3 rise 2 cookie sample_member_id_2\n\n") + rendered_obj = self.jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_amphora_tuple(), + sample_configs.sample_listener_tuple(timeout_member_connect=None, + timeout_client_data=None, + timeout_member_data=None)) + self.assertEqual( + sample_configs.sample_base_expected_config(frontend=fe, + backend=be), + rendered_obj) + def test_render_template_member_monitor_addr_port(self): be = ("backend sample_pool_id_1\n" " mode http\n" @@ -165,6 +241,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "addr 192.168.1.1 port 9000 " @@ -186,7 +264,8 @@ class TestHaproxyCfg(base.TestCase): " maxconn 98\n" " bind 10.0.0.2:443\n" " mode tcp\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") be = ("backend sample_pool_id_1\n" " mode tcp\n" " balance roundrobin\n" @@ -196,6 +275,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check check-ssl verify none inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -214,7 +295,8 @@ class TestHaproxyCfg(base.TestCase): " maxconn 98\n" " bind 10.0.0.2:443\n" " mode tcp\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") be = ("backend sample_pool_id_1\n" " mode tcp\n" " balance roundrobin\n" @@ -223,6 +305,8 @@ class TestHaproxyCfg(base.TestCase): " option ssl-hello-chk\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -243,6 +327,8 @@ class TestHaproxyCfg(base.TestCase): " cookie SRV insert indirect nocache\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13 " "cookie sample_member_id_1\n" " server sample_member_id_2 10.0.0.98:82 weight 13 " @@ -263,6 +349,8 @@ class TestHaproxyCfg(base.TestCase): " external-check command /var/lib/octavia/ping-wrapper.sh\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -283,13 +371,16 @@ class TestHaproxyCfg(base.TestCase): " maxconn 98\n" " bind 10.0.0.2:443\n" " mode tcp\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") be = ("backend sample_pool_id_1\n" " mode tcp\n" " balance roundrobin\n" " cookie SRV insert indirect nocache\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13 " "cookie sample_member_id_1\n" " server sample_member_id_2 10.0.0.98:82 weight 13 " @@ -306,12 +397,15 @@ class TestHaproxyCfg(base.TestCase): " maxconn 98\n" " bind 10.0.0.2:443\n" " mode tcp\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") be = ("backend sample_pool_id_1\n" " mode tcp\n" " balance roundrobin\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13\n" " server sample_member_id_2 10.0.0.98:82 weight 13\n\n") rendered_obj = self.jinja_cfg.render_loadbalancer_obj( @@ -327,6 +421,8 @@ class TestHaproxyCfg(base.TestCase): " balance roundrobin\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13\n" " server sample_member_id_2 10.0.0.98:82 weight 13\n\n") rendered_obj = self.jinja_cfg.render_loadbalancer_obj( @@ -347,6 +443,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2\n" " server sample_member_id_2 10.0.0.98:82 " @@ -371,6 +469,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2\n" " server sample_member_id_2 10.0.0.98:82 " @@ -403,7 +503,8 @@ class TestHaproxyCfg(base.TestCase): ".example.com\n" " http-request deny if sample_l7rule_id_4 " "sample_l7rule_id_5\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") be = ("backend sample_pool_id_1\n" " mode http\n" " balance roundrobin\n" @@ -413,6 +514,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13 check " "inter 30s fall 3 rise 2 cookie sample_member_id_1\n" " server sample_member_id_2 10.0.0.98:82 weight 13 check " @@ -427,6 +530,8 @@ class TestHaproxyCfg(base.TestCase): " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_3 10.0.0.97:82 weight 13 check " "inter 30s fall 3 rise 2 cookie sample_member_id_3\n\n") rendered_obj = self.jinja_cfg.render_loadbalancer_obj( @@ -446,6 +551,8 @@ class TestHaproxyCfg(base.TestCase): " option forwardfor\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -472,6 +579,8 @@ class TestHaproxyCfg(base.TestCase): " http-request set-header X-Forwarded-Port %[dst_port]\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1\n" @@ -495,6 +604,8 @@ class TestHaproxyCfg(base.TestCase): " timeout check 31s\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 " "weight 13 check inter 30s fall 3 rise 2 " "cookie sample_member_id_1 send-proxy\n" diff --git a/octavia/tests/unit/common/sample_configs/sample_configs.py b/octavia/tests/unit/common/sample_configs/sample_configs.py index dfa2cda9b3..fb73b9b5f6 100644 --- a/octavia/tests/unit/common/sample_configs/sample_configs.py +++ b/octavia/tests/unit/common/sample_configs/sample_configs.py @@ -245,7 +245,12 @@ RET_LISTENER = { 'pools': [RET_POOL_1], 'l7policies': [], 'enabled': True, - 'insert_headers': {}} + 'insert_headers': {}, + 'timeout_client_data': 50000, + 'timeout_member_connect': 5000, + 'timeout_member_data': 50000, + 'timeout_tcp_inspect': 0, +} RET_LISTENER_L7 = { 'id': 'sample_listener_id_1', @@ -261,7 +266,12 @@ RET_LISTENER_L7 = { 'l7policies': [RET_L7POLICY_1, RET_L7POLICY_2, RET_L7POLICY_3, RET_L7POLICY_4, RET_L7POLICY_5, RET_L7POLICY_6], 'enabled': True, - 'insert_headers': {}} + 'insert_headers': {}, + 'timeout_client_data': 50000, + 'timeout_member_connect': 5000, + 'timeout_member_data': 50000, + 'timeout_tcp_inspect': 0, +} RET_LISTENER_TLS = { 'id': 'sample_listener_id_1', @@ -409,7 +419,11 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True, tls=False, sni=False, peer_port=None, topology=None, l7=False, enabled=True, insert_headers=None, be_proto=None, monitor_ip_port=False, - monitor_proto=None, backup_member=False): + monitor_proto=None, backup_member=False, + timeout_client_data=50000, + timeout_member_connect=5000, + timeout_member_data=50000, + timeout_tcp_inspect=0): proto = 'HTTP' if proto is None else proto if be_proto is None: be_proto = 'HTTP' if proto is 'TERMINATED_HTTPS' else proto @@ -422,7 +436,9 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True, 'connection_limit, tls_certificate_id, ' 'sni_container_ids, default_tls_container, ' 'sni_containers, load_balancer, peer_port, pools, ' - 'l7policies, enabled, insert_headers',) + 'l7policies, enabled, insert_headers, timeout_client_data,' + 'timeout_member_connect, timeout_member_data, ' + 'timeout_tcp_inspect',) if l7: pools = [ sample_pool_tuple( @@ -492,7 +508,11 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True, pools=pools, l7policies=l7policies, enabled=enabled, - insert_headers=insert_headers + insert_headers=insert_headers, + timeout_client_data=timeout_client_data, + timeout_member_connect=timeout_member_connect, + timeout_member_data=timeout_member_data, + timeout_tcp_inspect=timeout_tcp_inspect ) @@ -703,32 +723,41 @@ def sample_l7rule_tuple(id, def sample_base_expected_config(frontend=None, backend=None, - peers=None, global_opts=None): + peers=None, global_opts=None, defaults=None): if frontend is None: frontend = ("frontend sample_listener_id_1\n" " option httplog\n" " maxconn 98\n" " bind 10.0.0.2:80\n" " mode http\n" - " default_backend sample_pool_id_1\n\n") + " default_backend sample_pool_id_1\n" + " timeout client 50000\n\n") if backend is None: backend = ("backend sample_pool_id_1\n" " mode http\n" " balance roundrobin\n" " cookie SRV insert indirect nocache\n" - " timeout check 31\n" + " timeout check 31s\n" " option httpchk GET /index.html\n" " http-check expect rstatus 418\n" " fullconn 98\n" " option allbackups\n" + " timeout connect 5000\n" + " timeout server 50000\n" " server sample_member_id_1 10.0.0.99:82 weight 13 " "check inter 30s fall 3 rise 2 cookie sample_member_id_1\n" " server sample_member_id_2 10.0.0.98:82 weight 13 " - "check inter 30s fall 3 rise 2 cookie sample_member_id_2\n") + "check inter 30s fall 3 rise 2 cookie sample_member_id_2\n" + "\n") if peers is None: peers = "\n\n" if global_opts is None: global_opts = " maxconn 98\n\n" + if defaults is None: + defaults = ("defaults\n" + " log global\n" + " retries 3\n" + " option redispatch\n\n") return ("# Configuration for test-lb\n" "global\n" " daemon\n" @@ -738,11 +767,4 @@ def sample_base_expected_config(frontend=None, backend=None, " log /dev/log local1 notice\n" " stats socket /var/lib/octavia/sample_listener_id_1.sock" " mode 0666 level user\n" + - global_opts + - "defaults\n" - " log global\n" - " retries 3\n" - " option redispatch\n" - " timeout connect 5000\n" - " timeout client 50000\n" - " timeout server 50000\n\n" + peers + frontend + backend) + global_opts + defaults + peers + frontend + backend) diff --git a/releasenotes/notes/Allow-configuration-of-listener-timeout-values-9a7600c4e21364e3.yaml b/releasenotes/notes/Allow-configuration-of-listener-timeout-values-9a7600c4e21364e3.yaml new file mode 100644 index 0000000000..8ba21006f1 --- /dev/null +++ b/releasenotes/notes/Allow-configuration-of-listener-timeout-values-9a7600c4e21364e3.yaml @@ -0,0 +1,11 @@ +--- +features: + - | + Listeners have four new timeout settings: + + * `timeout_client_data`: Frontend client inactivity timeout + * `timeout_member_connect`: Backend member connection timeout + * `timeout_member_data`: Backend member inactivity timeout + * `timeout_tcp_inspect`: Time to wait for TCP packets for content inspection + + The value for all of these fields is expected to be in milliseconds.