From 7100872ddb56397e4e9414ffeb17663ca54dc5d0 Mon Sep 17 00:00:00 2001 From: cheng Date: Fri, 21 Apr 2017 03:20:33 -0400 Subject: [PATCH] Add new PROXY protocol to lbaas pool This patch enable configure PROXY protocol informs to backend server about the lay 3/4 address of the incoming connections. Close-Bug: #1677987 Change-Id: Idc9a5718dddbaaaec251c9a0673c74e4132c5f54 Signed-off-by: cheng --- doc/source/api/octaviaapi.rst | 2 +- octavia/common/constants.py | 3 +- octavia/common/jinja/haproxy/jinja_cfg.py | 1 + .../common/jinja/haproxy/templates/macros.j2 | 13 +++++- ...309960964f8_add_proxy_protocol_for_pool.py | 46 +++++++++++++++++++ octavia/tests/functional/api/v1/test_pool.py | 25 ++++++++++ octavia/tests/functional/api/v2/test_pool.py | 23 ++++++++++ .../common/jinja/haproxy/test_jinja_cfg.py | 21 +++++++++ .../common/sample_configs/sample_configs.py | 6 ++- ...pport-proxy-protocol-cc5991175a110619.yaml | 3 ++ 10 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 octavia/db/migration/alembic_migrations/versions/5309960964f8_add_proxy_protocol_for_pool.py create mode 100644 releasenotes/notes/support-proxy-protocol-cc5991175a110619.yaml diff --git a/doc/source/api/octaviaapi.rst b/doc/source/api/octaviaapi.rst index dddce75182..395960f148 100644 --- a/doc/source/api/octaviaapi.rst +++ b/doc/source/api/octaviaapi.rst @@ -826,7 +826,7 @@ Pools +---------------------+---------------+------------------------------------+ | protocol | String | Network protocol from the \ | | | | following: ``TCP``, ``HTTP``, \ | -| | | ``HTTPS`` | +| | | ``HTTPS``, ``PROXY`` | +---------------------+---------------+------------------------------------+ | lb_algorithm | UUID | Load balancing algorithm from \ | | | | the following: \ | diff --git a/octavia/common/constants.py b/octavia/common/constants.py index dd5cceae3a..5639ca0eb7 100644 --- a/octavia/common/constants.py +++ b/octavia/common/constants.py @@ -58,8 +58,9 @@ PROTOCOL_TCP = 'TCP' PROTOCOL_HTTP = 'HTTP' PROTOCOL_HTTPS = 'HTTPS' PROTOCOL_TERMINATED_HTTPS = 'TERMINATED_HTTPS' +PROTOCOL_PROXY = 'PROXY' SUPPORTED_PROTOCOLS = (PROTOCOL_TCP, PROTOCOL_HTTPS, PROTOCOL_HTTP, - PROTOCOL_TERMINATED_HTTPS) + PROTOCOL_TERMINATED_HTTPS, PROTOCOL_PROXY) # API Integer Ranges MIN_PORT_NUMBER = 1 diff --git a/octavia/common/jinja/haproxy/jinja_cfg.py b/octavia/common/jinja/haproxy/jinja_cfg.py index 184a60c28c..4812ebc56d 100644 --- a/octavia/common/jinja/haproxy/jinja_cfg.py +++ b/octavia/common/jinja/haproxy/jinja_cfg.py @@ -26,6 +26,7 @@ PROTOCOL_MAP = { constants.PROTOCOL_TCP: 'tcp', constants.PROTOCOL_HTTP: 'http', constants.PROTOCOL_HTTPS: 'tcp', + constants.PROTOCOL_PROXY: 'proxy', constants.PROTOCOL_TERMINATED_HTTPS: 'http' } diff --git a/octavia/common/jinja/haproxy/templates/macros.j2 b/octavia/common/jinja/haproxy/templates/macros.j2 index 41450aad59..2c14240560 100644 --- a/octavia/common/jinja/haproxy/templates/macros.j2 +++ b/octavia/common/jinja/haproxy/templates/macros.j2 @@ -153,15 +153,24 @@ frontend {{ listener.id }} {% else %} {% set persistence_opt = "" %} {% endif %} - {{ "server %s %s:%d weight %s%s%s"|e|format( + {% if pool.protocol.lower() == constants.PROTOCOL_PROXY.lower() %} + {% set proxy_protocol_opt = " send-proxy" %} + {% else %} + {% set proxy_protocol_opt = "" %} + {% endif %} + {{ "server %s %s:%d weight %s%s%s%s"|e|format( member.id, member.address, member.protocol_port, member.weight, - hm_opt, persistence_opt)|trim() }} + hm_opt, persistence_opt, proxy_protocol_opt)|trim() }} {% endmacro %} {% macro backend_macro(constants, listener, pool) %} backend {{ pool.id }} + {% if pool.protocol.lower() == constants.PROTOCOL_PROXY.lower() %} + mode {{ listener.protocol_mode }} + {% else %} mode {{ pool.protocol }} + {% endif %} balance {{ pool.lb_algorithm }} {% if pool.session_persistence %} {% if (pool.session_persistence.type == diff --git a/octavia/db/migration/alembic_migrations/versions/5309960964f8_add_proxy_protocol_for_pool.py b/octavia/db/migration/alembic_migrations/versions/5309960964f8_add_proxy_protocol_for_pool.py new file mode 100644 index 0000000000..453216e1eb --- /dev/null +++ b/octavia/db/migration/alembic_migrations/versions/5309960964f8_add_proxy_protocol_for_pool.py @@ -0,0 +1,46 @@ +# Copyright 2017 EayunStack, Inc. +# +# 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 proxy protocol for pool + +Revision ID: 5309960964f8 +Revises: 52377704420e +Create Date: 2017-04-27 01:13:38.064697 + +""" + +from alembic import op +import sqlalchemy as sa +from sqlalchemy import sql + + +# revision identifiers, used by Alembic. +revision = '5309960964f8' +down_revision = '52377704420e' + +new_protocol = 'PROXY' + + +def upgrade(): + insert_table = sql.table( + u'protocol', + sql.column(u'name', sa.String), + sql.column(u'description', sa.String) + ) + + op.bulk_insert( + insert_table, + [ + {'name': new_protocol} + ] + ) diff --git a/octavia/tests/functional/api/v1/test_pool.py b/octavia/tests/functional/api/v1/test_pool.py index 0012637d69..73ed1c00a2 100644 --- a/octavia/tests/functional/api/v1/test_pool.py +++ b/octavia/tests/functional/api/v1/test_pool.py @@ -110,6 +110,31 @@ class TestPool(base.BaseAPITest): self.listener.get('id'), constants.ACTIVE, constants.ONLINE) + def test_create_with_proxy_protocol(self): + api_pool = self.create_pool(self.lb.get('id'), + self.listener.get('id'), + constants.PROTOCOL_PROXY, + constants.LB_ALGORITHM_ROUND_ROBIN) + self.assert_correct_lb_status(self.lb.get('id'), + constants.PENDING_UPDATE, + constants.ONLINE) + self.assert_correct_listener_status(self.lb.get('id'), + self.listener.get('id'), + constants.PENDING_UPDATE, + constants.ONLINE) + self.set_lb_status(self.lb.get('id')) + self.assertEqual(constants.PROTOCOL_PROXY, api_pool.get('protocol')) + self.assertEqual(constants.LB_ALGORITHM_ROUND_ROBIN, + api_pool.get('lb_algorithm')) + self.assertIsNotNone(api_pool.get('created_at')) + self.assertIsNone(api_pool.get('updated_at')) + self.assert_correct_lb_status(self.lb.get('id'), + constants.ACTIVE, + constants.ONLINE) + self.assert_correct_listener_status(self.lb.get('id'), + self.listener.get('id'), + constants.ACTIVE, constants.ONLINE) + def test_create_sans_listener(self): api_pool = self.create_pool_sans_listener( self.lb.get('id'), constants.PROTOCOL_HTTP, diff --git a/octavia/tests/functional/api/v2/test_pool.py b/octavia/tests/functional/api/v2/test_pool.py index 5649c5072d..dcc2646dbb 100644 --- a/octavia/tests/functional/api/v2/test_pool.py +++ b/octavia/tests/functional/api/v2/test_pool.py @@ -217,6 +217,29 @@ class TestPool(base.BaseAPITest): lb_id=self.lb_id, listener_id=self.listener_id, pool_id=api_pool.get('id')) + def test_create_with_proxy_protocol(self): + api_pool = self.create_pool( + self.lb_id, + constants.PROTOCOL_PROXY, + constants.LB_ALGORITHM_ROUND_ROBIN, + listener_id=self.listener_id).get(self.root_tag) + self.assert_correct_status( + lb_id=self.lb_id, listener_id=self.listener_id, + pool_id=api_pool.get('id'), + lb_prov_status=constants.PENDING_UPDATE, + listener_prov_status=constants.PENDING_UPDATE, + pool_prov_status=constants.PENDING_CREATE, + pool_op_status=constants.OFFLINE) + self.set_lb_status(self.lb_id) + self.assertEqual(constants.PROTOCOL_PROXY, api_pool.get('protocol')) + self.assertEqual(constants.LB_ALGORITHM_ROUND_ROBIN, + api_pool.get('lb_algorithm')) + self.assertIsNotNone(api_pool.get('created_at')) + self.assertIsNone(api_pool.get('updated_at')) + self.assert_correct_status( + lb_id=self.lb_id, listener_id=self.listener_id, + pool_id=api_pool.get('id')) + def test_create_sans_listener(self): api_pool = self.create_pool( self.lb_id, 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 0f8dd16619..21a9e6fc06 100644 --- a/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py +++ b/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py @@ -371,6 +371,27 @@ class TestHaproxyCfg(base.TestCase): sample_configs.sample_base_expected_config(backend=be), rendered_obj) + def test_render_template_pool_proxy_protocol(self): + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " timeout check 31s\n" + " fullconn 98\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" + " server sample_member_id_2 10.0.0.98:82 " + "weight 13 check inter 30s fall 3 rise 2 " + "cookie sample_member_id_2 send-proxy\n\n") + rendered_obj = self.jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_amphora_tuple(), + sample_configs.sample_listener_tuple( + be_proto='PROXY')) + self.assertEqual( + sample_configs.sample_base_expected_config(backend=be), + rendered_obj) + def test_transform_session_persistence(self): in_persistence = sample_configs.sample_session_persistence_tuple() ret = self.jinja_cfg._transform_session_persistence(in_persistence) diff --git a/octavia/tests/unit/common/sample_configs/sample_configs.py b/octavia/tests/unit/common/sample_configs/sample_configs.py index 4db3ce92f3..c29ea70c81 100644 --- a/octavia/tests/unit/common/sample_configs/sample_configs.py +++ b/octavia/tests/unit/common/sample_configs/sample_configs.py @@ -398,9 +398,11 @@ def sample_vip_tuple(): def sample_listener_tuple(proto=None, monitor=True, persistence=True, persistence_type=None, persistence_cookie=None, tls=False, sni=False, peer_port=None, topology=None, - l7=False, enabled=True, insert_headers=None): + l7=False, enabled=True, insert_headers=None, + be_proto=None): proto = 'HTTP' if proto is None else proto - be_proto = 'HTTP' if proto is 'TERMINATED_HTTPS' else proto + if be_proto is None: + be_proto = 'HTTP' if proto is 'TERMINATED_HTTPS' else proto topology = 'SINGLE' if topology is None else topology port = '443' if proto is 'HTTPS' or proto is 'TERMINATED_HTTPS' else '80' peer_port = 1024 if peer_port is None else peer_port diff --git a/releasenotes/notes/support-proxy-protocol-cc5991175a110619.yaml b/releasenotes/notes/support-proxy-protocol-cc5991175a110619.yaml new file mode 100644 index 0000000000..f18fad1a38 --- /dev/null +++ b/releasenotes/notes/support-proxy-protocol-cc5991175a110619.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add support PROXY protocol for lbaas pool in octavia