Implement provider drivers - Listener

This patch adds provider driver support to the Octavia v2
Listener API.

This patch also creates a provider driver for Octavia, fully
implementing the listener methods.

This patch also corrects the basic cookbook example for adding SNI
certificates to a listener.

Follow on patches will implement the remain parts of the API.

Story: 1655768
Task: 5165

Depends-On: https://review.openstack.org/492311
Change-Id: I386097197f51d94b34f9a7fd7af7b36238294faf
This commit is contained in:
Michael Johnson 2018-05-04 11:29:55 -07:00
parent 7b2621fe29
commit 7a24b08434
13 changed files with 468 additions and 259 deletions

View File

@ -461,7 +461,7 @@ listener using Server Name Indication (SNI) technology.
openstack loadbalancer create --name lb1 --vip-subnet-id public-subnet
# Re-run the following until lb1 shows ACTIVE and ONLINE statuses:
openstack loadbalancer show lb1
openstack loadbalancer listener create --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret list | awk '/ tls_secret1 / {print $2}') --sni-container_refs $(openstack secret list | awk '/ tls_secret1 / {print $2}') $(openstack secret list | awk '/ tls_secret2 / {print $2}') lb1
openstack loadbalancer listener create --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret list | awk '/ tls_secret1 / {print $2}') --sni-container-refs $(openstack secret list | awk '/ tls_secret1 / {print $2}') $(openstack secret list | awk '/ tls_secret2 / {print $2}') -- lb1
openstack loadbalancer pool create --name pool1 --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP
openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.10 --protocol-port 80 pool1
openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.11 --protocol-port 80 pool1

View File

@ -90,7 +90,14 @@ class AmphoraProviderDriver(driver_base.ProviderDriver):
self.client.cast({}, 'delete_listener', **payload)
def listener_update(self, listener):
pass
listener_dict = listener.to_dict()
if 'admin_state_up' in listener_dict:
listener_dict['enabled'] = listener_dict.pop('admin_state_up')
listener_id = listener_dict.pop('listener_id')
payload = {consts.LISTENER_ID: listener_id,
consts.LISTENER_UPDATES: listener_dict}
self.client.cast({}, 'update_listener', **payload)
# Pool
def pool_create(self, pool):

View File

@ -42,6 +42,7 @@ class BaseDataModel(object):
ret[attr].append(
item.to_dict(calling_classes=(
calling_classes + [type(self)]),
recurse=True,
render_unsets=render_unsets))
else:
ret[attr].append(None)
@ -125,18 +126,21 @@ class LoadBalancer(BaseDataModel):
class Listener(BaseDataModel):
def __init__(self, admin_state_up=Unset, connection_limit=Unset,
default_pool=Unset, default_pool_id=Unset,
default_tls_container=Unset, description=Unset,
default_tls_container_ref=Unset,
default_tls_container_data=Unset, description=Unset,
insert_headers=Unset, l7policies=Unset, listener_id=Unset,
loadbalancer_id=Unset, name=Unset, protocol=Unset,
protocol_port=Unset, sni_containers=Unset,
timeout_client_data=Unset, timeout_member_connect=Unset,
timeout_member_data=Unset, timeout_tcp_inspect=Unset):
protocol_port=Unset, sni_container_refs=Unset,
sni_container_data=Unset, timeout_client_data=Unset,
timeout_member_connect=Unset, timeout_member_data=Unset,
timeout_tcp_inspect=Unset):
self.admin_state_up = admin_state_up
self.connection_limit = connection_limit
self.default_pool = default_pool
self.default_pool_id = default_pool_id
self.default_tls_container = default_tls_container
self.default_tls_container_data = default_tls_container_data
self.default_tls_container_ref = default_tls_container_ref
self.description = description
self.insert_headers = insert_headers
self.l7policies = l7policies
@ -145,7 +149,8 @@ class Listener(BaseDataModel):
self.name = name
self.protocol = protocol
self.protocol_port = protocol_port
self.sni_containers = sni_containers
self.sni_container_data = sni_container_data
self.sni_container_refs = sni_container_refs
self.timeout_client_data = timeout_client_data
self.timeout_member_connect = timeout_member_connect
self.timeout_member_data = timeout_member_data

View File

