Merge "Allow listeners for different protocols on the same port"

This commit is contained in:
Zuul 2019-08-22 19:44:55 +00:00 committed by Gerrit Code Review
commit 7bffc1391b
7 changed files with 113 additions and 4 deletions

View File

@ -229,6 +229,40 @@ class ListenersController(base.BaseController):
listener_dict.get('client_ca_tls_certificate_id'),
listener_dict.get('client_crl_container_id', None))
# Validate that the L4 protocol (UDP or TCP) is not already used for
# the specified protocol_port in this load balancer
pcontext = pecan.request.context
query_filter = {
'project_id': listener_dict['project_id'],
'load_balancer_id': listener_dict['load_balancer_id'],
'protocol_port': listener_dict['protocol_port']
}
# Get listeners on the same load balancer that use the same
# protocol port
db_listeners = self.repositories.listener.get_all_API_list(
lock_session, show_deleted=False,
pagination_helper=pcontext.get(constants.PAGINATION_HELPER),
**query_filter)[0]
if db_listeners:
l4_protocol = constants.L4_PROTOCOL_MAP[listener_protocol]
# List supported protocols that share the same L4 protocol as our
# new listener
disallowed_protocols = [
p
for p in constants.L4_PROTOCOL_MAP
if constants.L4_PROTOCOL_MAP[p] == l4_protocol
]
for db_l in db_listeners:
# Check if l4 protocol ports conflict
if db_l.protocol in disallowed_protocols:
raise exceptions.DuplicateListenerEntry(
protocol=db_l.protocol,
port=listener_dict.get('protocol_port'))
try:
db_listener = self.repositories.listener.create(
lock_session, **listener_dict)
@ -242,13 +276,14 @@ class ListenersController(base.BaseController):
lock_session, id=db_listener.id)
return db_listener
except odb_exceptions.DBDuplicateEntry as de:
column_list = ['load_balancer_id', 'protocol_port']
column_list = ['load_balancer_id', 'protocol', 'protocol_port']
constraint_list = ['uq_listener_load_balancer_id_protocol_port']
if ['id'] == de.columns:
raise exceptions.IDAlreadyExists()
if (set(column_list) == set(de.columns) or
set(constraint_list) == set(de.columns)):
raise exceptions.DuplicateListenerEntry(
protocol=listener_dict.get('protocol'),
port=listener_dict.get('protocol_port'))
except odb_exceptions.DBError:
raise exceptions.InvalidOption(value=listener_dict.get('protocol'),

View File

@ -674,3 +674,13 @@ TOPIC_AMPHORA_V2 = 'octavia_provisioning_v2'
HAPROXY_HTTP_PROTOCOLS = [lib_consts.PROTOCOL_HTTP,
lib_consts.PROTOCOL_TERMINATED_HTTPS]
# Map each supported protocol to its L4 protocol
L4_PROTOCOL_MAP = {
PROTOCOL_TCP: PROTOCOL_TCP,
PROTOCOL_HTTP: PROTOCOL_TCP,
PROTOCOL_HTTPS: PROTOCOL_TCP,
PROTOCOL_TERMINATED_HTTPS: PROTOCOL_TCP,
PROTOCOL_PROXY: PROTOCOL_TCP,
PROTOCOL_UDP: PROTOCOL_UDP,
}

View File

@ -147,7 +147,8 @@ class CertificateGenerationException(OctaviaException):
class DuplicateListenerEntry(APIException):
msg = _("Another Listener on this Load Balancer "
"is already using protocol_port %(port)d")
"is already using protocol %(protocol)s "
"and protocol_port %(port)d")
code = 409

View File

@ -0,0 +1,35 @@
# Copyright (c) 2019 Red Hat, 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 protocol in listener keys
Revision ID: a5762a99609a
Revises: 392fb85b4419
Create Date: 2019-06-28 14:02:11.415292
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = 'a5762a99609a'
down_revision = '392fb85b4419'
def upgrade():
op.execute("ALTER TABLE `listener` "
"DROP INDEX `uq_listener_load_balancer_id_protocol_port`, "
"ADD UNIQUE KEY "
"`uq_listener_load_balancer_id_protocol_port` "
"(`load_balancer_id`, `protocol`, `protocol_port`)")

View File

@ -455,8 +455,9 @@ class Listener(base_models.BASE, base_models.IdMixin,
__v2_wsme__ = listener.ListenerResponse
__table_args__ = (
sa.UniqueConstraint('load_balancer_id', 'protocol_port',
name='uq_listener_load_balancer_id_protocol_port'),
sa.UniqueConstraint(
'load_balancer_id', 'protocol', 'protocol_port',
name='uq_listener_load_balancer_id_protocol_port'),
)
description = sa.Column(sa.String(255), nullable=True)

View File

@ -1708,6 +1708,28 @@ class TestListener(base.BaseAPITest):
body = self._build_body(listener2_post)
self.post(self.LISTENERS_PATH, body, status=409)
def test_create_listeners_tcp_https_same_port(self):
listener1 = self.create_listener(constants.PROTOCOL_TCP, 80,
self.lb_id)
self.set_lb_status(self.lb_id)
listener2_post = {'protocol': constants.PROTOCOL_HTTPS,
'protocol_port':
listener1['listener']['protocol_port'],
'loadbalancer_id': self.lb_id}
body = self._build_body(listener2_post)
self.post(self.LISTENERS_PATH, body, status=409)
def test_create_listeners_tcp_udp_same_port(self):
listener1 = self.create_listener(constants.PROTOCOL_TCP, 80,
self.lb_id)
self.set_lb_status(self.lb_id)
listener2_post = {'protocol': constants.PROTOCOL_UDP,
'protocol_port':
listener1['listener']['protocol_port'],
'loadbalancer_id': self.lb_id}
body = self._build_body(listener2_post)
self.post(self.LISTENERS_PATH, body, status=201)
def test_delete(self):
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
self.lb_id)

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixed bug which prevented the creation of listeners for different protocols
on the same port (i.e: tcp port 53, and udp port 53).