Add proxy v2 protocol support
This patch adds support for the proxy protocol v2 on pools. Depends-On: https://review.opendev.org/747296 Change-Id: Ic112c5e71ee9b6433b307fdf27059f217ba4136e Story: 2005611 Task: 30858
This commit is contained in:
parent
46de66b240
commit
7fe78c5943
@ -1236,7 +1236,7 @@ protocol:
|
|||||||
protocol-pools:
|
protocol-pools:
|
||||||
description: |
|
description: |
|
||||||
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``PROXY``,
|
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``PROXY``,
|
||||||
``TCP``, or ``UDP``.
|
``PROXYV2``, ``TCP``, or ``UDP``.
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
@ -102,7 +102,8 @@ is ready for further configuration.
|
|||||||
At a minimum, you must specify these pool attributes:
|
At a minimum, you must specify these pool attributes:
|
||||||
|
|
||||||
- ``protocol`` The protocol for which this pool and its members
|
- ``protocol`` The protocol for which this pool and its members
|
||||||
listen. A valid value is ``HTTP``, ``HTTPS``, ``PROXY``, ``TCP``, or ``UDP``.
|
listen. A valid value is ``HTTP``, ``HTTPS``, ``PROXY``, ``PROXYV2``,
|
||||||
|
``TCP``, or ``UDP``.
|
||||||
|
|
||||||
- ``lb_algorithm`` The load-balancer algorithm, such as
|
- ``lb_algorithm`` The load-balancer algorithm, such as
|
||||||
``ROUND_ROBIN``, ``LEAST_CONNECTIONS``, ``SOURCE_IP`` and ``SOURCE_IP_PORT``,
|
``ROUND_ROBIN``, ``LEAST_CONNECTIONS``, ``SOURCE_IP`` and ``SOURCE_IP_PORT``,
|
||||||
|
@ -114,6 +114,14 @@ cli=openstack loadbalancer pool create --protocol PROXY --listener <listener>
|
|||||||
driver.amphora=complete
|
driver.amphora=complete
|
||||||
driver.ovn=missing
|
driver.ovn=missing
|
||||||
|
|
||||||
|
[operation.protocol.PROXYV2]
|
||||||
|
title=protocol - PROXYV2
|
||||||
|
status=optional
|
||||||
|
notes=PROXY protocol version 2 support for the pool.
|
||||||
|
cli=openstack loadbalancer pool create --protocol PROXYV2 --listener <listener>
|
||||||
|
driver.amphora=complete
|
||||||
|
driver.ovn=missing
|
||||||
|
|
||||||
[operation.protocol.TCP]
|
[operation.protocol.TCP]
|
||||||
title=protocol - TCP
|
title=protocol - TCP
|
||||||
status=optional
|
status=optional
|
||||||
|
@ -128,6 +128,9 @@ class RootController(object):
|
|||||||
self._add_a_version(versions, 'v2.20', 'v2', 'SUPPORTED',
|
self._add_a_version(versions, 'v2.20', 'v2', 'SUPPORTED',
|
||||||
'2020-08-02T00:00:00Z', host_url)
|
'2020-08-02T00:00:00Z', host_url)
|
||||||
# Amphora delete
|
# Amphora delete
|
||||||
self._add_a_version(versions, 'v2.21', 'v2', 'CURRENT',
|
self._add_a_version(versions, 'v2.21', 'v2', 'SUPPORTED',
|
||||||
'2020-09-03T00:00:00Z', host_url)
|
'2020-09-03T00:00:00Z', host_url)
|
||||||
|
# Add PROXYV2 pool protocol
|
||||||
|
self._add_a_version(versions, 'v2.22', 'v2', 'CURRENT',
|
||||||
|
'2020-09-04T00:00:00Z', host_url)
|
||||||
return {'versions': versions}
|
return {'versions': versions}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from octavia_lib.common import constants as lib_constants
|
||||||
from wsme import types as wtypes
|
from wsme import types as wtypes
|
||||||
|
|
||||||
from octavia.api.common import types
|
from octavia.api.common import types
|
||||||
@ -146,8 +147,9 @@ class PoolPOST(BasePoolType):
|
|||||||
admin_state_up = wtypes.wsattr(bool, default=True)
|
admin_state_up = wtypes.wsattr(bool, default=True)
|
||||||
listener_id = wtypes.wsattr(wtypes.UuidType())
|
listener_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
loadbalancer_id = wtypes.wsattr(wtypes.UuidType())
|
loadbalancer_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
protocol = wtypes.wsattr(wtypes.Enum(str, *constants.SUPPORTED_PROTOCOLS),
|
protocol = wtypes.wsattr(
|
||||||
mandatory=True)
|
wtypes.Enum(str, *lib_constants.POOL_SUPPORTED_PROTOCOLS),
|
||||||
|
mandatory=True)
|
||||||
lb_algorithm = wtypes.wsattr(
|
lb_algorithm = wtypes.wsattr(
|
||||||
wtypes.Enum(str, *constants.SUPPORTED_LB_ALGORITHMS),
|
wtypes.Enum(str, *constants.SUPPORTED_LB_ALGORITHMS),
|
||||||
mandatory=True)
|
mandatory=True)
|
||||||
@ -198,7 +200,8 @@ class PoolSingleCreate(BasePoolType):
|
|||||||
name = wtypes.wsattr(wtypes.StringType(max_length=255))
|
name = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||||
description = wtypes.wsattr(wtypes.StringType(max_length=255))
|
description = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||||
admin_state_up = wtypes.wsattr(bool, default=True)
|
admin_state_up = wtypes.wsattr(bool, default=True)
|
||||||
protocol = wtypes.wsattr(wtypes.Enum(str, *constants.SUPPORTED_PROTOCOLS))
|
protocol = wtypes.wsattr(
|
||||||
|
wtypes.Enum(str, *lib_constants.POOL_SUPPORTED_PROTOCOLS))
|
||||||
lb_algorithm = wtypes.wsattr(
|
lb_algorithm = wtypes.wsattr(
|
||||||
wtypes.Enum(str, *constants.SUPPORTED_LB_ALGORITHMS))
|
wtypes.Enum(str, *constants.SUPPORTED_LB_ALGORITHMS))
|
||||||
session_persistence = wtypes.wsattr(SessionPersistencePOST)
|
session_persistence = wtypes.wsattr(SessionPersistencePOST)
|
||||||
|
@ -207,10 +207,13 @@ UPDATE_HEALTH = 'UPDATE_HEALTH'
|
|||||||
|
|
||||||
VALID_LISTENER_POOL_PROTOCOL_MAP = {
|
VALID_LISTENER_POOL_PROTOCOL_MAP = {
|
||||||
PROTOCOL_TCP: [PROTOCOL_HTTP, PROTOCOL_HTTPS,
|
PROTOCOL_TCP: [PROTOCOL_HTTP, PROTOCOL_HTTPS,
|
||||||
PROTOCOL_PROXY, PROTOCOL_TCP],
|
PROTOCOL_PROXY, lib_consts.PROTOCOL_PROXYV2, PROTOCOL_TCP],
|
||||||
PROTOCOL_HTTP: [PROTOCOL_HTTP, PROTOCOL_PROXY],
|
PROTOCOL_HTTP: [PROTOCOL_HTTP, PROTOCOL_PROXY,
|
||||||
PROTOCOL_HTTPS: [PROTOCOL_HTTPS, PROTOCOL_PROXY, PROTOCOL_TCP],
|
lib_consts.PROTOCOL_PROXYV2],
|
||||||
PROTOCOL_TERMINATED_HTTPS: [PROTOCOL_HTTP, PROTOCOL_PROXY],
|
PROTOCOL_HTTPS: [PROTOCOL_HTTPS, PROTOCOL_PROXY,
|
||||||
|
lib_consts.PROTOCOL_PROXYV2, PROTOCOL_TCP],
|
||||||
|
PROTOCOL_TERMINATED_HTTPS: [PROTOCOL_HTTP, PROTOCOL_PROXY,
|
||||||
|
lib_consts.PROTOCOL_PROXYV2],
|
||||||
PROTOCOL_UDP: [PROTOCOL_UDP]}
|
PROTOCOL_UDP: [PROTOCOL_UDP]}
|
||||||
|
|
||||||
# API Integer Ranges
|
# API Integer Ranges
|
||||||
@ -810,6 +813,7 @@ L4_PROTOCOL_MAP = {
|
|||||||
PROTOCOL_HTTPS: PROTOCOL_TCP,
|
PROTOCOL_HTTPS: PROTOCOL_TCP,
|
||||||
PROTOCOL_TERMINATED_HTTPS: PROTOCOL_TCP,
|
PROTOCOL_TERMINATED_HTTPS: PROTOCOL_TCP,
|
||||||
PROTOCOL_PROXY: PROTOCOL_TCP,
|
PROTOCOL_PROXY: PROTOCOL_TCP,
|
||||||
|
lib_consts.PROTOCOL_PROXYV2: PROTOCOL_TCP,
|
||||||
PROTOCOL_UDP: PROTOCOL_UDP,
|
PROTOCOL_UDP: PROTOCOL_UDP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +284,9 @@ class JinjaTemplater(object):
|
|||||||
os.path.join(self.base_crt_dir, loadbalancer.id,
|
os.path.join(self.base_crt_dir, loadbalancer.id,
|
||||||
tls_certs[listener.client_crl_container_id]))
|
tls_certs[listener.client_crl_container_id]))
|
||||||
|
|
||||||
|
tls_enabled = False
|
||||||
if listener.protocol == constants.PROTOCOL_TERMINATED_HTTPS:
|
if listener.protocol == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
|
tls_enabled = True
|
||||||
if listener.tls_ciphers is not None:
|
if listener.tls_ciphers is not None:
|
||||||
ret_value['tls_ciphers'] = listener.tls_ciphers
|
ret_value['tls_ciphers'] = listener.tls_ciphers
|
||||||
if listener.tls_versions is not None:
|
if listener.tls_versions is not None:
|
||||||
@ -300,7 +302,7 @@ class JinjaTemplater(object):
|
|||||||
if tls_certs is not None and tls_certs.get(pool.id):
|
if tls_certs is not None and tls_certs.get(pool.id):
|
||||||
kwargs = {'pool_tls_certs': tls_certs.get(pool.id)}
|
kwargs = {'pool_tls_certs': tls_certs.get(pool.id)}
|
||||||
pools.append(self._transform_pool(
|
pools.append(self._transform_pool(
|
||||||
pool, feature_compatibility, **kwargs))
|
pool, feature_compatibility, tls_enabled, **kwargs))
|
||||||
ret_value['pools'] = pools
|
ret_value['pools'] = pools
|
||||||
policy_gen = (policy for policy in listener.l7policies if
|
policy_gen = (policy for policy in listener.l7policies if
|
||||||
policy.provisioning_status != constants.PENDING_DELETE)
|
policy.provisioning_status != constants.PENDING_DELETE)
|
||||||
@ -311,21 +313,27 @@ class JinjaTemplater(object):
|
|||||||
break
|
break
|
||||||
|
|
||||||
l7policies = [self._transform_l7policy(
|
l7policies = [self._transform_l7policy(
|
||||||
x, feature_compatibility, tls_certs)
|
x, feature_compatibility, tls_enabled, tls_certs)
|
||||||
for x in policy_gen]
|
for x in policy_gen]
|
||||||
ret_value['l7policies'] = l7policies
|
ret_value['l7policies'] = l7policies
|
||||||
return ret_value
|
return ret_value
|
||||||
|
|
||||||
def _transform_pool(self, pool, feature_compatibility,
|
def _transform_pool(self, pool, feature_compatibility,
|
||||||
pool_tls_certs=None):
|
listener_tls_enabled, pool_tls_certs=None):
|
||||||
"""Transforms a pool into an object that will
|
"""Transforms a pool into an object that will
|
||||||
|
|
||||||
be processed by the templating system
|
be processed by the templating system
|
||||||
"""
|
"""
|
||||||
|
proxy_protocol_version = None
|
||||||
|
if pool.protocol == constants.PROTOCOL_PROXY:
|
||||||
|
proxy_protocol_version = 1
|
||||||
|
if pool.protocol == lib_consts.PROTOCOL_PROXYV2:
|
||||||
|
proxy_protocol_version = 2
|
||||||
ret_value = {
|
ret_value = {
|
||||||
'id': pool.id,
|
'id': pool.id,
|
||||||
'protocol': PROTOCOL_MAP[pool.protocol],
|
'protocol': PROTOCOL_MAP[pool.protocol],
|
||||||
'proxy_protocol': pool.protocol == constants.PROTOCOL_PROXY,
|
'proxy_protocol': proxy_protocol_version,
|
||||||
|
'listener_tls_enabled': listener_tls_enabled,
|
||||||
'lb_algorithm': BALANCE_MAP.get(pool.lb_algorithm, 'roundrobin'),
|
'lb_algorithm': BALANCE_MAP.get(pool.lb_algorithm, 'roundrobin'),
|
||||||
'members': [],
|
'members': [],
|
||||||
'health_monitor': '',
|
'health_monitor': '',
|
||||||
@ -425,7 +433,7 @@ class JinjaTemplater(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _transform_l7policy(self, l7policy, feature_compatibility,
|
def _transform_l7policy(self, l7policy, feature_compatibility,
|
||||||
tls_certs=None):
|
listener_tls_enabled, tls_certs=None):
|
||||||
"""Transforms an L7 policy into an object that will
|
"""Transforms an L7 policy into an object that will
|
||||||
|
|
||||||
be processed by the templating system
|
be processed by the templating system
|
||||||
@ -446,7 +454,8 @@ class JinjaTemplater(object):
|
|||||||
kwargs = {'pool_tls_certs':
|
kwargs = {'pool_tls_certs':
|
||||||
tls_certs.get(l7policy.redirect_pool.id)}
|
tls_certs.get(l7policy.redirect_pool.id)}
|
||||||
ret_value['redirect_pool'] = self._transform_pool(
|
ret_value['redirect_pool'] = self._transform_pool(
|
||||||
l7policy.redirect_pool, feature_compatibility, **kwargs)
|
l7policy.redirect_pool, feature_compatibility,
|
||||||
|
listener_tls_enabled, **kwargs)
|
||||||
else:
|
else:
|
||||||
ret_value['redirect_pool'] = None
|
ret_value['redirect_pool'] = None
|
||||||
if (l7policy.action in [constants.L7POLICY_ACTION_REDIRECT_TO_URL,
|
if (l7policy.action in [constants.L7POLICY_ACTION_REDIRECT_TO_URL,
|
||||||
|
@ -217,8 +217,14 @@ frontend {{ listener.id }}
|
|||||||
{% else %}
|
{% else %}
|
||||||
{% set persistence_opt = "" %}
|
{% set persistence_opt = "" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if pool.proxy_protocol %}
|
{% if pool.proxy_protocol == 1 %}
|
||||||
{% set proxy_protocol_opt = " send-proxy" %}
|
{% set proxy_protocol_opt = " send-proxy" %}
|
||||||
|
{% elif pool.proxy_protocol == 2 %}
|
||||||
|
{% if pool.listener_tls_enabled %}
|
||||||
|
{% set proxy_protocol_opt = " send-proxy-v2-ssl-cn" %}
|
||||||
|
{% else %}
|
||||||
|
{% set proxy_protocol_opt = " send-proxy-v2" %}
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% set proxy_protocol_opt = "" %}
|
{% set proxy_protocol_opt = "" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -288,7 +294,7 @@ frontend {{ listener.id }}
|
|||||||
|
|
||||||
{% macro backend_macro(constants, lib_consts, listener, pool, loadbalancer) %}
|
{% macro backend_macro(constants, lib_consts, listener, pool, loadbalancer) %}
|
||||||
backend {{ pool.id }}:{{ listener.id }}
|
backend {{ pool.id }}:{{ listener.id }}
|
||||||
{% if pool.protocol.lower() == constants.PROTOCOL_PROXY.lower() %}
|
{% if pool.proxy_protocol is not none %}
|
||||||
mode {{ listener.protocol_mode }}
|
mode {{ listener.protocol_mode }}
|
||||||
{% else %}
|
{% else %}
|
||||||
mode {{ pool.protocol }}
|
mode {{ pool.protocol }}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
# Copyright 2020 Red Hat, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# 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 v2 pool protocol
|
||||||
|
|
||||||
|
Revision ID: e6ee84f0abf3
|
||||||
|
Revises: 2ab994dd3ec2
|
||||||
|
Create Date: 2020-08-24 11:12:46.745185
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import sql
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'e6ee84f0abf3'
|
||||||
|
down_revision = '2ab994dd3ec2'
|
||||||
|
|
||||||
|
|
||||||
|
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': 'PROXYV2'}
|
||||||
|
]
|
||||||
|
)
|
@ -11,6 +11,7 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
from octavia_lib.common import constants as lib_constants
|
||||||
|
|
||||||
from octavia.common import constants
|
from octavia.common import constants
|
||||||
|
|
||||||
@ -280,4 +281,5 @@ INVALID_LISTENER_POOL_PROTOCOL_MAP = {
|
|||||||
constants.PROTOCOL_HTTP,
|
constants.PROTOCOL_HTTP,
|
||||||
constants.PROTOCOL_HTTPS,
|
constants.PROTOCOL_HTTPS,
|
||||||
constants.PROTOCOL_TERMINATED_HTTPS,
|
constants.PROTOCOL_TERMINATED_HTTPS,
|
||||||
constants.PROTOCOL_PROXY]}
|
constants.PROTOCOL_PROXY,
|
||||||
|
lib_constants.PROTOCOL_PROXYV2]}
|
||||||
|
@ -45,7 +45,7 @@ class TestRootController(base_db_test.OctaviaDBTestBase):
|
|||||||
def test_api_versions(self):
|
def test_api_versions(self):
|
||||||
versions = self._get_versions_with_config()
|
versions = self._get_versions_with_config()
|
||||||
version_ids = tuple(v.get('id') for v in versions)
|
version_ids = tuple(v.get('id') for v in versions)
|
||||||
self.assertEqual(22, len(version_ids))
|
self.assertEqual(23, len(version_ids))
|
||||||
self.assertIn('v2.0', version_ids)
|
self.assertIn('v2.0', version_ids)
|
||||||
self.assertIn('v2.1', version_ids)
|
self.assertIn('v2.1', version_ids)
|
||||||
self.assertIn('v2.2', version_ids)
|
self.assertIn('v2.2', version_ids)
|
||||||
@ -68,6 +68,7 @@ class TestRootController(base_db_test.OctaviaDBTestBase):
|
|||||||
self.assertIn('v2.19', version_ids)
|
self.assertIn('v2.19', version_ids)
|
||||||
self.assertIn('v2.20', version_ids)
|
self.assertIn('v2.20', version_ids)
|
||||||
self.assertIn('v2.21', version_ids)
|
self.assertIn('v2.21', version_ids)
|
||||||
|
self.assertIn('v2.22', version_ids)
|
||||||
|
|
||||||
# Each version should have a 'self' 'href' to the API version URL
|
# Each version should have a 'self' 'href' to the API version URL
|
||||||
# [{u'rel': u'self', u'href': u'http://localhost/v2'}]
|
# [{u'rel': u'self', u'href': u'http://localhost/v2'}]
|
||||||
|
@ -1303,21 +1303,27 @@ class TestL7Policy(base.BaseAPITest):
|
|||||||
self.set_object_status(self.lb_repo, self.lb_id)
|
self.set_object_status(self.lb_repo, self.lb_id)
|
||||||
port = port + 1
|
port = port + 1
|
||||||
for pool_proto in invalid_map[listener_proto]:
|
for pool_proto in invalid_map[listener_proto]:
|
||||||
pool = self.create_pool(
|
if pool_proto == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
self.lb_id, pool_proto,
|
pool = self.create_pool(
|
||||||
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
self.lb_id, pool_proto,
|
||||||
self.set_object_status(self.lb_repo, self.lb_id)
|
constants.LB_ALGORITHM_ROUND_ROBIN, status=400)
|
||||||
|
self.assertIn("Invalid input", pool['faultstring'])
|
||||||
|
else:
|
||||||
|
pool = self.create_pool(
|
||||||
|
self.lb_id, pool_proto,
|
||||||
|
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
||||||
|
self.set_object_status(self.lb_repo, self.lb_id)
|
||||||
|
|
||||||
l7policy['listener_id'] = listener.get('id')
|
l7policy['listener_id'] = listener.get('id')
|
||||||
l7policy['redirect_pool_id'] = pool.get('id')
|
l7policy['redirect_pool_id'] = pool.get('id')
|
||||||
expect_error_msg = ("Validation failure: The pool protocol "
|
expect_error_msg = (
|
||||||
"'%s' is invalid while the listener "
|
"Validation failure: The pool protocol '%s' is "
|
||||||
"protocol is '%s'.") % (pool_proto,
|
"invalid while the listener protocol is '%s'.") % (
|
||||||
listener_proto)
|
pool_proto, listener_proto)
|
||||||
res = self.post(self.L7POLICIES_PATH,
|
res = self.post(self.L7POLICIES_PATH,
|
||||||
self._build_body(l7policy), status=400)
|
self._build_body(l7policy), status=400)
|
||||||
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
||||||
self.assert_correct_status(lb_id=self.lb_id)
|
self.assert_correct_status(lb_id=self.lb_id)
|
||||||
|
|
||||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||||
def test_listener_pool_protocol_map_put(self, mock_cert_data):
|
def test_listener_pool_protocol_map_put(self, mock_cert_data):
|
||||||
@ -1361,21 +1367,27 @@ class TestL7Policy(base.BaseAPITest):
|
|||||||
self.set_object_status(self.lb_repo, self.lb_id)
|
self.set_object_status(self.lb_repo, self.lb_id)
|
||||||
port = port + 1
|
port = port + 1
|
||||||
for pool_proto in invalid_map[listener_proto]:
|
for pool_proto in invalid_map[listener_proto]:
|
||||||
pool = self.create_pool(
|
if pool_proto == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
self.lb_id, pool_proto,
|
pool = self.create_pool(
|
||||||
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
self.lb_id, pool_proto,
|
||||||
self.set_object_status(self.lb_repo, self.lb_id)
|
constants.LB_ALGORITHM_ROUND_ROBIN, status=400)
|
||||||
l7policy = self.create_l7policy(
|
self.assertIn("Invalid input", pool['faultstring'])
|
||||||
listener.get('id'),
|
else:
|
||||||
constants.L7POLICY_ACTION_REJECT).get(self.root_tag)
|
pool = self.create_pool(
|
||||||
self.set_object_status(self.lb_repo, self.lb_id)
|
self.lb_id, pool_proto,
|
||||||
new_l7policy['redirect_pool_id'] = pool.get('id')
|
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
||||||
expect_error_msg = ("Validation failure: The pool protocol "
|
self.set_object_status(self.lb_repo, self.lb_id)
|
||||||
"'%s' is invalid while the listener "
|
l7policy = self.create_l7policy(
|
||||||
"protocol is '%s'.") % (pool_proto,
|
listener.get('id'),
|
||||||
listener_proto)
|
constants.L7POLICY_ACTION_REJECT).get(self.root_tag)
|
||||||
res = self.put(self.L7POLICY_PATH.format(
|
self.set_object_status(self.lb_repo, self.lb_id)
|
||||||
l7policy_id=l7policy.get('id')),
|
new_l7policy['redirect_pool_id'] = pool.get('id')
|
||||||
self._build_body(new_l7policy), status=400)
|
expect_error_msg = (
|
||||||
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
"Validation failure: The pool protocol '%s' is "
|
||||||
self.assert_correct_status(lb_id=self.lb_id)
|
"invalid while the listener protocol is '%s'.") % (
|
||||||
|
pool_proto, listener_proto)
|
||||||
|
res = self.put(self.L7POLICY_PATH.format(
|
||||||
|
l7policy_id=l7policy.get('id')),
|
||||||
|
self._build_body(new_l7policy), status=400)
|
||||||
|
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
||||||
|
self.assert_correct_status(lb_id=self.lb_id)
|
||||||
|
@ -2849,23 +2849,29 @@ class TestListener(base.BaseAPITest):
|
|||||||
for listener_proto in invalid_map:
|
for listener_proto in invalid_map:
|
||||||
for pool_proto in invalid_map[listener_proto]:
|
for pool_proto in invalid_map[listener_proto]:
|
||||||
port = port + 1
|
port = port + 1
|
||||||
pool = self.create_pool(
|
if pool_proto == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
self.lb_id, pool_proto,
|
pool = self.create_pool(
|
||||||
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
self.lb_id, pool_proto,
|
||||||
self.set_object_status(self.lb_repo, self.lb_id)
|
constants.LB_ALGORITHM_ROUND_ROBIN, status=400)
|
||||||
expect_error_msg = ("Validation failure: The pool protocol "
|
self.assertIn("Invalid input", pool['faultstring'])
|
||||||
"'%s' is invalid while the listener "
|
else:
|
||||||
"protocol is '%s'.") % (pool_proto,
|
pool = self.create_pool(
|
||||||
listener_proto)
|
self.lb_id, pool_proto,
|
||||||
listener = {'protocol': listener_proto,
|
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
||||||
'protocol_port': port,
|
self.set_object_status(self.lb_repo, self.lb_id)
|
||||||
'loadbalancer_id': self.lb_id,
|
expect_error_msg = (
|
||||||
'default_pool_id': pool.get('id')}
|
"Validation failure: The pool protocol '%s' is "
|
||||||
body = self._build_body(listener)
|
"invalid while the listener protocol is '%s'.") % (
|
||||||
res = self.post(self.LISTENERS_PATH, body,
|
pool_proto, listener_proto)
|
||||||
status=400, expect_errors=True)
|
listener = {'protocol': listener_proto,
|
||||||
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
'protocol_port': port,
|
||||||
self.assert_correct_status(lb_id=self.lb_id)
|
'loadbalancer_id': self.lb_id,
|
||||||
|
'default_pool_id': pool.get('id')}
|
||||||
|
body = self._build_body(listener)
|
||||||
|
res = self.post(self.LISTENERS_PATH, body,
|
||||||
|
status=400, expect_errors=True)
|
||||||
|
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
||||||
|
self.assert_correct_status(lb_id=self.lb_id)
|
||||||
|
|
||||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||||
def test_listener_pool_protocol_map_put(self, mock_cert_data):
|
def test_listener_pool_protocol_map_put(self, mock_cert_data):
|
||||||
@ -2907,13 +2913,20 @@ class TestListener(base.BaseAPITest):
|
|||||||
"'%s' is invalid while the listener "
|
"'%s' is invalid while the listener "
|
||||||
"protocol is '%s'.") % (pool_proto,
|
"protocol is '%s'.") % (pool_proto,
|
||||||
listener_proto)
|
listener_proto)
|
||||||
pool = self.create_pool(
|
if pool_proto == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
self.lb_id, pool_proto,
|
pool = self.create_pool(
|
||||||
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
self.lb_id, pool_proto,
|
||||||
self.set_object_status(self.lb_repo, self.lb_id)
|
constants.LB_ALGORITHM_ROUND_ROBIN, status=400)
|
||||||
new_listener = {'default_pool_id': pool.get('id')}
|
self.assertIn("Invalid input", pool['faultstring'])
|
||||||
res = self.put(
|
else:
|
||||||
self.LISTENER_PATH.format(listener_id=listener.get('id')),
|
pool = self.create_pool(
|
||||||
self._build_body(new_listener), status=400)
|
self.lb_id, pool_proto,
|
||||||
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
||||||
self.assert_correct_status(lb_id=self.lb_id)
|
self.set_object_status(self.lb_repo, self.lb_id)
|
||||||
|
new_listener = {'default_pool_id': pool.get('id')}
|
||||||
|
res = self.put(
|
||||||
|
self.LISTENER_PATH.format(
|
||||||
|
listener_id=listener.get('id')),
|
||||||
|
self._build_body(new_listener), status=400)
|
||||||
|
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
||||||
|
self.assert_correct_status(lb_id=self.lb_id)
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
from octavia_lib.common import constants as lib_constants
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_config import fixture as oslo_fixture
|
from oslo_config import fixture as oslo_fixture
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
@ -765,6 +766,30 @@ class TestPool(base.BaseAPITest):
|
|||||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||||
pool_id=api_pool.get('id'))
|
pool_id=api_pool.get('id'))
|
||||||
|
|
||||||
|
def test_create_with_proxy_v2_protocol(self):
|
||||||
|
api_pool = self.create_pool(
|
||||||
|
self.lb_id,
|
||||||
|
lib_constants.PROTOCOL_PROXYV2,
|
||||||
|
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(lib_constants.PROTOCOL_PROXYV2,
|
||||||
|
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):
|
def test_create_sans_listener(self):
|
||||||
api_pool = self.create_pool(
|
api_pool = self.create_pool(
|
||||||
self.lb_id,
|
self.lb_id,
|
||||||
@ -2382,5 +2407,10 @@ class TestPool(base.BaseAPITest):
|
|||||||
lb_pool['listener_id'] = listener.get('id')
|
lb_pool['listener_id'] = listener.get('id')
|
||||||
res = self.post(self.POOLS_PATH, self._build_body(lb_pool),
|
res = self.post(self.POOLS_PATH, self._build_body(lb_pool),
|
||||||
status=400, expect_errors=True)
|
status=400, expect_errors=True)
|
||||||
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
if pool_proto == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
|
self.assertIn('Invalid input',
|
||||||
|
res.json['faultstring'])
|
||||||
|
else:
|
||||||
|
self.assertEqual(expect_error_msg,
|
||||||
|
res.json['faultstring'])
|
||||||
self.assert_correct_status(lb_id=self.lb_id)
|
self.assert_correct_status(lb_id=self.lb_id)
|
||||||
|
@ -1288,18 +1288,18 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
|
|
||||||
def test_transform_pool(self):
|
def test_transform_pool(self):
|
||||||
in_pool = sample_configs_combined.sample_pool_tuple()
|
in_pool = sample_configs_combined.sample_pool_tuple()
|
||||||
ret = self.jinja_cfg._transform_pool(in_pool, {})
|
ret = self.jinja_cfg._transform_pool(in_pool, {}, False)
|
||||||
self.assertEqual(sample_configs_combined.RET_POOL_1, ret)
|
self.assertEqual(sample_configs_combined.RET_POOL_1, ret)
|
||||||
|
|
||||||
def test_transform_pool_2(self):
|
def test_transform_pool_2(self):
|
||||||
in_pool = sample_configs_combined.sample_pool_tuple(sample_pool=2)
|
in_pool = sample_configs_combined.sample_pool_tuple(sample_pool=2)
|
||||||
ret = self.jinja_cfg._transform_pool(in_pool, {})
|
ret = self.jinja_cfg._transform_pool(in_pool, {}, False)
|
||||||
self.assertEqual(sample_configs_combined.RET_POOL_2, ret)
|
self.assertEqual(sample_configs_combined.RET_POOL_2, ret)
|
||||||
|
|
||||||
def test_transform_pool_http_reuse(self):
|
def test_transform_pool_http_reuse(self):
|
||||||
in_pool = sample_configs_combined.sample_pool_tuple(sample_pool=2)
|
in_pool = sample_configs_combined.sample_pool_tuple(sample_pool=2)
|
||||||
ret = self.jinja_cfg._transform_pool(
|
ret = self.jinja_cfg._transform_pool(
|
||||||
in_pool, {constants.HTTP_REUSE: True})
|
in_pool, {constants.HTTP_REUSE: True}, False)
|
||||||
expected_config = copy.copy(sample_configs_combined.RET_POOL_2)
|
expected_config = copy.copy(sample_configs_combined.RET_POOL_2)
|
||||||
expected_config[constants.HTTP_REUSE] = True
|
expected_config[constants.HTTP_REUSE] = True
|
||||||
self.assertEqual(expected_config, ret)
|
self.assertEqual(expected_config, ret)
|
||||||
@ -1309,7 +1309,7 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
cert_path = os.path.join(self.jinja_cfg.base_crt_dir,
|
cert_path = os.path.join(self.jinja_cfg.base_crt_dir,
|
||||||
'test_listener_id', 'pool_cert.pem')
|
'test_listener_id', 'pool_cert.pem')
|
||||||
ret = self.jinja_cfg._transform_pool(
|
ret = self.jinja_cfg._transform_pool(
|
||||||
in_pool, {}, pool_tls_certs={'client_cert': cert_path})
|
in_pool, {}, False, pool_tls_certs={'client_cert': cert_path})
|
||||||
expected_config = copy.copy(sample_configs_combined.RET_POOL_1)
|
expected_config = copy.copy(sample_configs_combined.RET_POOL_1)
|
||||||
expected_config['client_cert'] = cert_path
|
expected_config['client_cert'] = cert_path
|
||||||
self.assertEqual(expected_config, ret)
|
self.assertEqual(expected_config, ret)
|
||||||
@ -1383,25 +1383,25 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
def test_transform_l7policy(self):
|
def test_transform_l7policy(self):
|
||||||
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
||||||
'sample_l7policy_id_1')
|
'sample_l7policy_id_1')
|
||||||
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
|
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}, False)
|
||||||
self.assertEqual(sample_configs_combined.RET_L7POLICY_1, ret)
|
self.assertEqual(sample_configs_combined.RET_L7POLICY_1, ret)
|
||||||
|
|
||||||
def test_transform_l7policy_2_8(self):
|
def test_transform_l7policy_2_8(self):
|
||||||
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
||||||
'sample_l7policy_id_2', sample_policy=2)
|
'sample_l7policy_id_2', sample_policy=2)
|
||||||
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
|
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}, False)
|
||||||
self.assertEqual(sample_configs_combined.RET_L7POLICY_2, ret)
|
self.assertEqual(sample_configs_combined.RET_L7POLICY_2, ret)
|
||||||
|
|
||||||
# test invalid action without redirect_http_code
|
# test invalid action without redirect_http_code
|
||||||
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
||||||
'sample_l7policy_id_8', sample_policy=2, redirect_http_code=None)
|
'sample_l7policy_id_8', sample_policy=2, redirect_http_code=None)
|
||||||
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
|
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}, False)
|
||||||
self.assertEqual(sample_configs_combined.RET_L7POLICY_8, ret)
|
self.assertEqual(sample_configs_combined.RET_L7POLICY_8, ret)
|
||||||
|
|
||||||
def test_transform_l7policy_disabled_rule(self):
|
def test_transform_l7policy_disabled_rule(self):
|
||||||
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
in_l7policy = sample_configs_combined.sample_l7policy_tuple(
|
||||||
'sample_l7policy_id_6', sample_policy=6)
|
'sample_l7policy_id_6', sample_policy=6)
|
||||||
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
|
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}, False)
|
||||||
self.assertEqual(sample_configs_combined.RET_L7POLICY_6, ret)
|
self.assertEqual(sample_configs_combined.RET_L7POLICY_6, ret)
|
||||||
|
|
||||||
def test_escape_haproxy_config_string(self):
|
def test_escape_haproxy_config_string(self):
|
||||||
|
@ -117,8 +117,9 @@ RET_MEMBER_3 = {
|
|||||||
RET_POOL_1 = {
|
RET_POOL_1 = {
|
||||||
'id': 'sample_pool_id_1',
|
'id': 'sample_pool_id_1',
|
||||||
'protocol': 'http',
|
'protocol': 'http',
|
||||||
'proxy_protocol': False,
|
'proxy_protocol': None,
|
||||||
'lb_algorithm': 'roundrobin',
|
'lb_algorithm': 'roundrobin',
|
||||||
|
'listener_tls_enabled': False,
|
||||||
'members': [RET_MEMBER_1, RET_MEMBER_2],
|
'members': [RET_MEMBER_1, RET_MEMBER_2],
|
||||||
'health_monitor': RET_MONITOR_1,
|
'health_monitor': RET_MONITOR_1,
|
||||||
'session_persistence': RET_PERSISTENCE,
|
'session_persistence': RET_PERSISTENCE,
|
||||||
@ -134,8 +135,9 @@ RET_POOL_1 = {
|
|||||||
RET_POOL_2 = {
|
RET_POOL_2 = {
|
||||||
'id': 'sample_pool_id_2',
|
'id': 'sample_pool_id_2',
|
||||||
'protocol': 'http',
|
'protocol': 'http',
|
||||||
'proxy_protocol': False,
|
'proxy_protocol': None,
|
||||||
'lb_algorithm': 'roundrobin',
|
'lb_algorithm': 'roundrobin',
|
||||||
|
'listener_tls_enabled': False,
|
||||||
'members': [RET_MEMBER_3],
|
'members': [RET_MEMBER_3],
|
||||||
'health_monitor': RET_MONITOR_2,
|
'health_monitor': RET_MONITOR_2,
|
||||||
'session_persistence': RET_PERSISTENCE,
|
'session_persistence': RET_PERSISTENCE,
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added support for proxy protocol version 2.
|
Loading…
Reference in New Issue
Block a user