@ -14,6 +14,8 @@
import copy
import six
from oslo_config import cfg
from oslo_log import log as logging
from stevedore import driver as stevedore_driver
@ -109,21 +111,26 @@ def lb_dict_to_provider_dict(lb_dict, vip=None,
def db_listeners_to_provider_listeners(db_listeners):
provider_listeners = []
for listener in db_listeners:
new_listener_dict = listener_dict_to_provider_dict(
listener.to_dict(recurse=True))
if ('default_pool' in new_listener_dict and
new_listener_dict['default_pool']):
provider_pool = db_pool_to_provider_pool(listener.default_pool)
new_listener_dict['default_pool_id'] = provider_pool.pool_id
new_listener_dict['default_pool'] = provider_pool
if 'l7policies' in new_listener_dict:
new_listener_dict['l7policies'] = (
db_l7policies_to_provider_l7policies(listener.l7policies))
provider_listeners.append(
driver_dm.Listener.from_dict(new_listener_dict))
provider_listener = db_listener_to_provider_listener(listener)
provider_listeners.append(provider_listener)
return provider_listeners
def db_listener_to_provider_listener(db_listener):
new_listener_dict = listener_dict_to_provider_dict(
db_listener.to_dict(recurse=True))
if ('default_pool' in new_listener_dict and
new_listener_dict['default_pool']):
provider_pool = db_pool_to_provider_pool(db_listener.default_pool)
new_listener_dict['default_pool_id'] = provider_pool.pool_id
new_listener_dict['default_pool'] = provider_pool
if 'l7policies' in new_listener_dict:
new_listener_dict['l7policies'] = (
db_l7policies_to_provider_l7policies(db_listener.l7policies))
provider_listener = driver_dm.Listener.from_dict(new_listener_dict)
return provider_listener
def listener_dict_to_provider_dict(listener_dict):
new_listener_dict = _base_to_provider_dict(listener_dict)
new_listener_dict['listener_id'] = new_listener_dict.pop('id')
@ -133,9 +140,14 @@ def listener_dict_to_provider_dict(listener_dict):
# Pull the certs out of the certificate manager to pass to the provider
if 'tls_certificate_id' in new_listener_dict:
del new_listener_dict['tls_certificate_id']
new_listener_dict['default_tls_container_ref'] = new_listener_dict.pop(
'tls_certificate_id')
if 'sni_containers' in new_listener_dict:
del new_listener_dict['sni_containers']
new_listener_dict['sni_container_refs'] = new_listener_dict.pop(
'sni_containers')
if 'sni_container_refs' in listener_dict:
listener_dict['sni_containers'] = listener_dict.pop(
'sni_container_refs')
listener_obj = data_models.Listener(**listener_dict)
if listener_obj.tls_certificate_id or listener_obj.sni_containers:
SNI_objs = []
@ -145,6 +157,9 @@ def listener_dict_to_provider_dict(listener_dict):
elif isinstance(sni, dict):
sni_obj = data_models.SNI(**sni)
SNI_objs.append(sni_obj)
elif isinstance(sni, six.string_types):
sni_obj = data_models.SNI(tls_container_id=sni)
SNI_objs.append(sni_obj)
else:
raise Exception(_('Invalid SNI container on listener'))
listener_obj.sni_containers = SNI_objs
@ -155,8 +170,14 @@ def listener_dict_to_provider_dict(listener_dict):
).driver
cert_dict = cert_parser.load_certificates_data(cert_manager,
listener_obj)
new_listener_dict['default_tls_container'] = cert_dict['tls_cert']
new_listener_dict['sni_containers'] = cert_dict['sni_certs']
if 'tls_cert' in cert_dict:
new_listener_dict['default_tls_container_data'] = (
cert_dict['tls_cert'].to_dict())
if 'sni_certs' in cert_dict:
sni_data_list = []
for sni in cert_dict['sni_certs']:
sni_data_list.append(sni.to_dict())
new_listener_dict['sni_container_data'] = sni_data_list
# Remove the DB back references
if 'load_balancer' in new_listener_dict:
@ -173,11 +194,12 @@ def listener_dict_to_provider_dict(listener_dict):
pool = new_listener_dict.pop('default_pool')
new_listener_dict['default_pool'] = pool_dict_to_provider_dict(pool)
provider_l7policies = []
l7policies = new_listener_dict.pop('l7policies')
for l7policy in l7policies:
provider_l7policy = l7policy_dict_to_provider_dict(l7policy)
provider_l7policies.append(provider_l7policy)
new_listener_dict['l7policies'] = provider_l7policies
if 'l7policies' in new_listener_dict:
l7policies = new_listener_dict.pop('l7policies')
for l7policy in l7policies:
provider_l7policy = l7policy_dict_to_provider_dict(l7policy)
provider_l7policies.append(provider_l7policy)
new_listener_dict['l7policies'] = provider_l7policies
return new_listener_dict

View File

@ -125,6 +125,13 @@ class BaseController(rest.RestController):
show_deleted=show_deleted)
return lb.project_id
def _get_lb_project_id_provider(self, session, id, show_deleted=True):
"""Get the project_id of the load balancer from the database."""
lb = self._get_db_obj(session, self.repositories.load_balancer,
data_models.LoadBalancer, id,
show_deleted=show_deleted)
return lb.project_id, lb.provider
def _get_l7policy_project_id(self, session, id, show_deleted=True):
"""Get the project_id of the load balancer from the database."""
l7policy = self._get_db_obj(session, self.repositories.l7policy,

View File

@ -23,6 +23,9 @@ from stevedore import driver as stevedore_driver
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
from octavia.api.drivers import data_models as driver_dm
from octavia.api.drivers import driver_factory
from octavia.api.drivers import utils as driver_utils
from octavia.api.v2.controllers import base
from octavia.api.v2.controllers import l7policy
from octavia.api.v2.types import listener as listener_types
@ -178,25 +181,6 @@ class ListenersController(base.BaseController):
raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
option='protocol')
def _send_listener_to_handler(self, session, db_listener):
try:
LOG.info("Sending Creation of Listener %s to handler",
db_listener.id)
self.handler.create(db_listener)
except Exception:
with excutils.save_and_reraise_exception(
reraise=False), db_api.get_lock_session() as lock_session:
self._reset_lb_status(
lock_session, lb_id=db_listener.load_balancer_id)
# Listener now goes to ERROR
self.repositories.listener.update(
lock_session, db_listener.id,
provisioning_status=constants.ERROR)
db_listener = self._get_db_listener(session, db_listener.id)
result = self._convert_db_to_type(db_listener,
listener_types.ListenerResponse)
return listener_types.ListenerRootResponse(listener=result)
@wsme_pecan.wsexpose(listener_types.ListenerRootResponse,
body=listener_types.ListenerRootPOST, status_code=201)
def post(self, listener_):
@ -205,7 +189,7 @@ class ListenersController(base.BaseController):
context = pecan.request.context.get('octavia_context')
load_balancer_id = listener.loadbalancer_id
listener.project_id = self._get_lb_project_id(
listener.project_id, provider = self._get_lb_project_id_provider(
context.session, load_balancer_id)
self._auth_validate_action(context, listener.project_id,
@ -216,6 +200,9 @@ class ListenersController(base.BaseController):
raise exceptions.DisabledOption(
value=constants.PROTOCOL_TERMINATED_HTTPS, option='protocol')
# Load the driver early as it also provides validation
driver = driver_factory.get_driver(provider)
lock_session = db_api.get_session(autocommit=False)
try:
if self.repositories.check_quota_met(
@ -237,12 +224,30 @@ class ListenersController(base.BaseController):
db_listener = self._validate_create_listener(
lock_session, listener_dict)
# Prepare the data for the driver data model
provider_listener = (
driver_utils.db_listener_to_provider_listener(db_listener))
# re-inject the sni container references lost due to SNI
# being a seperate table in the DB
provider_listener.sni_container_refs = listener.sni_container_refs
# Dispatch to the driver
LOG.info("Sending create Listener %s to provider %s",
db_listener.id, driver.name)
driver_utils.call_provider(
driver.name, driver.listener_create, provider_listener)
lock_session.commit()
except Exception:
with excutils.save_and_reraise_exception():
lock_session.rollback()
return self._send_listener_to_handler(context.session, db_listener)
db_listener = self._get_db_listener(context.session, db_listener.id)
result = self._convert_db_to_type(db_listener,
listener_types.ListenerResponse)
return listener_types.ListenerRootResponse(listener=result)
def _graph_create(self, lock_session, listener_dict,
l7policies=None, pool_name_ids=None):
@ -285,8 +290,10 @@ class ListenersController(base.BaseController):
show_deleted=False)
load_balancer_id = db_listener.load_balancer_id
self._auth_validate_action(context, db_listener.project_id,
constants.RBAC_PUT)
project_id, provider = self._get_lb_project_id_provider(
context.session, load_balancer_id)
self._auth_validate_action(context, project_id, constants.RBAC_PUT)
# TODO(rm_work): Do we need something like this? What do we do on an
# empty body for a PUT?
@ -297,8 +304,6 @@ class ListenersController(base.BaseController):
if listener.default_pool_id:
self._validate_pool(context.session, load_balancer_id,
listener.default_pool_id)
self._test_lb_and_listener_statuses(context.session, load_balancer_id,
id=id)
sni_containers = listener.sni_container_refs or []
tls_refs = [sni for sni in sni_containers]
@ -306,18 +311,33 @@ class ListenersController(base.BaseController):
tls_refs.append(listener.default_tls_container_ref)
self._validate_tls_refs(tls_refs)
try:
LOG.info("Sending Update of Listener %s to handler", id)
self.handler.update(db_listener, listener)
except Exception:
with excutils.save_and_reraise_exception(
reraise=False), db_api.get_lock_session() as lock_session:
self._reset_lb_status(
lock_session, lb_id=db_listener.load_balancer_id)
# Listener now goes to ERROR
self.repositories.listener.update(
lock_session, db_listener.id,
provisioning_status=constants.ERROR)
# Load the driver early as it also provides validation
driver = driver_factory.get_driver(provider)
with db_api.get_lock_session() as lock_session:
self._test_lb_and_listener_statuses(lock_session,
load_balancer_id, id=id)
# Prepare the data for the driver data model
listener_dict = listener.to_dict(render_unsets=False)
listener_dict['id'] = id
provider_listener_dict = (
driver_utils.listener_dict_to_provider_dict(listener_dict))
# Dispatch to the driver
LOG.info("Sending update Listener %s to provider %s", id,
driver.name)
driver_utils.call_provider(
driver.name, driver.listener_update,
driver_dm.Listener.from_dict(provider_listener_dict))
# Update the database to reflect what the driver just accepted
self.repositories.listener.update(
lock_session, id, **listener.to_dict(render_unsets=False))
# Force SQL alchemy to query the DB, otherwise we get inconsistent
# results
context.session.expire_all()
db_listener = self._get_db_listener(context.session, id)
result = self._convert_db_to_type(db_listener,
listener_types.ListenerResponse)
@ -331,26 +351,23 @@ class ListenersController(base.BaseController):
show_deleted=False)
load_balancer_id = db_listener.load_balancer_id
self._auth_validate_action(context, db_listener.project_id,
constants.RBAC_DELETE)
project_id, provider = self._get_lb_project_id_provider(
context.session, load_balancer_id)
self._test_lb_and_listener_statuses(
context.session, load_balancer_id,
id=id, listener_status=constants.PENDING_DELETE)
self._auth_validate_action(context, project_id, constants.RBAC_DELETE)
try:
LOG.info("Sending Deletion of Listener %s to handler",
db_listener.id)
self.handler.delete(db_listener)
except Exception:
with excutils.save_and_reraise_exception(
reraise=False), db_api.get_lock_session() as lock_session:
self._reset_lb_status(
lock_session, lb_id=db_listener.load_balancer_id)
# Listener now goes to ERROR
self.repositories.listener.update(
lock_session, db_listener.id,
provisioning_status=constants.ERROR)
# Load the driver early as it also provides validation
driver = driver_factory.get_driver(provider)
with db_api.get_lock_session() as lock_session:
self._test_lb_and_listener_statuses(
lock_session, load_balancer_id,
id=id, listener_status=constants.PENDING_DELETE)
LOG.info("Sending delete Listener %s to provider %s", id,
driver.name)
driver_utils.call_provider(driver.name, driver.listener_delete, id)
@pecan.expose()
def _lookup(self, id, *remainder):

View File

@ -226,6 +226,7 @@ HEALTH_MONITOR_ID = 'health_monitor_id'
L7POLICY_ID = 'l7policy_id'
L7RULE_ID = 'l7rule_id'
LOAD_BALANCER_UPDATES = 'load_balancer_updates'
LISTENER_UPDATES = 'listener_updates'
CERT_ROTATE_AMPHORA_FLOW = 'octavia-cert-rotate-amphora-flow'
CREATE_AMPHORA_FLOW = 'octavia-create-amphora-flow'

View File

@ -23,6 +23,7 @@ from oslo_utils import uuidutils
from octavia.common import constants
import octavia.common.context
from octavia.common import data_models
from octavia.common import exceptions
from octavia.tests.functional.api.v2 import base
@ -433,7 +434,14 @@ class TestListener(base.BaseAPITest):
listener_path = self.listener_path
self.get(listener_path.format(listener_id='SEAN-CONNERY'), status=404)
def test_create(self, response_status=201, **optionals):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_create(self, mock_cert_data, response_status=201, **optionals):
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
sni1 = uuidutils.generate_uuid()
sni2 = uuidutils.generate_uuid()
lb_listener = {'name': 'listener1', 'default_pool_id': None,
@ -630,18 +638,27 @@ class TestListener(base.BaseAPITest):
body = self._build_body(lb_listener)
self.post(self.LISTENERS_PATH, body, status=403)
def test_create_with_bad_handler(self):
self.handler_mock().listener.create.side_effect = Exception()
api_listener = self.create_listener(
constants.PROTOCOL_HTTP, 80,
self.lb_id).get(self.root_tag)
self.assert_correct_status(
lb_id=self.lb_id,
listener_id=api_listener.get('id'),
listener_prov_status=constants.ERROR,
listener_op_status=constants.OFFLINE)
@mock.patch('octavia.api.drivers.utils.call_provider')
def test_create_with_bad_provider(self, mock_provider):
mock_provider.side_effect = exceptions.ProviderDriverError(
prov='bad_driver', user_msg='broken')
lb_listener = {'name': 'listener1',
'protocol': constants.PROTOCOL_HTTP,
'protocol_port': 80,
'loadbalancer_id': self.lb_id}
body = self._build_body(lb_listener)
response = self.post(self.LISTENERS_PATH, body, status=500)
self.assertIn('Provider \'bad_driver\' reports error: broken',
response.json.get('faultstring'))
def test_create_authorized(self, **optionals):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_create_authorized(self, mock_cert_data, **optionals):
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
sni1 = uuidutils.generate_uuid()
sni2 = uuidutils.generate_uuid()
lb_listener = {'name': 'listener1', 'default_pool_id': None,
@ -731,21 +748,23 @@ class TestListener(base.BaseAPITest):
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json)
def test_update_with_bad_handler(self):
@mock.patch('octavia.api.drivers.utils.call_provider')
def test_update_with_bad_provider(self, mock_provider):
api_listener = self.create_listener(
constants.PROTOCOL_HTTP, 80,
self.lb_id).get(self.root_tag)
self.set_lb_status(lb_id=self.lb_id)
new_listener = {'name': 'new_name'}
self.handler_mock().listener.update.side_effect = Exception()
self.put(self.LISTENER_PATH.format(listener_id=api_listener.get('id')),
self._build_body(new_listener))
self.assert_correct_status(
lb_id=self.lb_id,
listener_id=api_listener.get('id'),
listener_prov_status=constants.ERROR)
mock_provider.side_effect = exceptions.ProviderDriverError(
prov='bad_driver', user_msg='broken')
response = self.put(
self.LISTENER_PATH.format(listener_id=api_listener.get('id')),
self._build_body(new_listener), status=500)
self.assertIn('Provider \'bad_driver\' reports error: broken',
response.json.get('faultstring'))
def test_delete_with_bad_handler(self):
@mock.patch('octavia.api.drivers.utils.call_provider')
def test_delete_with_bad_provider(self, mock_provider):
api_listener = self.create_listener(
constants.PROTOCOL_HTTP, 80,
self.lb_id).get(self.root_tag)
@ -755,19 +774,19 @@ class TestListener(base.BaseAPITest):
api_listener['operating_status'] = constants.ONLINE
response = self.get(self.LISTENER_PATH.format(
listener_id=api_listener.get('id'))).json.get(self.root_tag)
self.assertIsNone(api_listener.pop('updated_at'))
self.assertIsNotNone(response.pop('updated_at'))
self.assertEqual(api_listener, response)
self.handler_mock().listener.delete.side_effect = Exception()
mock_provider.side_effect = exceptions.ProviderDriverError(
prov='bad_driver', user_msg='broken')
self.delete(self.LISTENER_PATH.format(
listener_id=api_listener.get('id')))
self.assert_correct_status(
lb_id=self.lb_id,
listener_id=api_listener.get('id'),
listener_prov_status=constants.ERROR)
listener_id=api_listener.get('id')), status=500)
def test_update(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_update(self, mock_cert_data):
cert1 = data_models.TLSContainer(certificate='cert 1')
mock_cert_data.return_value = {'tls_cert': cert1}
tls_uuid = uuidutils.generate_uuid()
listener = self.create_listener(
constants.PROTOCOL_TCP, 80, self.lb_id,
@ -821,7 +840,11 @@ class TestListener(base.BaseAPITest):
self.assert_final_listener_statuses(self.lb_id,
listener['listener']['id'])
def test_update_authorized(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_update_authorized(self, mock_cert_data):
cert1 = data_models.TLSContainer(certificate='cert 1')
mock_cert_data.return_value = {'tls_cert': cert1}
tls_uuid = uuidutils.generate_uuid()
listener = self.create_listener(
constants.PROTOCOL_TCP, 80, self.lb_id,
@ -875,7 +898,11 @@ class TestListener(base.BaseAPITest):
self.assert_final_listener_statuses(self.lb_id,
api_listener['id'])
def test_update_not_authorized(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_update_not_authorized(self, mock_cert_data):
cert1 = data_models.TLSContainer(certificate='cert 1')
mock_cert_data.return_value = {'tls_cert': cert1}
tls_uuid = uuidutils.generate_uuid()
listener = self.create_listener(
constants.PROTOCOL_TCP, 80, self.lb_id,
@ -1062,7 +1089,12 @@ class TestListener(base.BaseAPITest):
listener_id=listener['listener'].get('id'))
self.put(listener_path, {}, status=400)
def test_update_bad_tls_ref(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_update_bad_tls_ref(self, mock_cert_data):
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'sni_certs': [cert2, cert3]}
sni1 = uuidutils.generate_uuid()
sni2 = uuidutils.generate_uuid()
tls_ref = uuidutils.generate_uuid()
@ -1202,7 +1234,14 @@ class TestListener(base.BaseAPITest):
listener_id=api_listener['id'])
self.delete(listener_path, status=404)
def test_create_with_tls_termination_data(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_create_with_tls_termination_data(self, mock_cert_data):
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
cert_id = uuidutils.generate_uuid()
listener = self.create_listener(constants.PROTOCOL_TERMINATED_HTTPS,
80, self.lb_id,
@ -1212,7 +1251,11 @@ class TestListener(base.BaseAPITest):
get_listener = self.get(listener_path).json['listener']
self.assertEqual(cert_id, get_listener['default_tls_container_ref'])
def test_update_with_tls_termination_data(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_update_with_tls_termination_data(self, mock_cert_data):
cert1 = data_models.TLSContainer(certificate='cert 1')
mock_cert_data.return_value = {'tls_cert': cert1}
cert_id = uuidutils.generate_uuid()
listener = self.create_listener(constants.PROTOCOL_TERMINATED_HTTPS,
80, self.lb_id)
@ -1224,7 +1267,8 @@ class TestListener(base.BaseAPITest):
self.put(listener_path,
self._build_body({'default_tls_container_ref': cert_id}))
get_listener = self.get(listener_path).json['listener']
self.assertIsNone(get_listener.get('default_tls_container_ref'))
self.assertEqual(cert_id,
get_listener.get('default_tls_container_ref'))
def test_create_with_tls_termination_disabled(self):
self.conf.config(group='api_settings',
@ -1239,7 +1283,14 @@ class TestListener(base.BaseAPITest):
.format(constants.PROTOCOL_TERMINATED_HTTPS),
listener.get('faultstring'))
def test_create_with_sni_data(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_create_with_sni_data(self, mock_cert_data):
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
sni_id1 = uuidutils.generate_uuid()
sni_id2 = uuidutils.generate_uuid()
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
@ -1251,7 +1302,12 @@ class TestListener(base.BaseAPITest):
self.assertItemsEqual([sni_id1, sni_id2],
get_listener['sni_container_refs'])
def test_update_with_sni_data(self):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_update_with_sni_data(self, mock_cert_data):
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'sni_certs': [cert2, cert3]}
sni_id1 = uuidutils.generate_uuid()
sni_id2 = uuidutils.generate_uuid()
listener = self.create_listener(constants.PROTOCOL_HTTP, 80,

View File

@ -2350,8 +2350,11 @@ class TestLoadBalancerGraph(base.BaseAPITest):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_with_one_listener_sni_containers(self, mock_cert_data):
mock_cert_data.return_value = {'tls_cert': 'cert 1',
'sni_certs': ['cert 2', 'cert 3']}
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
create_sni_containers, expected_sni_containers = (
self._get_sni_container_bodies())
create_listener, expected_listener = self._get_listener_bodies(
@ -2512,9 +2515,11 @@ class TestLoadBalancerGraph(base.BaseAPITest):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_with_one_of_everything(self, mock_cert_data):
mock_cert_data.return_value = {'tls_cert': 'cert 1',
'sni_certs': ['cert 2', 'cert 3']}
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
body, expected_lb = self._test_with_one_of_everything_helper()
response = self.post(self.LBS_PATH, body)
api_lb = response.json.get(self.root_tag)
@ -2599,9 +2604,11 @@ class TestLoadBalancerGraph(base.BaseAPITest):
# TODO(johnsom) Fix this when there is a noop certificate manager
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_create_over_quota_sanity_check(self, mock_cert_data):
mock_cert_data.return_value = {'tls_cert': 'cert 1',
'sni_certs': ['cert 2', 'cert 3']}
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_cert_data.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
# This one should create, as we don't check quotas on L7Policies
body, _ = self._test_with_one_of_everything_helper()
self.start_quota_mock(data_models.L7Policy)

View File

@ -33,12 +33,16 @@ class TestProviderDataModels(base.TestCase):
self.vip_subnet_id = uuidutils.generate_uuid()
self.listener_id = uuidutils.generate_uuid()
self.vip_qos_policy_id = uuidutils.generate_uuid()
self.default_tls_container_ref = uuidutils.generate_uuid()
self.sni_container_ref_1 = uuidutils.generate_uuid()
self.sni_container_ref_2 = uuidutils.generate_uuid()
self.ref_listener = data_models.Listener(
admin_state_up=True,
connection_limit=5000,
default_pool_id=None,
default_tls_container='a_pkcs12_bundle',
default_tls_container_data='default_cert_data',
default_tls_container_ref=self.default_tls_container_ref,
description='The listener',
insert_headers={'X-Forwarded-For': 'true'},
l7policies=[],
@ -47,7 +51,9 @@ class TestProviderDataModels(base.TestCase):
name='super_listener',
protocol='avian',
protocol_port=42,
sni_containers='another_pkcs12_bundle')
sni_container_data=['sni_cert_data_1', 'sni_cert_data_2'],
sni_container_refs=[self.sni_container_ref_1,
self.sni_container_ref_2])
self.ref_lb = data_models.LoadBalancer(
admin_state_up=False,
@ -75,18 +81,23 @@ class TestProviderDataModels(base.TestCase):
'name': 'favorite_lb',
'vip_qos_policy_id': self.vip_qos_policy_id}
self.ref_listener = {'admin_state_up': True,
'connection_limit': 5000,
'default_pool_id': None,
'default_tls_container': 'a_pkcs12_bundle',
'description': 'The listener',
'insert_headers': {'X-Forwarded-For': 'true'},
'listener_id': self.listener_id,
'loadbalancer_id': self.loadbalancer_id,
'name': 'super_listener',
'protocol': 'avian',
'protocol_port': 42,
'sni_containers': 'another_pkcs12_bundle'}
self.ref_listener = {
'admin_state_up': True,
'connection_limit': 5000,
'default_pool_id': None,
'default_tls_container_data': 'default_cert_data',
'default_tls_container_ref': self.default_tls_container_ref,
'description': 'The listener',
'insert_headers': {'X-Forwarded-For': 'true'},
'listener_id': self.listener_id,
'l7policies': [],
'loadbalancer_id': self.loadbalancer_id,
'name': 'super_listener',
'protocol': 'avian',
'protocol_port': 42,
'sni_container_data': ['sni_cert_data_1', 'sni_cert_data_2'],
'sni_container_refs': [self.sni_container_ref_1,
self.sni_container_ref_2]}
self.ref_lb_dict_with_listener = {
'admin_state_up': False,

View File

@ -38,6 +38,9 @@ class TestNoopProviderDriver(base.TestCase):
self.l7policy_id = uuidutils.generate_uuid()
self.l7rule_id = uuidutils.generate_uuid()
self.project_id = uuidutils.generate_uuid()
self.default_tls_container_ref = uuidutils.generate_uuid()
self.sni_container_ref_1 = uuidutils.generate_uuid()
self.sni_container_ref_2 = uuidutils.generate_uuid()
self.ref_vip = data_models.VIP(
vip_address=self.vip_address,
@ -110,7 +113,8 @@ class TestNoopProviderDriver(base.TestCase):
connection_limit=5,
default_pool=self.ref_pool,
default_pool_id=self.pool_id,
default_tls_container='a_pkcs12_bundle',
default_tls_container_data='default_cert_data',
default_tls_container_ref=self.default_tls_container_ref,
description='The listener',
insert_headers={'X-Forwarded-For': 'true'},
l7policies=[self.ref_l7policy],
@ -119,7 +123,9 @@ class TestNoopProviderDriver(base.TestCase):
name='super_listener',
protocol='avian',
protocol_port=42,
sni_containers='another_pkcs12_bundle')
sni_container_data=['sni_cert_data_1', 'sni_cert_data_2'],
sni_container_refs=[self.sni_container_ref_1,
self.sni_container_ref_2])
self.ref_lb = data_models.LoadBalancer(
admin_state_up=False,

View File

@ -53,8 +53,9 @@ class TestUtils(base.TestCase):
self.network_id = uuidutils.generate_uuid()
self.subnet_id = uuidutils.generate_uuid()
self.qos_policy_id = uuidutils.generate_uuid()
self.sni_containers = [{'tls_container_id': '2'},
{'tls_container_id': '3'}]
self.default_tls_container_ref = uuidutils.generate_uuid()
self.sni_container_ref_1 = uuidutils.generate_uuid()
self.sni_container_ref_2 = uuidutils.generate_uuid()
_common_test_dict = {'provisioning_status': constants.ACTIVE,
'operating_status': constants.ONLINE,
@ -356,27 +357,29 @@ class TestUtils(base.TestCase):
self.provider_l7policy2]
# Setup Listeners
self.test_listener1_dict = {'id': listener1_id,
'name': 'listener_1',
'description': 'Listener 1',
'default_pool_id': pool1_id,
'load_balancer_id': self.lb_id,
'protocol': 'avian',
'protocol_port': 90,
'connection_limit': 10000,
'tls_certificate_id': '1',
'stats': None,
'default_pool': self.test_pool1_dict,
'load_balancer': None,
'sni_containers': self.sni_containers,
'peer_port': 55,
'l7policies': self.test_l7policies,
'insert_headers': {},
'pools': None,
'timeout_client_data': 1000,
'timeout_member_connect': 2000,
'timeout_member_data': 3000,
'timeout_tcp_inspect': 4000}
self.test_listener1_dict = {
'id': listener1_id,
'name': 'listener_1',
'description': 'Listener 1',
'default_pool_id': pool1_id,
'load_balancer_id': self.lb_id,
'protocol': 'avian',
'protocol_port': 90,
'connection_limit': 10000,
'tls_certificate_id': self.default_tls_container_ref,
'stats': None,
'default_pool': self.test_pool1_dict,
'load_balancer': None,
'sni_containers': [self.sni_container_ref_1,
self.sni_container_ref_2],
'peer_port': 55,
'l7policies': self.test_l7policies,
'insert_headers': {},
'pools': None,
'timeout_client_data': 1000,
'timeout_member_connect': 2000,
'timeout_member_data': 3000,
'timeout_tcp_inspect': 4000}
self.test_listener1_dict.update(_common_test_dict)
@ -403,12 +406,17 @@ class TestUtils(base.TestCase):
self.test_db_listeners = [self.db_listener1, self.db_listener2]
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
self.provider_listener1_dict = {
'admin_state_up': True,
'connection_limit': 10000,
'default_pool': self.provider_pool1_dict,
'default_pool_id': pool1_id,
'default_tls_container': 'cert 1',
'default_tls_container_data': cert1.to_dict(),
'default_tls_container_ref': self.default_tls_container_ref,
'description': 'Listener 1',
'insert_headers': {},
'l7policies': self.provider_l7policies_dict,
@ -417,7 +425,9 @@ class TestUtils(base.TestCase):
'name': 'listener_1',
'protocol': 'avian',
'protocol_port': 90,
'sni_containers': ['cert 2', 'cert 3'],
'sni_container_data': [cert2.to_dict(), cert3.to_dict()],
'sni_container_refs': [self.sni_container_ref_1,
self.sni_container_ref_2],
'timeout_client_data': 1000,
'timeout_member_connect': 2000,
'timeout_member_data': 3000,
@ -498,9 +508,11 @@ class TestUtils(base.TestCase):
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_lb_dict_to_provider_dict(self, mock_load_cert):
mock_load_cert.return_value = {'tls_cert': 'cert 1',
'sni_certs': ['cert 2', 'cert 3']}
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_load_cert.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
test_lb_dict = {'name': 'lb1', 'project_id': self.project_id,
'vip_subnet_id': self.subnet_id,
'vip_port_id': self.port_id,
@ -542,16 +554,22 @@ class TestUtils(base.TestCase):
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_db_listeners_to_provider_listeners(self, mock_load_cert):
mock_load_cert.return_value = {'tls_cert': 'cert 1',
'sni_certs': ['cert 2', 'cert 3']}
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_load_cert.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
provider_listeners = utils.db_listeners_to_provider_listeners(
self.test_db_listeners)
self.assertEqual(self.provider_listeners, provider_listeners)
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
def test_listener_dict_to_provider_dict(self, mock_load_cert):
mock_load_cert.return_value = {'tls_cert': 'cert 1',
'sni_certs': ['cert 2', 'cert 3']}
cert1 = data_models.TLSContainer(certificate='cert 1')
cert2 = data_models.TLSContainer(certificate='cert 2')
cert3 = data_models.TLSContainer(certificate='cert 3')
mock_load_cert.return_value = {'tls_cert': cert1,
'sni_certs': [cert2, cert3]}
provider_listener = utils.listener_dict_to_provider_dict(
self.test_listener1_dict)
self.assertEqual(self.provider_listener1_dict, provider_listener)

View File

@ -219,6 +219,8 @@ Load balancer
+-----------------+--------+-----------------------------------------------+
| name | string | Human-readable name of the resource. |
+-----------------+--------+-----------------------------------------------+
|vip_qos_policy_id| string | The ID of the qos policy for the VIP. |
+-----------------+--------+-----------------------------------------------+
The load balancer will be in the ``PENDING_UPDATE`` provisioning_status when
it is passed to the driver. The driver will update the provisioning_status
@ -332,61 +334,85 @@ Listener
As of the writing of this specification the create listener object may
contain the following:
+-----------------------+--------+------------------------------------------+
| Name | Type | Description |
+=======================+========+==========================================+
| admin_state_up | bool | Admin state: True if up, False if down. |
+-----------------------+--------+------------------------------------------+
| connection_limit | int | The max number of connections permitted |
| | | for this listener. Default is -1, which |
| | | is infinite connections. |
+-----------------------+--------+------------------------------------------+
| default_pool | object | A `Pool object`_. |
+-----------------------+--------+------------------------------------------+
| default_pool_id | string | The ID of the pool used by the listener |
| | | if no L7 policies match. |
+-----------------------+--------+------------------------------------------+
| default_tls_container | object | A pkcs12 format certicate and key. |
+-----------------------+--------+------------------------------------------+
| description | string | A human-readable description for the |
| | | listener. |
+-----------------------+--------+------------------------------------------+
| insert_headers | dict | A dictionary of optional headers to |
| | | insert into the request before it is sent|
| | | to the backend member. See |
| | | `Supported HTTP Header Insertions`_. |
| | | Keys and values are specified as strings.|
+-----------------------+--------+------------------------------------------+
| l7policies | list | A list of `L7policy objects`_. |
+-----------------------+--------+------------------------------------------+
| listener_id | string | ID of listener to create. |
+-----------------------+--------+------------------------------------------+
| loadbalancer_id | string | ID of load balancer. |
+-----------------------+--------+------------------------------------------+
| name | string | Human-readable name of the listener. |
+-----------------------+--------+------------------------------------------+
| protocol | string | Protocol type: One of HTTP, HTTPS, TCP, |
| | | or TERMINATED_HTTPS. |
+-----------------------+--------+------------------------------------------+
| protocol_port | int | Protocol port number. |
+-----------------------+--------+------------------------------------------+
| sni_containers | object | A pkcs12 format set of certificates. |
+-----------------------+--------+------------------------------------------+
| timeout_client_data | int | Frontend client inactivity timeout in |
| | | milliseconds. |
+-----------------------+--------+------------------------------------------+
| timeout_member_connect| int | Backend member connection timeout in |
| | | milliseconds. |
+-----------------------+--------+------------------------------------------+
| timeout_member_data | int | Backend member inactivity timeout in |
| | | milliseconds. |
+-----------------------+--------+------------------------------------------+
| timeout_tcp_inspect | int | Time, in milliseconds, to wait for |
| | | additional TCP packets for content |
| | | inspection. |
+-----------------------+--------+------------------------------------------+
+----------------------------+--------+-------------------------------------+
| Name | Type | Description |
+============================+========+=====================================+
| admin_state_up | bool | Admin state: True if up, False if |
| | | down. |
+----------------------------+--------+-------------------------------------+
| connection_limit | int | The max number of connections |
| | | permitted for this listener. Default|
| | | is -1, which is infinite |
| | | connections. |
+----------------------------+--------+-------------------------------------+
| default_pool | object | A `Pool object`_. |
+----------------------------+--------+-------------------------------------+
| default_pool_id | string | The ID of the pool used by the |
| | | listener if no L7 policies match. |
+----------------------------+--------+-------------------------------------+
| default_tls_container_data | dict | A `TLS container`_ dict. |
+----------------------------+--------+-------------------------------------+
| default_tls_container_refs | string | The reference to the secrets |
| | | container. |
+----------------------------+--------+-------------------------------------+
| description | string | A human-readable description for the|
| | | listener. |
+----------------------------+--------+-------------------------------------+
| insert_headers | dict | A dictionary of optional headers to |
| | | insert into the request before it is|
| | | sent to the backend member. See |
| | | `Supported HTTP Header Insertions`_.|
| | | Keys and values are specified as |
| | | strings. |
+----------------------------+--------+-------------------------------------+
| l7policies | list | A list of `L7policy objects`_. |
+----------------------------+--------+-------------------------------------+
| listener_id | string | ID of listener to create. |
+----------------------------+--------+-------------------------------------+
| loadbalancer_id | string | ID of load balancer. |
+----------------------------+--------+-------------------------------------+
| name | string | Human-readable name of the listener.|
+----------------------------+--------+-------------------------------------+
| protocol | string | Protocol type: One of HTTP, HTTPS, |
| | | TCP, or TERMINATED_HTTPS. |
+----------------------------+--------+-------------------------------------+
| protocol_port | int | Protocol port number. |
+----------------------------+--------+-------------------------------------+
| sni_container_data | list | A list of `TLS container`_ dict. |
+----------------------------+--------+-------------------------------------+
| sni_container_refs | list | A list of references to the SNI |
| | | secrets containers. |
+----------------------------+--------+-------------------------------------+
| timeout_client_data | int | Frontend client inactivity timeout |
| | | in milliseconds. |
+----------------------------+--------+-------------------------------------+
| timeout_member_connect | int | Backend member connection timeout in|
| | | milliseconds. |
+----------------------------+--------+-------------------------------------+
| timeout_member_data | int | Backend member inactivity timeout in|
| | | milliseconds. |
+----------------------------+--------+-------------------------------------+
| timeout_tcp_inspect | int | Time, in milliseconds, to wait for |
| | | additional TCP packets for content |
| | | inspection. |
+----------------------------+--------+-------------------------------------+
.. _TLS container:
As of the writing of this specification the TLS container dictionary
contains the following:
+---------------+--------+------------------------------------------------+
| Key | Type | Description |
+===============+========+================================================+
| certificate | string | The PEM encoded certificate. |
+---------------+--------+------------------------------------------------+
| intermediates | List | A list of intermediate PEM certificates. |
+---------------+--------+------------------------------------------------+
| primary_cn | string | The primary common name of the certificate. |
+---------------+--------+------------------------------------------------+
| private_key | string | The PEM encoded private key. |
+---------------+--------+------------------------------------------------+
.. _Supported HTTP Header Insertions:
@ -434,35 +460,57 @@ Listener
As of the writing of this specification the update listener object may
contain the following:
+-----------------------+--------+------------------------------------------+
| Name | Type | Description |
+=======================+========+==========================================+
| admin_state_up | bool | Admin state: True if up, False if down. |
+-----------------------+--------+----------+-------------------------------+
| connection_limit | int | The max number of connections permitted |
| | | for this listener. Default is -1, which |
| | | is infinite connections. |
+-----------------------+--------+------------------------------------------+
| default_pool_id | string | The ID of the pool used by the listener |
| | | if no L7 policies match. |
+-----------------------+--------+------------------------------------------+
| default_tls_container | object | A pkcs12 format certicate and key. |
+-----------------------+--------+------------------------------------------+
| description | string | A human-readable description for the |
| | | listener. |
+-----------------------+--------+------------------------------------------+
| insert_headers | dict | A dictionary of optional headers to |
| | | insert into the request before it is sent|
| | | to the backend member. See |
| | | `Supported HTTP Header Insertions`_. |
| | | Keys and values are specified as strings.|
+-----------------------+--------+------------------------------------------+
| listener_id | string | ID of listener to update. |
+-----------------------+--------+------------------------------------------+
| name | string | Human-readable name of the listener. |
+-----------------------+--------+------------------------------------------+
| sni_containers | object | A pkcs12 format set of certificates. |
+-----------------------+--------+------------------------------------------+
+----------------------------+--------+-------------------------------------+
| Name | Type | Description |
+============================+========+=====================================+
| admin_state_up | bool | Admin state: True if up, False if |
| | | down. |
+----------------------------+--------+-------------------------------------+
| connection_limit | int | The max number of connections |
| | | permitted for this listener. Default|
| | | is -1, which is infinite |
| | | connections. |
+----------------------------+--------+-------------------------------------+
| default_pool_id | string | The ID of the pool used by the |
| | | listener if no L7 policies match. |
+----------------------------+--------+-------------------------------------+
| default_tls_container_data | dict | A `TLS container`_ dict. |
+----------------------------+--------+-------------------------------------+
| default_tls_container_refs | string | The reference to the secrets |
| | | container. |
+----------------------------+--------+-------------------------------------+
| description | string | A human-readable description for |
| | | the listener. |
+----------------------------+--------+-------------------------------------+
| insert_headers | dict | A dictionary of optional headers to |
| | | insert into the request before it is|
| | | sent to the backend member. See |
| | | `Supported HTTP Header Insertions`_.|
| | | Keys and values are specified as |
| | | strings. |
+----------------------------+--------+-------------------------------------+
| listener_id | string | ID of listener to update. |
+----------------------------+--------+-------------------------------------+
| name | string | Human-readable name of the listener.|
+----------------------------+--------+-------------------------------------+
| sni_container_data | list | A list of `TLS container`_ dict. |
+----------------------------+--------+-------------------------------------+
| sni_container_refs | list | A list of references to the SNI |
| | | secrets containers. |
+----------------------------+--------+-------------------------------------+
| timeout_client_data | int | Frontend client inactivity timeout |
| | | in milliseconds. |
+----------------------------+--------+-------------------------------------+
| timeout_member_connect | int | Backend member connection timeout in|
| | | milliseconds. |
+----------------------------+--------+-------------------------------------+
| timeout_member_data | int | Backend member inactivity timeout in|
| | | milliseconds. |
+----------------------------+--------+-------------------------------------+
| timeout_tcp_inspect | int | Time, in milliseconds, to wait for |
| | | additional TCP packets for content |
| | | inspection. |
+----------------------------+--------+-------------------------------------+
The listener will be in the ``PENDING_UPDATE`` provisioning_status when
it is passed to the driver. The driver will update the provisioning_status
@ -761,6 +809,10 @@ Member
+=======================+========+==========================================+
| admin_state_up | bool | Admin state: True if up, False if down. |
+-----------------------+--------+------------------------------------------+
| backup | bool | Is the member a backup? Backup members |
| | | only receive traffic when all non-backup |
| | | members are down. |
+-----------------------+--------+------------------------------------------+
| member_id | string | ID of member to update. |
+-----------------------+--------+------------------------------------------+
| monitor_address | string | An alternate IP address used for health |