#    Copyright 2014 Rackspace
#    Copyright 2016 Blue Box, an IBM Company
#
#    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.

import copy
import random
from unittest import mock

from octavia_lib.common import constants as lib_consts
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
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.db import api as db_api
from octavia.tests.common import constants as c_const
from octavia.tests.common import sample_certs
from octavia.tests.functional.api.v2 import base


class TestListener(base.BaseAPITest):

    root_tag = 'listener'
    root_tag_list = 'listeners'
    root_tag_links = 'listeners_links'

    def setUp(self):
        super().setUp()
        self.lb = self.create_load_balancer(uuidutils.generate_uuid())
        self.lb_id = self.lb.get('loadbalancer').get('id')
        self.project_id = self.lb.get('loadbalancer').get('project_id')
        self.set_lb_status(self.lb_id)
        self.listener_path = self.LISTENERS_PATH + '/{listener_id}'
        self.pool = self.create_pool(
            self.lb_id, constants.PROTOCOL_HTTP,
            constants.LB_ALGORITHM_ROUND_ROBIN)
        self.pool_id = self.pool.get('pool').get('id')
        self.set_lb_status(self.lb_id)

    def test_get_all_admin(self):
        project_id = uuidutils.generate_uuid()
        lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
                                        name='lb1', project_id=project_id)
        lb1_id = lb1.get('loadbalancer').get('id')
        self.set_lb_status(lb1_id)
        listener1 = self.create_listener(
            constants.PROTOCOL_HTTP, 80, lb1_id,
            tags=['test_tag1']).get(self.root_tag)
        self.set_lb_status(lb1_id)
        listener2 = self.create_listener(
            constants.PROTOCOL_HTTP, 81, lb1_id,
            tags=['test_tag2']).get(self.root_tag)
        self.set_lb_status(lb1_id)
        listener3 = self.create_listener(
            constants.PROTOCOL_HTTP, 82, lb1_id,
            tags=['test_tag3']).get(self.root_tag)
        self.set_lb_status(lb1_id)
        listeners = self.get(self.LISTENERS_PATH).json.get(self.root_tag_list)
        self.assertEqual(3, len(listeners))
        listener_id_ports = [(li.get('id'), li.get('protocol_port'),
                              li.get('tags'))
                             for li in listeners]
        self.assertIn((listener1.get('id'), listener1.get('protocol_port'),
                       listener1.get('tags')),
                      listener_id_ports)
        self.assertIn((listener2.get('id'), listener2.get('protocol_port'),
                       listener2.get('tags')),
                      listener_id_ports)
        self.assertIn((listener3.get('id'), listener3.get('protocol_port'),
                       listener3.get('tags')),
                      listener_id_ports)

    def test_get_all_non_admin(self):
        project_id = uuidutils.generate_uuid()
        lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
                                        name='lb1', project_id=project_id)
        lb1_id = lb1.get('loadbalancer').get('id')
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 80,
                             lb1_id)
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 81,
                             lb1_id)
        self.set_lb_status(lb1_id)
        listener3 = self.create_listener(constants.PROTOCOL_HTTP, 82,
                                         self.lb_id).get(self.root_tag)
        self.set_lb_status(self.lb_id)

        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.KEYSTONE)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               listener3['project_id']):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['load-balancer_member', 'member'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': self.project_id}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):
                listeners = self.get(
                    self.LISTENERS_PATH).json.get(self.root_tag_list)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)

        self.assertEqual(1, len(listeners))
        listener_id_ports = [(li.get('id'), li.get('protocol_port'))
                             for li in listeners]
        self.assertIn((listener3.get('id'), listener3.get('protocol_port')),
                      listener_id_ports)

    def test_get_all_unscoped_token(self):
        project_id = uuidutils.generate_uuid()
        lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
                                        name='lb1', project_id=project_id)
        lb1_id = lb1.get('loadbalancer').get('id')
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 80,
                             lb1_id)
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 81,
                             lb1_id)
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 82,
                             self.lb_id).get(self.root_tag)
        self.set_lb_status(self.lb_id)

        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.KEYSTONE)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               None):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['load-balancer_member'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': None}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):
                result = self.get(self.LISTENERS_PATH, status=403).json
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)
        self.assertEqual(self.NOT_AUTHORIZED_BODY, result)

    def test_get_all_non_admin_global_observer(self):
        project_id = uuidutils.generate_uuid()
        lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
                                        name='lb1', project_id=project_id)
        lb1_id = lb1.get('loadbalancer').get('id')
        self.set_lb_status(lb1_id)
        listener1 = self.create_listener(
            constants.PROTOCOL_HTTP, 80, lb1_id).get(self.root_tag)
        self.set_lb_status(lb1_id)
        listener2 = self.create_listener(
            constants.PROTOCOL_HTTP, 81, lb1_id).get(self.root_tag)
        self.set_lb_status(lb1_id)
        listener3 = self.create_listener(
            constants.PROTOCOL_HTTP, 82, lb1_id).get(self.root_tag)
        self.set_lb_status(lb1_id)

        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.KEYSTONE)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               self.project_id):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['admin'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': self.project_id}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):
                listeners = self.get(self.LISTENERS_PATH)
                listeners = listeners.json.get(self.root_tag_list)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)

        self.assertEqual(3, len(listeners))
        listener_id_ports = [(li.get('id'), li.get('protocol_port'))
                             for li in listeners]
        self.assertIn((listener1.get('id'), listener1.get('protocol_port')),
                      listener_id_ports)
        self.assertIn((listener2.get('id'), listener2.get('protocol_port')),
                      listener_id_ports)
        self.assertIn((listener3.get('id'), listener3.get('protocol_port')),
                      listener_id_ports)

    def test_get_all_not_authorized(self):
        project_id = uuidutils.generate_uuid()
        lb1 = self.create_load_balancer(uuidutils.generate_uuid(),
                                        name='lb1', project_id=project_id)
        lb1_id = lb1.get('loadbalancer').get('id')
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 80,
                             lb1_id)
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 81,
                             lb1_id)
        self.set_lb_status(lb1_id)
        self.create_listener(constants.PROTOCOL_HTTP, 82,
                             self.lb_id).get(self.root_tag)
        self.set_lb_status(self.lb_id)

        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.KEYSTONE)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               uuidutils.generate_uuid()):
            listeners = self.get(self.LISTENERS_PATH, status=403).json
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)
        self.assertEqual(self.NOT_AUTHORIZED_BODY, listeners)

    def test_get_all_by_project_id(self):
        project1_id = uuidutils.generate_uuid()
        project2_id = uuidutils.generate_uuid()
        lb1 = self.create_load_balancer(uuidutils.generate_uuid(), name='lb1',
                                        project_id=project1_id)
        lb1_id = lb1.get('loadbalancer').get('id')
        self.set_lb_status(lb1_id)
        lb2 = self.create_load_balancer(uuidutils.generate_uuid(), name='lb2',
                                        project_id=project2_id)
        lb2_id = lb2.get('loadbalancer').get('id')

        self.set_lb_status(lb2_id)
        listener1 = self.create_listener(constants.PROTOCOL_HTTP, 80, lb1_id,
                                         name='listener1').get(self.root_tag)
        self.set_lb_status(lb1_id)
        listener2 = self.create_listener(constants.PROTOCOL_HTTP, 81, lb1_id,
                                         name='listener2').get(self.root_tag)
        self.set_lb_status(lb1_id)
        listener3 = self.create_listener(constants.PROTOCOL_HTTP, 82, lb2_id,
                                         name='listener3').get(self.root_tag)
        self.set_lb_status(lb2_id)
        listeners = self.get(
            self.LISTENERS_PATH,
            params={'project_id': project1_id}).json.get(self.root_tag_list)

        self.assertEqual(2, len(listeners))
        listener_id_ports = [(li.get('id'), li.get('protocol_port'))
                             for li in listeners]
        self.assertIn((listener1.get('id'), listener1.get('protocol_port')),
                      listener_id_ports)
        self.assertIn((listener2.get('id'), listener2.get('protocol_port')),
                      listener_id_ports)
        listeners = self.get(
            self.LISTENERS_PATH,
            params={'project_id': project2_id}).json.get(self.root_tag_list)
        listener_id_ports = [(li.get('id'), li.get('protocol_port'))
                             for li in listeners]
        self.assertEqual(1, len(listeners))
        self.assertIn((listener3.get('id'), listener3.get('protocol_port')),
                      listener_id_ports)

    def test_get_all_sorted(self):
        self.create_listener(constants.PROTOCOL_HTTP, 80,
                             self.lb_id,
                             name='listener1')
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP, 81,
                             self.lb_id,
                             name='listener2')
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP, 82,
                             self.lb_id,
                             name='listener3')
        self.set_lb_status(self.lb_id)
        response = self.get(self.LISTENERS_PATH,
                            params={'sort': 'name:desc'})
        listeners_desc = response.json.get(self.root_tag_list)
        response = self.get(self.LISTENERS_PATH,
                            params={'sort': 'name:asc'})
        listeners_asc = response.json.get(self.root_tag_list)

        self.assertEqual(3, len(listeners_desc))
        self.assertEqual(3, len(listeners_asc))

        listener_id_names_desc = [(listener.get('id'), listener.get('name'))
                                  for listener in listeners_desc]
        listener_id_names_asc = [(listener.get('id'), listener.get('name'))
                                 for listener in listeners_asc]
        self.assertEqual(listener_id_names_asc,
                         list(reversed(listener_id_names_desc)))

    def test_get_all_limited(self):
        self.create_listener(constants.PROTOCOL_HTTP, 80,
                             self.lb_id,
                             name='listener1')
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP, 81,
                             self.lb_id,
                             name='listener2')
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP, 82,
                             self.lb_id,
                             name='listener3')
        self.set_lb_status(self.lb_id)

        # First two -- should have 'next' link
        first_two = self.get(self.LISTENERS_PATH, params={'limit': 2}).json
        objs = first_two[self.root_tag_list]
        links = first_two[self.root_tag_links]
        self.assertEqual(2, len(objs))
        self.assertEqual(1, len(links))
        self.assertEqual('next', links[0]['rel'])

        # Third + off the end -- should have previous link
        third = self.get(self.LISTENERS_PATH, params={
            'limit': 2,
            'marker': first_two[self.root_tag_list][1]['id']}).json
        objs = third[self.root_tag_list]
        links = third[self.root_tag_links]
        self.assertEqual(1, len(objs))
        self.assertEqual(1, len(links))
        self.assertEqual('previous', links[0]['rel'])

        # Middle -- should have both links
        middle = self.get(self.LISTENERS_PATH, params={
            'limit': 1,
            'marker': first_two[self.root_tag_list][0]['id']}).json
        objs = middle[self.root_tag_list]
        links = middle[self.root_tag_links]
        self.assertEqual(1, len(objs))
        self.assertEqual(2, len(links))
        self.assertCountEqual(['previous', 'next'],
                              [link['rel'] for link in links])

    def test_get_all_fields_filter(self):
        self.create_listener(constants.PROTOCOL_HTTP, 80,
                             self.lb_id,
                             name='listener1')
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP, 81,
                             self.lb_id,
                             name='listener2')
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP, 82,
                             self.lb_id,
                             name='listener3')
        self.set_lb_status(self.lb_id)

        lis = self.get(self.LISTENERS_PATH, params={
            'fields': ['id', 'project_id']}).json
        for li in lis['listeners']:
            self.assertIn('id', li)
            self.assertIn('project_id', li)
            self.assertNotIn('description', li)

    def test_get_one_fields_filter(self):
        listener1 = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id,
            name='listener1').get(self.root_tag)
        self.set_lb_status(self.lb_id)

        li = self.get(
            self.LISTENER_PATH.format(listener_id=listener1.get('id')),
            params={'fields': ['id', 'project_id']}).json.get(self.root_tag)
        self.assertIn('id', li)
        self.assertIn('project_id', li)
        self.assertNotIn('description', li)

    def test_get_all_filter(self):
        li1 = self.create_listener(constants.PROTOCOL_HTTP,
                                   80,
                                   self.lb_id,
                                   name='listener1').get(self.root_tag)
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP,
                             81,
                             self.lb_id,
                             name='listener2').get(self.root_tag)
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP,
                             82,
                             self.lb_id,
                             name='listener3').get(self.root_tag)
        self.set_lb_status(self.lb_id)

        lis = self.get(self.LISTENERS_PATH, params={
            'id': li1['id']}).json
        self.assertEqual(1, len(lis['listeners']))
        self.assertEqual(li1['id'],
                         lis['listeners'][0]['id'])

    def test_get_all_tags_filter(self):
        listener1 = self.create_listener(
            constants.PROTOCOL_HTTP,
            80,
            self.lb_id,
            name='listener1',
            tags=['test_tag1', 'test_tag2']
        ).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        listener2 = self.create_listener(
            constants.PROTOCOL_HTTP,
            81,
            self.lb_id,
            name='listener2',
            tags=['test_tag2', 'test_tag3']
        ).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        listener3 = self.create_listener(
            constants.PROTOCOL_HTTP,
            82,
            self.lb_id,
            name='listener3',
            tags=['test_tag4', 'test_tag5']
        ).get(self.root_tag)
        self.set_lb_status(self.lb_id)

        listeners = self.get(
            self.LISTENERS_PATH,
            params={'tags': 'test_tag2'}
        ).json.get(self.root_tag_list)
        self.assertIsInstance(listeners, list)
        self.assertEqual(2, len(listeners))
        self.assertEqual(
            [listener1.get('id'), listener2.get('id')],
            [listener.get('id') for listener in listeners]
        )

        listeners = self.get(
            self.LISTENERS_PATH,
            params={'tags': ['test_tag2', 'test_tag3']}
        ).json.get(self.root_tag_list)
        self.assertIsInstance(listeners, list)
        self.assertEqual(1, len(listeners))
        self.assertEqual(
            [listener2.get('id')],
            [listener.get('id') for listener in listeners]
        )

        listeners = self.get(
            self.LISTENERS_PATH,
            params={'tags-any': 'test_tag2'}
        ).json.get(self.root_tag_list)
        self.assertIsInstance(listeners, list)
        self.assertEqual(2, len(listeners))
        self.assertEqual(
            [listener1.get('id'), listener2.get('id')],
            [listener.get('id') for listener in listeners]
        )

        listeners = self.get(
            self.LISTENERS_PATH,
            params={'not-tags': 'test_tag2'}
        ).json.get(self.root_tag_list)
        self.assertIsInstance(listeners, list)
        self.assertEqual(1, len(listeners))
        self.assertEqual(
            [listener3.get('id')],
            [listener.get('id') for listener in listeners]
        )

        listeners = self.get(
            self.LISTENERS_PATH,
            params={'not-tags-any': ['test_tag2', 'test_tag4']}
        ).json.get(self.root_tag_list)
        self.assertIsInstance(listeners, list)
        self.assertEqual(0, len(listeners))

        listeners = self.get(
            self.LISTENERS_PATH,
            params={'tags': 'test_tag2',
                    'tags-any': ['test_tag1', 'test_tag3']}
        ).json.get(self.root_tag_list)
        self.assertIsInstance(listeners, list)
        self.assertEqual(2, len(listeners))
        self.assertEqual(
            [listener1.get('id'), listener2.get('id')],
            [listener.get('id') for listener in listeners]
        )

        listeners = self.get(
            self.LISTENERS_PATH,
            params={'tags': 'test_tag2', 'not-tags': 'test_tag2'}
        ).json.get(self.root_tag_list)
        self.assertIsInstance(listeners, list)
        self.assertEqual(0, len(listeners))

    def test_get_all_hides_deleted(self):
        api_listener = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag)

        response = self.get(self.LISTENERS_PATH)
        objects = response.json.get(self.root_tag_list)
        self.assertEqual(len(objects), 1)
        self.set_object_status(self.listener_repo, api_listener.get('id'),
                               provisioning_status=constants.DELETED)
        response = self.get(self.LISTENERS_PATH)
        objects = response.json.get(self.root_tag_list)
        self.assertEqual(len(objects), 0)

    def test_get(self):
        listener = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag)
        response = self.get(self.listener_path.format(
            listener_id=listener['id']))
        api_listener = response.json.get(self.root_tag)
        self.assertEqual(listener, api_listener)
        self.assertEqual([], api_listener['tags'])

    def test_get_authorized(self):
        listener = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag)

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.TESTING)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               self.project_id):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['load-balancer_member', 'member'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': self.project_id}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):

                response = self.get(self.listener_path.format(
                    listener_id=listener['id']))
        api_listener = response.json.get(self.root_tag)
        self.assertEqual(listener, api_listener)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)

    def test_get_not_authorized(self):
        listener = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag)

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.TESTING)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               uuidutils.generate_uuid()):
            response = self.get(self.listener_path.format(
                listener_id=listener['id']), status=403)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)
        self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json)

    def test_get_deleted_gives_404(self):
        api_listener = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id).get(self.root_tag)

        self.set_object_status(self.listener_repo, api_listener.get('id'),
                               provisioning_status=constants.DELETED)
        self.get(self.LISTENER_PATH.format(listener_id=api_listener.get('id')),
                 status=404)

    def test_get_bad_listener_id(self):
        listener_path = self.listener_path
        self.get(listener_path.format(listener_id='SEAN-CONNERY'), status=404)

    # 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,
                       'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                       'protocol_port': 80, 'connection_limit': 10,
                       'default_tls_container_ref': uuidutils.generate_uuid(),
                       'sni_container_refs': [sni1, sni2],
                       'insert_headers': {},
                       'project_id': self.project_id,
                       'loadbalancer_id': self.lb_id,
                       'tags': ['test_tag']}
        lb_listener.update(optionals)
        body = self._build_body(lb_listener)
        response = self.post(self.LISTENERS_PATH, body, status=response_status)
        if response_status >= 300:
            return response
        listener_api = response.json['listener']
        extra_expects = {'provisioning_status': constants.PENDING_CREATE,
                         'operating_status': constants.OFFLINE}
        lb_listener.update(extra_expects)
        self.assertTrue(uuidutils.is_uuid_like(listener_api.get('id')))
        for key, value in optionals.items():
            self.assertEqual(value, listener_api.get(key))
        lb_listener['id'] = listener_api.get('id')
        lb_listener.pop('sni_container_refs')
        sni_ex = [sni1, sni2]
        sni_resp = listener_api.pop('sni_container_refs')
        self.assertEqual(2, len(sni_resp))
        for sni in sni_resp:
            self.assertIn(sni, sni_ex)
        self.assertIsNotNone(listener_api.pop('created_at'))
        self.assertIsNone(listener_api.pop('updated_at'))
        self.assertEqual(['test_tag'], listener_api['tags'])
        self.assertNotEqual(lb_listener, listener_api)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id, listener_api.get('id'))
        return listener_api

    def test_create_with_timeouts(self):
        optionals = {
            'timeout_client_data': 1,
            'timeout_member_connect': 2,
            'timeout_member_data': constants.MIN_TIMEOUT,
            'timeout_tcp_inspect': constants.MAX_TIMEOUT,
        }
        listener_api = self.test_create(**optionals)
        self.assertEqual(1, listener_api.get('timeout_client_data'))
        self.assertEqual(2, listener_api.get('timeout_member_connect'))
        self.assertEqual(constants.MIN_TIMEOUT,
                         listener_api.get('timeout_member_data'))
        self.assertEqual(constants.MAX_TIMEOUT,
                         listener_api.get('timeout_tcp_inspect'))

    def test_create_with_default_timeouts(self):
        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        self.conf.config(group='haproxy_amphora', timeout_client_data=20)
        self.conf.config(group='haproxy_amphora', timeout_member_connect=21)
        self.conf.config(group='haproxy_amphora',
                         timeout_member_data=constants.MIN_TIMEOUT)
        self.conf.config(group='haproxy_amphora',
                         timeout_tcp_inspect=constants.MAX_TIMEOUT)

        listener_api = self.test_create()
        self.assertEqual(20, listener_api.get('timeout_client_data'))
        self.assertEqual(21, listener_api.get('timeout_member_connect'))
        self.assertEqual(constants.MIN_TIMEOUT,
                         listener_api.get('timeout_member_data'))
        self.assertEqual(constants.MAX_TIMEOUT,
                         listener_api.get('timeout_tcp_inspect'))

    def test_create_with_timeouts_too_high(self):
        optionals = {
            'timeout_client_data': 1,
            'timeout_member_connect': 1,
            'timeout_member_data': 1,
            'timeout_tcp_inspect': 1,
        }
        for field in optionals.items():
            optionals.update({field[0]: constants.MAX_TIMEOUT + 1})
            resp = self.test_create(response_status=400, **optionals).json
            optionals.update({field[0]: 1})
            fault = resp.get('faultstring')
            self.assertIn(
                f'Invalid input for field/attribute {field[0]}', fault)
            self.assertIn(
                f'Value should be lower or equal to {constants.MAX_TIMEOUT}',
                fault)

    def test_create_with_timeouts_too_low(self):
        optionals = {
            'timeout_client_data': 1,
            'timeout_member_connect': 2,
            'timeout_member_data': 3,
            'timeout_tcp_inspect': constants.MIN_TIMEOUT - 1,
        }
        resp = self.test_create(response_status=400, **optionals).json
        fault = resp.get('faultstring')
        self.assertIn(
            'Invalid input for field/attribute timeout_tcp_inspect', fault)
        self.assertIn(
            f'Value should be greater or equal to {constants.MIN_TIMEOUT}',
            fault)

    def test_create_udp_case(self):
        api_listener = self.create_listener(constants.PROTOCOL_UDP, 6666,
                                            self.lb_id).get(self.root_tag)
        self.assertEqual(constants.PROTOCOL_UDP, api_listener.get('protocol'))
        self.assertEqual(6666, api_listener.get('protocol_port'))
        self.assert_correct_status(
            lb_id=self.lb_id, listener_id=api_listener.get('id'),
            lb_prov_status=constants.PENDING_UPDATE,
            listener_prov_status=constants.PENDING_CREATE,
            listener_op_status=constants.OFFLINE)

    def test_negative_create_udp_case(self):
        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()
        req_dict = {'name': 'listener1', 'default_pool_id': None,
                    'description': 'desc1',
                    'admin_state_up': False,
                    'protocol': constants.PROTOCOL_UDP,
                    'protocol_port': 6666, 'connection_limit': 10,
                    'default_tls_container_ref': uuidutils.generate_uuid(),
                    'sni_container_refs': [sni1, sni2],
                    'insert_headers': {},
                    'loadbalancer_id': self.lb_id}
        expect_error_msg = ("Validation failure: %s protocol listener does "
                            "not support TLS.") % constants.PROTOCOL_UDP
        res = self.post(self.LISTENERS_PATH, self._build_body(req_dict),
                        status=400, expect_errors=True)
        self.assertEqual(expect_error_msg, res.json['faultstring'])
        self.assert_correct_status(lb_id=self.lb_id)

        # Default pool protocol is udp which is different with listener
        # protocol.
        udp_pool_id = self.create_pool(
            self.lb_id, constants.PROTOCOL_UDP,
            constants.LB_ALGORITHM_ROUND_ROBIN).get('pool').get('id')
        self.set_lb_status(self.lb_id)
        lb_listener = {'name': 'listener1',
                       'default_pool_id': udp_pool_id,
                       'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80,
                       'loadbalancer_id': self.lb_id}
        expect_error_msg = ("Validation failure: The pool protocol '%s' is "
                            "invalid while the listener protocol is '%s'.") % (
                                constants.PROTOCOL_UDP,
                                lb_listener['protocol'])
        res = self.post(self.LISTENERS_PATH, self._build_body(lb_listener),
                        status=400, expect_errors=True)
        self.assertEqual(expect_error_msg, res.json['faultstring'])
        self.assert_correct_status(lb_id=self.lb_id)

    def test_create_duplicate_fails(self):
        self.create_listener(constants.PROTOCOL_HTTP, 80, self.lb_id)
        self.set_lb_status(self.lb_id)
        self.create_listener(constants.PROTOCOL_HTTP, 80, self.lb_id,
                             status=409)

    def test_create_bad_tls_ref(self):
        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()
        tls_ref = uuidutils.generate_uuid()
        lb_listener = {'name': 'listener1', 'default_pool_id': None,
                       'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                       'protocol_port': 80,
                       'sni_container_refs': [sni1, sni2],
                       'default_tls_container_ref': tls_ref,
                       'loadbalancer_id': self.lb_id}

        body = self._build_body(lb_listener)
        self.cert_manager_mock().get_cert.side_effect = [
            Exception("bad cert"), None, Exception("bad_cert")]
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertIn(sni1, response['faultstring'])
        self.assertNotIn(sni2, response['faultstring'])
        self.assertIn(tls_ref, response['faultstring'])

    def test_create_with_certs_not_terminated_https(self):
        optionals = {
            'default_tls_container_ref': uuidutils.generate_uuid(),
            'protocol': constants.PROTOCOL_TCP
        }
        resp = self.test_create(response_status=400, **optionals).json
        fault = resp.get('faultstring')
        self.assertIn(
            'Certificate container references are not allowed on ', fault)
        self.assertIn(
            f'{constants.PROTOCOL_TCP} protocol listeners.', fault)

    def test_create_without_certs_if_terminated_https(self):
        optionals = {
            'default_tls_container_ref': None,
            'sni_container_refs': None,
            'protocol': constants.PROTOCOL_TERMINATED_HTTPS
        }
        resp = self.test_create(response_status=400, **optionals).json
        fault = resp.get('faultstring')
        self.assertIn(
            'An SNI or default certificate container reference must ', fault)
        self.assertIn(
            'be provided for {} protocol listeners.'.format(
                constants.PROTOCOL_TERMINATED_HTTPS), fault)

    def test_create_client_ca_cert_without_tls_cert(self):
        optionals = {
            'default_tls_container_ref': None,
            'sni_container_refs': None,
            'client_ca_tls_container_ref': uuidutils.generate_uuid(),
            'protocol': constants.PROTOCOL_TERMINATED_HTTPS
        }
        resp = self.test_create(response_status=400, **optionals).json
        fault = resp.get('faultstring')
        self.assertIn(
            'An SNI or default certificate container reference must ', fault)
        self.assertIn(
            'be provided with a client CA container reference.', fault)

    def test_create_crl_without_ca_cert(self):
        optionals = {
            'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
            'client_ca_tls_container_ref': None,
            'client_crl_container_ref': uuidutils.generate_uuid()
        }
        resp = self.test_create(response_status=400, **optionals).json
        fault = resp.get('faultstring')
        self.assertIn(
            'A client authentication CA reference is required to specify a '
            'client authentication revocation list.', fault)

    def test_create_with_default_pool_id(self):
        lb_listener = {'name': 'listener1',
                       'default_pool_id': self.pool_id,
                       'description': 'desc1',
                       'admin_state_up': False,
                       '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)
        api_listener = response.json['listener']
        self.assertEqual(api_listener.get('default_pool_id'),
                         self.pool_id)

    def test_create_with_bad_default_pool_id(self):
        lb_listener = {'name': 'listener1',
                       'default_pool_id': uuidutils.generate_uuid(),
                       'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80,
                       'loadbalancer_id': self.lb_id}
        body = self._build_body(lb_listener)
        self.post(self.LISTENERS_PATH, body, status=404)

    def test_create_with_shared_default_pool_id(self):
        lb_listener1 = {'name': 'listener1',
                        'default_pool_id': self.pool_id,
                        'description': 'desc1',
                        'admin_state_up': False,
                        'protocol': constants.PROTOCOL_HTTP,
                        'protocol_port': 80,
                        'loadbalancer_id': self.lb_id}
        lb_listener2 = {'name': 'listener2',
                        'default_pool_id': self.pool_id,
                        'description': 'desc2',
                        'admin_state_up': False,
                        'protocol': constants.PROTOCOL_HTTP,
                        'protocol_port': 81,
                        'loadbalancer_id': self.lb_id}
        body1 = self._build_body(lb_listener1)
        body2 = self._build_body(lb_listener2)
        listener1 = self.post(self.LISTENERS_PATH, body1).json['listener']
        self.set_lb_status(self.lb_id, constants.ACTIVE)
        listener2 = self.post(self.LISTENERS_PATH, body2).json['listener']
        self.assertEqual(listener1['default_pool_id'], self.pool_id)
        self.assertEqual(listener1['default_pool_id'],
                         listener2['default_pool_id'])

    def test_create_with_project_id(self):
        self.test_create(project_id=self.project_id)

    def test_create_defaults(self):
        defaults = {'name': None, 'default_pool_id': None,
                    'description': None, 'admin_state_up': True,
                    'connection_limit': None,
                    'default_tls_container_ref': None,
                    'sni_container_refs': [], 'project_id': None,
                    'insert_headers': {}}
        lb_listener = {'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)
        listener_api = response.json['listener']
        extra_expects = {'provisioning_status': constants.PENDING_CREATE,
                         'operating_status': constants.OFFLINE}
        lb_listener.update(extra_expects)
        lb_listener.update(defaults)
        self.assertTrue(uuidutils.is_uuid_like(listener_api.get('id')))
        lb_listener['id'] = listener_api.get('id')
        self.assertIsNotNone(listener_api.pop('created_at'))
        self.assertIsNone(listener_api.pop('updated_at'))
        self.assertNotEqual(lb_listener, listener_api)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id, listener_api['id'])

    def test_create_over_quota(self):
        self.start_quota_mock(data_models.Listener)
        lb_listener = {'name': 'listener1',
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80,
                       'loadbalancer_id': self.lb_id}
        body = self._build_body(lb_listener)
        self.post(self.LISTENERS_PATH, body, status=403)

    @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):
        lb_listener = {'name': 'listener1', 'default_pool_id': None,
                       'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'default_tls_container_ref': None,
                       'sni_container_refs': None,
                       'insert_headers': {},
                       'project_id': self.project_id,
                       'loadbalancer_id': self.lb_id}
        lb_listener.update(optionals)
        body = self._build_body(lb_listener)

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.TESTING)

        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               self.project_id):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['load-balancer_member', 'member'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': self.project_id}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):
                response = self.post(self.LISTENERS_PATH, body)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)

        listener_api = response.json['listener']
        extra_expects = {'provisioning_status': constants.PENDING_CREATE,
                         'operating_status': constants.OFFLINE}
        lb_listener.update(extra_expects)
        self.assertTrue(uuidutils.is_uuid_like(listener_api.get('id')))
        for key, value in optionals.items():
            self.assertEqual(value, listener_api.get(key))
        lb_listener['id'] = listener_api.get('id')
        self.assertIsNotNone(listener_api.pop('created_at'))
        self.assertIsNone(listener_api.pop('updated_at'))
        self.assertNotEqual(lb_listener, listener_api)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id, listener_api.get('id'))

    def test_create_not_authorized(self, **optionals):
        lb_listener = {'name': 'listener1', 'default_pool_id': None,
                       'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'default_tls_container_ref': None,
                       'sni_container_refs': None,
                       'insert_headers': {},
                       'project_id': self.project_id,
                       'loadbalancer_id': self.lb_id}
        lb_listener.update(optionals)
        body = self._build_body(lb_listener)

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.TESTING)

        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               uuidutils.generate_uuid()):
            response = self.post(self.LISTENERS_PATH, body, status=403)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)
        self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json)

    def test_create_with_ca_cert(self):
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)

        optionals = {
            'client_ca_tls_container_ref': uuidutils.generate_uuid()
        }
        listener_api = self.test_create(**optionals)
        self.assertEqual(optionals['client_ca_tls_container_ref'],
                         listener_api.get('client_ca_tls_container_ref'))
        self.assertEqual(constants.CLIENT_AUTH_NONE,
                         listener_api.get('client_authentication'))

    def test_create_tls_with_no_subject_no_alt_names(self):
        tls_cert_mock = mock.MagicMock()
        tls_cert_mock.get_certificate.return_value = (
            sample_certs.NOCN_NOSUBALT_CRT)
        self.cert_manager_mock().get_cert.return_value = tls_cert_mock

        lb_listener = {'name': 'listener1-no-subject-no-alt-names',
                       'default_pool_id': None,
                       'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                       'protocol_port': 80, 'connection_limit': 10,
                       'default_tls_container_ref': uuidutils.generate_uuid(),
                       'insert_headers': {},
                       'project_id': self.project_id,
                       'loadbalancer_id': self.lb_id,
                       'tags': ['test_tag']}
        body = self._build_body(lb_listener)
        response = self.post(self.LISTENERS_PATH, body, status=400)
        self.assertIn("No CN or DNSName", response)

    def test_create_tls_with_no_subject_with_alt_names(self):
        tls_cert_mock = mock.MagicMock()
        tls_cert_mock.get_certificate.return_value = (
            sample_certs.NOCN_SUBALT_CRT)
        tls_cert_mock.get_private_key.return_value = (
            sample_certs.NOCN_SUBALT_KEY)
        tls_cert_mock.get_private_key_passphrase.return_value = None
        self.cert_manager_mock().get_cert.return_value = tls_cert_mock

        lb_listener = {'name': 'listener1-no-subject',
                       'default_pool_id': None,
                       'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                       'protocol_port': 80, 'connection_limit': 10,
                       'default_tls_container_ref': uuidutils.generate_uuid(),
                       'insert_headers': {},
                       'project_id': self.project_id,
                       'loadbalancer_id': self.lb_id,
                       'tags': ['test_tag']}
        body = self._build_body(lb_listener)
        response = self.post(self.LISTENERS_PATH, body, status=201)
        self.assertIn("PENDING_CREATE", response)

    def test_create_with_ca_cert_and_option(self):
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)
        optionals = {
            'client_ca_tls_container_ref': uuidutils.generate_uuid(),
            'client_authentication': constants.CLIENT_AUTH_MANDATORY
        }
        listener_api = self.test_create(**optionals)
        self.assertEqual(optionals['client_ca_tls_container_ref'],
                         listener_api.get('client_ca_tls_container_ref'))
        self.assertEqual(optionals['client_authentication'],
                         listener_api.get('client_authentication'))

    def test_create_with_ca_cert_and_crl(self):
        # Load up sample certs to test the validation
        self.cert_manager_mock().get_secret.side_effect = [
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]

        optionals = {
            'client_ca_tls_container_ref': uuidutils.generate_uuid(),
            'client_crl_container_ref': uuidutils.generate_uuid()
        }
        listener_api = self.test_create(**optionals)
        self.assertEqual(optionals['client_ca_tls_container_ref'],
                         listener_api.get('client_ca_tls_container_ref'))
        self.assertEqual(constants.CLIENT_AUTH_NONE,
                         listener_api.get('client_authentication'))
        self.assertEqual(optionals['client_crl_container_ref'],
                         listener_api.get('client_crl_container_ref'))

    # 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_crl_mismatch_ca_cert(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]}
        self.cert_manager_mock().get_secret.side_effect = [
            sample_certs.X509_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CERT, sample_certs.X509_CA_CRL]

        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()
        lb_listener = {
            'name': 'listener1', 'default_pool_id': None,
            'description': 'desc1',
            'admin_state_up': False,
            'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
            'protocol_port': 80,
            'default_tls_container_ref': uuidutils.generate_uuid(),
            'sni_container_refs': [sni1, sni2],
            'project_id': self.project_id,
            'loadbalancer_id': self.lb_id,
            'client_ca_tls_container_ref': uuidutils.generate_uuid(),
            'client_crl_container_ref': uuidutils.generate_uuid()
        }
        body = self._build_body(lb_listener)
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertEqual(
            "Validation failure: The CRL specified is not valid for client "
            "certificate authority reference supplied.",
            response['faultstring'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_create_with_ca_cert_negative_cases(self, mock_load_cert):
        # create just with option or crl,
        # no client_ca_tls_container_ref specified.
        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()

        for opt in [{'client_authentication': constants.CLIENT_AUTH_MANDATORY,
                     'client_crl_container_ref': uuidutils.generate_uuid()},
                    {'client_authentication': constants.CLIENT_AUTH_OPTIONAL,
                     'client_crl_container_ref': uuidutils.generate_uuid()}]:
            lb_listener = {
                'name': 'listener1', 'default_pool_id': None,
                'description': 'desc1',
                'admin_state_up': False,
                'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                'protocol_port': 80,
                'default_tls_container_ref': uuidutils.generate_uuid(),
                'sni_container_refs': [sni1, sni2],
                'project_id': self.project_id,
                'loadbalancer_id': self.lb_id}
            lb_listener.update(opt)
            body = self._build_body(lb_listener)
            response = self.post(self.LISTENERS_PATH, body, status=400).json
            self.assertEqual(
                "Validation failure: Client authentication setting %s "
                "requires a client CA container reference." %
                opt['client_authentication'], response['faultstring'])

    def test_create_with_bad_ca_cert_ref(self):
        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()
        lb_listener = {
            'name': 'listener1', 'default_pool_id': None,
            'description': 'desc1',
            'admin_state_up': False,
            'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
            'protocol_port': 80,
            'default_tls_container_ref': uuidutils.generate_uuid(),
            'sni_container_refs': [sni1, sni2],
            'project_id': self.project_id,
            'loadbalancer_id': self.lb_id,
            'client_ca_tls_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        self.cert_manager_mock().get_cert.side_effect = [
            'cert 1', 'cert 2', 'cert 3']
        self.cert_manager_mock().get_secret.side_effect = [
            Exception('bad ca cert')]
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertEqual("Could not retrieve certificate: ['%s']" %
                         lb_listener['client_ca_tls_container_ref'],
                         response['faultstring'])

    def test_create_with_unreachable_crl(self):
        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()
        lb_listener = {
            'name': 'listener1', 'default_pool_id': None,
            'description': 'desc1',
            'admin_state_up': False,
            'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
            'protocol_port': 80,
            'default_tls_container_ref': uuidutils.generate_uuid(),
            'sni_container_refs': [sni1, sni2],
            'project_id': self.project_id,
            'loadbalancer_id': self.lb_id,
            'client_ca_tls_container_ref': uuidutils.generate_uuid(),
            'client_crl_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        self.cert_manager_mock().get_secret.side_effect = Exception(
            'bad CRL ref')
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertIn(lb_listener['client_crl_container_ref'],
                      response['faultstring'])

    def test_create_with_bad_ca_cert(self):
        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()
        lb_listener = {
            'name': 'listener1', 'default_pool_id': None,
            'description': 'desc1',
            'admin_state_up': False,
            'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
            'protocol_port': 80,
            'default_tls_container_ref': uuidutils.generate_uuid(),
            'sni_container_refs': [sni1, sni2],
            'project_id': self.project_id,
            'loadbalancer_id': self.lb_id,
            'client_ca_tls_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        self.cert_manager_mock().get_cert.side_effect = [
            'cert 1', 'cert 2', 'cert 3']
        self.cert_manager_mock().get_secret.return_value = 'bad cert'
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertIn("The client authentication CA certificate is invalid. "
                      "It must be a valid x509 PEM format certificate.",
                      response['faultstring'])

    def _test_create_with_allowed_cidrs(self, allowed_cidrs, lb_id):
        listener = self.create_listener(constants.PROTOCOL_TCP,
                                        80, lb_id,
                                        allowed_cidrs=allowed_cidrs)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(allowed_cidrs, get_listener.get('allowed_cidrs'))

    def test_create_with_allowed_cidrs_ipv4(self):
        allowed_cidrs = ['10.0.1.0/24', '172.16.55.0/25']
        self._test_create_with_allowed_cidrs(allowed_cidrs, self.lb_id)

    def test_create_with_allowed_cidrs_ipv6(self):
        lb_ipv6 = self.create_load_balancer(
            uuidutils.generate_uuid(),
            vip_address='2001:db9:a1b:13f0::1',
        )
        lb_id = lb_ipv6.get('loadbalancer').get('id')
        self.set_lb_status(lb_id)
        allowed_cidrs = ['2001:db8:a0b:12f0::/64', '2a02:8071:69e::/64']
        self._test_create_with_allowed_cidrs(allowed_cidrs, lb_id)

    def test_create_with_bad_allowed_cidrs(self):
        allowed_cidrs = ['10.0.1.0/33', '172.16.55.1.0/25']
        lb_listener = {
            'protocol': constants.PROTOCOL_TCP,
            'protocol_port': 80,
            'project_id': self.project_id,
            'loadbalancer_id': self.lb_id,
            'allowed_cidrs': allowed_cidrs}
        body = self._build_body(lb_listener)
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertIn("Invalid input for field/attribute allowed_cidrs. "
                      "Value: '%s'. Value should be IPv4 or IPv6 CIDR format"
                      % allowed_cidrs, response['faultstring'])

    def test_create_with_incompatible_allowed_cidrs_ipv6(self):
        lb_listener = {
            'protocol': constants.PROTOCOL_TCP,
            'protocol_port': 80,
            'project_id': self.project_id,
            'loadbalancer_id': self.lb_id,
            'allowed_cidrs': ['2001:db8:a0b:12f0::/64']}
        body = self._build_body(lb_listener)
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertIn("Validation failure: CIDR 2001:db8:a0b:12f0::/64 IP "
                      "version incompatible with all VIPs ['198.0.2.5'] IP "
                      "version.",
                      response['faultstring'])

    def test_create_with_incompatible_allowed_cidrs_ipv4(self):
        lb_ipv6 = self.create_load_balancer(
            uuidutils.generate_uuid(),
            vip_address='2001:db9:a1b:13f0::1',
        )
        lb_id = lb_ipv6.get('loadbalancer').get('id')
        self.set_lb_status(lb_id)
        lb_listener = {
            'protocol': constants.PROTOCOL_TCP,
            'protocol_port': 80,
            'project_id': self.project_id,
            'loadbalancer_id': lb_id,
            'allowed_cidrs': ['10.0.1.0/24']}
        body = self._build_body(lb_listener)
        response = self.post(self.LISTENERS_PATH, body, status=400).json
        self.assertIn("Validation failure: CIDR 10.0.1.0/24 IP version "
                      "incompatible with all VIPs "
                      "['2001:db9:a1b:13f0::1'] IP version.",
                      response['faultstring'])

    def test_create_with_mixed_version_allowed_cidrs(self):
        lb_dualstack = self.create_load_balancer(
            uuidutils.generate_uuid(),
            additional_vips=[{'subnet_id': uuidutils.generate_uuid(),
                              'ip_address': '2001:db9:a1b:13f0::1',
                              }],
        )
        lb_id = lb_dualstack.get('loadbalancer').get('id')
        self.set_lb_status(lb_id)
        self._test_create_with_allowed_cidrs(['10.0.1.0/24',
                                              '2001:db9:a1b:13f0::/64'],
                                             lb_id)

    def test_create_with_duplicated_allowed_cidrs(self):
        allowed_cidrs = ['10.0.1.0/24', '10.0.2.0/24', '10.0.2.0/24']
        self.create_listener(constants.PROTOCOL_TCP, 80,
                             self.lb_id, allowed_cidrs=allowed_cidrs)

    # 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_versions(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        cert_id = uuidutils.generate_uuid()
        tls_versions = constants.TLS_VERSIONS_OWASP_SUITE_B
        listener = self.create_listener(constants.PROTOCOL_TERMINATED_HTTPS,
                                        80, self.lb_id,
                                        default_tls_container_ref=cert_id,
                                        tls_versions=tls_versions)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(tls_versions, get_listener['tls_versions'])

    # 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_versions_negative(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        cert_id = uuidutils.generate_uuid()
        req_dict = {'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                    'protocol_port': 80,
                    'loadbalancer_id': self.lb_id,
                    'default_tls_container_ref': cert_id,
                    'tls_versions': [lib_consts.TLS_VERSION_1_3, 'insecure']}
        res = self.post(self.LISTENERS_PATH, self._build_body(req_dict),
                        status=400)
        fault = res.json['faultstring']
        self.assertIn('Validation failure: Invalid TLS versions', fault)
        self.assert_correct_status(lb_id=self.lb_id)

        # 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_ciphers(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        cert_id = uuidutils.generate_uuid()
        tls_ciphers = constants.CIPHERS_OWASP_SUITE_B
        listener = self.create_listener(constants.PROTOCOL_TERMINATED_HTTPS,
                                        80, self.lb_id,
                                        default_tls_container_ref=cert_id,
                                        tls_ciphers=tls_ciphers)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(tls_ciphers, get_listener['tls_ciphers'])

    # 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_ciphers_negative(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        cert_id = uuidutils.generate_uuid()
        req_dict = {'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                    'protocol_port': 80,
                    'loadbalancer_id': self.lb_id,
                    'default_tls_container_ref': cert_id,
                    'tls_ciphers': ['cipher-insecure']}
        res = self.post(self.LISTENERS_PATH, self._build_body(req_dict),
                        status=400)
        fault = res.json['faultstring']
        self.assertIn('Invalid input for field/attribute tls_ciphers', fault)
        self.assert_correct_status(lb_id=self.lb_id)

    def _test_negative_create_with_headers(self, protocol):
        req_dict = {'name': 'listener1', 'default_pool_id': None,
                    'description': 'desc1',
                    'admin_state_up': False,
                    'protocol': protocol,
                    'protocol_port': 6666, 'connection_limit': 10,
                    'insert_headers': {
                        "X-Forwarded-Port": "true",
                        "X-Forwarded-For": "true"},
                    'loadbalancer_id': self.lb_id}
        res = self.post(self.LISTENERS_PATH, self._build_body(req_dict),
                        status=400)
        self.assertIn(protocol, res.json['faultstring'])
        self.assert_correct_status(lb_id=self.lb_id)

    def test_negative_create_HTTPS_with_headers(self):
        self._test_negative_create_with_headers(constants.PROTOCOL_HTTPS)

    def test_negative_create_PROXY_with_headers(self):
        self._test_negative_create_with_headers(constants.PROTOCOL_PROXY)

    def test_negative_create_TCP_with_headers(self):
        self._test_negative_create_with_headers(constants.PROTOCOL_TCP)

    def test_negative_create_UDP_with_headers(self):
        self._test_negative_create_with_headers(constants.PROTOCOL_UDP)

    def test_create_prometheus(self):
        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        self.conf.config(group='api_settings', allow_prometheus_listeners=True)
        listener = self.create_listener(lib_consts.PROTOCOL_PROMETHEUS,
                                        80, self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(lib_consts.PROTOCOL_PROMETHEUS,
                         get_listener['protocol'])

    def test_create_prometheus_disabled(self):
        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        self.conf.config(group='api_settings',
                         allow_prometheus_listeners=False)
        req_dict = {'name': 'create_listener_prometheus_disabled',
                    'protocol': lib_consts.PROTOCOL_PROMETHEUS,
                    'protocol_port': 6666,
                    'loadbalancer_id': self.lb_id}
        self.post(self.LISTENERS_PATH, self._build_body(req_dict), status=400)

    def test_update_allowed_cidrs(self):
        allowed_cidrs = ['10.0.1.0/24', '10.0.2.0/24']
        new_cidrs = ['10.0.1.0/24', '10.0.3.0/24']
        listener = self.create_listener(constants.PROTOCOL_TCP,
                                        80, self.lb_id,
                                        allowed_cidrs=allowed_cidrs)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        lb_listener = {'allowed_cidrs': new_cidrs}
        body = self._build_body(lb_listener)
        response = self.put(listener_path, body).json.get(self.root_tag)
        self.assertEqual(new_cidrs, response.get('allowed_cidrs'))

    def test_update_unset_allowed_cidrs(self):
        allowed_cidrs = ['10.0.1.0/24', '10.0.2.0/24']
        listener = self.create_listener(constants.PROTOCOL_TCP,
                                        80, self.lb_id,
                                        allowed_cidrs=allowed_cidrs)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        lb_listener = {'allowed_cidrs': None}
        body = self._build_body(lb_listener)
        api_listener = self.put(listener_path, body).json.get(self.root_tag)
        self.assertIsNone(api_listener.get('allowed_cidrs'))

    @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'}
        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'))

    @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)
        self.set_lb_status(lb_id=self.lb_id)
        # Set status to ACTIVE/ONLINE because set_lb_status did it in the db
        api_listener['provisioning_status'] = constants.ACTIVE
        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)
        mock_provider.side_effect = exceptions.ProviderDriverError(
            prov='bad_driver', user_msg='broken')
        self.delete(self.LISTENER_PATH.format(
            listener_id=api_listener.get('id')), status=500)

    # 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, **options):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)
        tls_uuid = uuidutils.generate_uuid()
        ca_tls_uuid = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=tls_uuid,
            default_pool_id=None, tags=['old_tag'],
            client_ca_tls_container_ref=ca_tls_uuid).get(self.root_tag)
        ori_listener = copy.deepcopy(listener)
        self.set_lb_status(self.lb_id)
        new_listener = {'name': 'listener2', 'admin_state_up': True,
                        'default_pool_id': self.pool_id,
                        'timeout_client_data': 1,
                        'timeout_member_connect': 2,
                        'timeout_member_data': 3,
                        'timeout_tcp_inspect': 4,
                        'tags': ['new_tag']}
        new_listener.update(options)
        body = self._build_body(new_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        api_listener = self.put(listener_path, body).json.get(self.root_tag)
        update_expect = {'provisioning_status': constants.PENDING_UPDATE,
                         'operating_status': constants.ONLINE}
        update_expect.update(new_listener)
        listener.update(update_expect)
        self.assertEqual(listener['created_at'], api_listener['created_at'])
        self.assertNotEqual(listener['updated_at'], api_listener['updated_at'])
        self.assertEqual(['new_tag'], api_listener['tags'])
        self.assertNotEqual(listener, api_listener)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id,
                                            api_listener['id'])
        return ori_listener, api_listener

    def test_update_with_bad_tls_ref(self):
        listener = self.create_listener(constants.PROTOCOL_TCP,
                                        443, self.lb_id)
        tls_uuid = uuidutils.generate_uuid()
        self.set_lb_status(self.lb_id)
        self.listener_repo.update(db_api.get_session(),
                                  listener['listener']['id'],
                                  tls_certificate_id=tls_uuid,
                                  protocol=constants.PROTOCOL_TERMINATED_HTTPS)

        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        update_data = {'name': 'listener2'}
        body = self._build_body(update_data)
        api_listener = self.put(listener_path, body).json.get(self.root_tag)
        response = self.get(self.listener_path.format(
            listener_id=listener['listener']['id']))
        api_listener = response.json.get(self.root_tag)
        self.assertEqual('listener2', api_listener['name'])

    # 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_versions(self, mock_cert_data):
        cert_id = uuidutils.generate_uuid()
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        tls_versions_orig = [lib_consts.TLS_VERSION_1_1]
        tls_versions = [lib_consts.TLS_VERSION_1_2, lib_consts.TLS_VERSION_1_3]
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            default_tls_container_ref=cert_id,
            tls_versions=tls_versions_orig)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(tls_versions_orig,
                         get_listener.get('tls_versions'))
        self.put(listener_path,
                 self._build_body({'tls_versions': tls_versions}))
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(tls_versions, get_listener.get('tls_versions'))

    # 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_versions_negative(self, mock_cert_data):
        cert_id = uuidutils.generate_uuid()
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        tls_versions_orig = [lib_consts.TLS_VERSION_1_1]
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            default_tls_container_ref=cert_id,
            tls_versions=tls_versions_orig)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(tls_versions_orig, get_listener.get('tls_versions'))

        req_dict = {'tls_versions': [lib_consts.TLS_VERSION_1_3, 'insecure']}
        res = self.put(listener_path, self._build_body(req_dict),
                       status=400)
        fault = res.json['faultstring']
        self.assertIn('Validation failure: Invalid TLS versions:', fault)
        self.assert_correct_status(lb_id=self.lb_id)

    # 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_ciphers_negative(self, mock_cert_data):
        cert_id = uuidutils.generate_uuid()
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        tls_ciphers_orig = constants.CIPHERS_OWASP_SUITE_B
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            default_tls_container_ref=cert_id,
            tls_ciphers=tls_ciphers_orig)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(tls_ciphers_orig, get_listener.get('tls_ciphers'))

        req_dict = {'tls_ciphers': ['cipher-insecure']}
        res = self.put(listener_path, self._build_body(req_dict),
                       status=400)
        fault = res.json['faultstring']
        self.assertIn('Invalid input for field/attribute tls_ciphers', fault)
        self.assert_correct_status(lb_id=self.lb_id)

    def test_negative_update_udp_case(self):
        api_listener = self.create_listener(constants.PROTOCOL_UDP, 6666,
                                            self.lb_id).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        sni1 = uuidutils.generate_uuid()
        sni2 = uuidutils.generate_uuid()
        new_listener = {'name': 'new-listener',
                        'admin_state_up': True,
                        'connection_limit': 10,
                        'default_tls_container_ref':
                            uuidutils.generate_uuid(),
                        'sni_container_refs': [sni1, sni2],
                        'insert_headers': {
                            "X-Forwarded-Port": "true",
                            "X-Forwarded-For": "true"}}
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        expect_error_msg = (
            "Validation failure: %s protocol listener does not support TLS or "
            "header insertion.") % constants.PROTOCOL_UDP
        res = self.put(listener_path, self._build_body(new_listener),
                       status=400, expect_errors=True)
        self.assertEqual(expect_error_msg, res.json['faultstring'])
        self.assert_correct_status(lb_id=self.lb_id)

    def test_update_bad_listener_id(self):
        self.put(self.listener_path.format(listener_id='SEAN-CONNERY'),
                 body={}, status=404)

    def test_update_with_bad_default_pool_id(self):
        bad_pool_uuid = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TCP, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_pool_id=self.pool_id)
        self.set_lb_status(self.lb_id)
        new_listener = {'name': 'listener2', 'admin_state_up': True,
                        'default_pool_id': bad_pool_uuid}
        body = self._build_body(new_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        self.put(listener_path, body, status=404)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.ACTIVE)
        self.assert_final_listener_statuses(self.lb_id,
                                            listener['listener']['id'])

    def test_update_with_certs_not_terminated_https(self):
        listener = self.create_listener(
            constants.PROTOCOL_TCP, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_pool_id=None,).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        lb_listener = {
            'default_tls_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        response = self.put(listener_path, body, status=400).json
        fault = response.get('faultstring')
        self.assertIn(
            'Certificate container references are not allowed on ', fault)
        self.assertIn(f'{constants.PROTOCOL_TCP} protocol listeners.', fault)

    def test_update_with_ca_cert(self):
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)

        optionals = {
            'client_ca_tls_container_ref': uuidutils.generate_uuid()
        }
        ori_listener, update_listener = self.test_update(**optionals)
        self.assertEqual(optionals['client_ca_tls_container_ref'],
                         update_listener.get('client_ca_tls_container_ref'))
        self.assertNotEqual(ori_listener['client_ca_tls_container_ref'],
                            optionals['client_ca_tls_container_ref'])

    def test_update_with_only_client_auth_option(self):
        optionals = {
            'client_authentication': constants.CLIENT_AUTH_OPTIONAL
        }
        ori_listener, update_listener = self.test_update(**optionals)
        self.assertEqual(optionals['client_authentication'],
                         update_listener.get('client_authentication'))
        self.assertNotEqual(ori_listener['client_authentication'],
                            optionals['client_authentication'])

    def test_update_with_crl(self):
        # Load up sample certs to test the validation
        self.cert_manager_mock().get_secret.side_effect = [
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]

        optionals = {
            'client_crl_container_ref': uuidutils.generate_uuid()
        }
        ori_listener, update_listener = self.test_update(**optionals)
        self.assertEqual(optionals['client_crl_container_ref'],
                         update_listener.get('client_crl_container_ref'))
        self.assertNotEqual(ori_listener['client_crl_container_ref'],
                            optionals['client_crl_container_ref'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_from_nonexist_ca_cert_to_new_ca_cert(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)
        tls_uuid = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=tls_uuid,
            default_pool_id=None).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        ca_tls_uuid = uuidutils.generate_uuid()
        new_listener = {
            'client_ca_tls_container_ref': ca_tls_uuid}
        body = self._build_body(new_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        api_listener = self.put(listener_path, body).json.get(self.root_tag)
        update_expect = {'provisioning_status': constants.PENDING_UPDATE,
                         'operating_status': constants.ONLINE}
        update_expect.update(new_listener)
        listener.update(update_expect)
        self.assertEqual(ca_tls_uuid,
                         api_listener['client_ca_tls_container_ref'])
        self.assertEqual(constants.CLIENT_AUTH_NONE,
                         api_listener['client_authentication'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_with_ca_cert_missing(self, mock_cert_data):
        # update a listener, no ca cert exist
        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_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=tls_uuid,
            default_pool_id=None).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        for opt in [{'client_authentication': constants.CLIENT_AUTH_OPTIONAL,
                     'client_crl_container_ref': uuidutils.generate_uuid()},
                    {'client_authentication': constants.CLIENT_AUTH_MANDATORY,
                     'client_crl_container_ref': uuidutils.generate_uuid()}]:
            body = self._build_body(opt)
            listener_path = self.LISTENER_PATH.format(
                listener_id=listener['id'])
            response = self.put(listener_path, body, status=400).json
            self.assertEqual(
                "Validation failure: Client authentication setting %s "
                "requires a client CA container reference." %
                opt['client_authentication'], response['faultstring'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_with_crl_but_ca_cert_missing(self, mock_cert_data):
        # update a listener, no ca cert exist
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1,
                                       'client_ca_cert': None}
        tls_uuid = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=tls_uuid,
            default_pool_id=None).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        body = self._build_body(
            {'client_crl_container_ref': uuidutils.generate_uuid()})
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        response = self.put(listener_path, body, status=400).json
        self.assertEqual(
            "Validation failure: A client authentication CA reference is "
            "required to specify a client authentication revocation list.",
            response['faultstring'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_unset_ca_cert(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)
        tls_uuid = uuidutils.generate_uuid()
        ca_tls_uuid = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=tls_uuid,
            default_pool_id=None,
            client_ca_tls_container_ref=ca_tls_uuid).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        lb_listener = {'client_ca_tls_container_ref': None}
        body = self._build_body(lb_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        api_listener = self.put(listener_path, body).json.get(self.root_tag)
        self.assertIsNone(api_listener.get('client_ca_tls_container_ref'))
        self.assertIsNone(api_listener.get('client_auth_option'))
        self.assertIsNone(api_listener.get('client_crl_container_ref'))

    @mock.patch(
        'octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_unset_crl(self, mock_cert_data):
        # Load up sample certs to test the validation
        self.cert_manager_mock().get_secret.side_effect = [
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]

        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=uuidutils.generate_uuid(),
            default_pool_id=None,
            client_ca_tls_container_ref=uuidutils.generate_uuid(),
            client_crl_container_ref=uuidutils.generate_uuid(),
            client_authentication=constants.CLIENT_AUTH_MANDATORY).get(
            self.root_tag)
        self.set_lb_status(self.lb_id)
        lb_listener = {'client_crl_container_ref': None}
        body = self._build_body(lb_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        api_listener = self.put(listener_path, body).json.get(self.root_tag)
        self.assertEqual(listener.get('client_ca_tls_container_ref'),
                         api_listener.get('client_ca_tls_container_ref'))
        self.assertEqual(listener.get('client_authentication'),
                         api_listener.get('client_authentication'))
        self.assertIsNone(api_listener.get('client_crl_container_ref'))

    # 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_unset_defaults(self, mock_cert_data):
        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        self.conf.config(group='haproxy_amphora', timeout_client_data=20)
        self.conf.config(group='haproxy_amphora', timeout_member_connect=21)
        self.conf.config(group='haproxy_amphora', timeout_member_data=22)
        self.conf.config(group='haproxy_amphora', timeout_tcp_inspect=23)
        self.conf.config(group='api_settings',
                         default_listener_tls_versions=(
                             constants.TLS_VERSIONS_OWASP_SUITE_B))
        self.conf.config(group='api_settings',
                         default_listener_ciphers=(
                             constants.CIPHERS_OWASP_SUITE_B))
        self.conf.config(group='api_settings',
                         default_listener_alpn_protocols=['http/1.1'])

        self.cert_manager_mock().get_secret.side_effect = [
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)
        tls_uuid = uuidutils.generate_uuid()
        ca_tls_uuid = uuidutils.generate_uuid()
        crl_tls_uuid = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=tls_uuid,
            default_pool_id=self.pool_id, tags=['old_tag'],
            insert_headers={'X-Forwarded-For': 'true'},
            timeout_client_data=1, timeout_member_connect=2,
            timeout_member_data=3, timeout_tcp_inspect=4,
            client_authentication=constants.CLIENT_AUTH_OPTIONAL,
            client_crl_container_ref=crl_tls_uuid,
            client_ca_tls_container_ref=ca_tls_uuid,
            tls_versions=[lib_consts.TLS_VERSION_1_3],
            tls_ciphers='TLS_AES_256_GCM_SHA384',
            alpn_protocols=['http/1.0'],
            hsts_max_age=20, hsts_include_subdomains=True,
            hsts_preload=True,
        ).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        unset_params = {
            'name': None, 'description': None, 'connection_limit': None,
            'default_tls_container_ref': None, 'sni_container_refs': None,
            'insert_headers': None, 'timeout_client_data': None,
            'timeout_member_connect': None, 'timeout_member_data': None,
            'timeout_tcp_inspect': None, 'client_ca_tls_container_ref': None,
            'client_authentication': None, 'default_pool_id': None,
            'client_crl_container_ref': None, 'tls_versions': None,
            'tls_ciphers': None, 'alpn_protocols': None,
            'hsts_max_age': None, 'hsts_include_subdomains': None,
            'hsts_preload': None,
        }
        body = self._build_body(unset_params)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        api_listener = self.put(listener_path, body).json.get(self.root_tag)

        self.assertEqual('', api_listener['name'])
        self.assertEqual('', api_listener['description'])
        self.assertEqual(constants.DEFAULT_CONNECTION_LIMIT,
                         api_listener['connection_limit'])
        self.assertIsNone(api_listener['default_tls_container_ref'])
        self.assertEqual([], api_listener['sni_container_refs'])
        self.assertEqual({}, api_listener['insert_headers'])
        self.assertEqual(20, api_listener['timeout_client_data'])
        self.assertEqual(21, api_listener['timeout_member_connect'])
        self.assertEqual(22, api_listener['timeout_member_data'])
        self.assertEqual(23, api_listener['timeout_tcp_inspect'])
        self.assertIsNone(api_listener['client_ca_tls_container_ref'])
        self.assertIsNone(api_listener['client_crl_container_ref'])
        self.assertEqual(constants.CLIENT_AUTH_NONE,
                         api_listener['client_authentication'])
        self.assertIsNone(api_listener['default_pool_id'])
        self.assertEqual(constants.TLS_VERSIONS_OWASP_SUITE_B,
                         api_listener['tls_versions'])
        self.assertEqual(constants.CIPHERS_OWASP_SUITE_B,
                         api_listener['tls_ciphers'])
        self.assertEqual(['http/1.1'], api_listener['alpn_protocols'])
        self.assertIsNone(api_listener['hsts_max_age'])
        self.assertFalse(api_listener['hsts_include_subdomains'])
        self.assertFalse(api_listener['hsts_preload'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_with_bad_ca_cert(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        self.cert_manager_mock().get_secret.return_value = (
            sample_certs.X509_CA_CERT)

        tls_uuid = uuidutils.generate_uuid()
        ca_tls_uuid = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=tls_uuid,
            default_pool_id=None,
            client_ca_tls_container_ref=ca_tls_uuid).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        self.cert_manager_mock().get_secret.side_effect = Exception(
            'bad ca cert')
        self.cert_manager_mock().get_secret.side_effect = Exception(
            'bad secret')
        lb_listener = {
            'client_ca_tls_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        response = self.put(listener_path, body, status=400).json
        self.assertIn(lb_listener['client_ca_tls_container_ref'],
                      response['faultstring'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_with_unreachable_crl(self, mock_cert_data):
        # Load up sample certs to test the validation
        tls_cert_mock = mock.MagicMock()
        tls_cert_mock.get_certificate.return_value = sample_certs.X509_CA_CERT
        self.cert_manager_mock().get_cert.return_value = tls_cert_mock
        self.cert_manager_mock().get_secret.side_effect = [
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, Exception('bad CRL ref')]

        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=uuidutils.generate_uuid(),
            default_pool_id=None,
            client_ca_tls_container_ref=uuidutils.generate_uuid(),
            client_crl_container_ref=uuidutils.generate_uuid()).get(
            self.root_tag)
        self.set_lb_status(self.lb_id)
        lb_listener = {
            'client_crl_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        response = self.put(listener_path, body, status=400).json
        self.assertIn(lb_listener['client_crl_container_ref'],
                      response['faultstring'])

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_with_bad_crl(self, mock_cert_data):
        # Load up sample certs to test the validation
        tls_cert_mock = mock.MagicMock()
        tls_cert_mock.get_certificate.return_value = sample_certs.X509_CA_CERT
        self.cert_manager_mock().get_cert.return_value = tls_cert_mock
        self.cert_manager_mock().get_secret.side_effect = [
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
            sample_certs.X509_CA_CERT, 'bad CRL']

        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_tls_container_ref=uuidutils.generate_uuid(),
            default_pool_id=None,
            client_ca_tls_container_ref=uuidutils.generate_uuid(),
            client_crl_container_ref=uuidutils.generate_uuid()).get(
            self.root_tag)
        self.set_lb_status(self.lb_id)
        lb_listener = {
            'client_crl_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])
        response = self.put(listener_path, body, status=400).json
        self.assertIn("The client authentication certificate revocation list "
                      "is invalid. It must be a valid x509 PEM format "
                      "certificate revocation list.",
                      response['faultstring'])

    def test_update_authorized(self):
        listener = self.create_listener(
            constants.PROTOCOL_TCP, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_pool_id=None).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        new_listener = {'name': 'listener2', 'admin_state_up': True,
                        'default_pool_id': self.pool_id}
        body = self._build_body(new_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.TESTING)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               self.project_id):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['load-balancer_member', 'member'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': self.project_id}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):
                api_listener = self.put(listener_path, body)
                api_listener = api_listener.json.get(self.root_tag)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)

        update_expect = {'name': 'listener2', 'admin_state_up': True,
                         'default_pool_id': self.pool_id,
                         'provisioning_status': constants.PENDING_UPDATE,
                         'operating_status': constants.ONLINE}
        listener.update(update_expect)
        self.assertEqual(listener['created_at'], api_listener['created_at'])
        self.assertNotEqual(listener['updated_at'], api_listener['updated_at'])
        self.assertNotEqual(listener, api_listener)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id,
                                            api_listener['id'])

    def test_update_not_authorized(self):
        listener = self.create_listener(
            constants.PROTOCOL_TCP, 80, self.lb_id,
            name='listener1', description='desc1',
            admin_state_up=False, connection_limit=10,
            default_pool_id=None).get(self.root_tag)
        self.set_lb_status(self.lb_id)
        new_listener = {'name': 'listener2', 'admin_state_up': True,
                        'default_pool_id': self.pool_id}
        body = self._build_body(new_listener)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['id'])

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.TESTING)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               uuidutils.generate_uuid()):
            api_listener = self.put(listener_path, body, status=403)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)
        self.assertEqual(self.NOT_AUTHORIZED_BODY, api_listener.json)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.ACTIVE)

    def test_create_listeners_same_port(self):
        listener1 = self.create_listener(constants.PROTOCOL_TCP, 80,
                                         self.lb_id)
        self.set_lb_status(self.lb_id)
        listener2_post = {'protocol': listener1['listener']['protocol'],
                          '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_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)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        self.delete(listener_path)
        response = self.get(listener_path)
        api_listener = response.json['listener']
        expected = {'name': None, 'default_pool_id': None,
                    'description': None, 'admin_state_up': True,
                    'operating_status': constants.ONLINE,
                    'provisioning_status': constants.PENDING_DELETE,
                    'connection_limit': None}
        listener['listener'].update(expected)

        self.assertIsNone(listener['listener'].pop('updated_at'))
        self.assertIsNotNone(api_listener.pop('updated_at'))
        self.assertNotEqual(listener, api_listener)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id, api_listener['id'],
                                            delete=True)

    # Problems with TLS certs should not block a delete
    def test_delete_with_bad_tls_ref(self):
        listener = self.create_listener(constants.PROTOCOL_TCP,
                                        443, self.lb_id)
        tls_uuid = uuidutils.generate_uuid()
        self.set_lb_status(self.lb_id)
        self.listener_repo.update(db_api.get_session(),
                                  listener['listener']['id'],
                                  tls_certificate_id=tls_uuid,
                                  protocol=constants.PROTOCOL_TERMINATED_HTTPS)

        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        self.delete(listener_path)
        response = self.get(listener_path)
        api_listener = response.json['listener']
        expected = {'name': None, 'default_pool_id': None,
                    'description': None, 'admin_state_up': True,
                    'operating_status': constants.ONLINE,
                    'provisioning_status': constants.PENDING_DELETE,
                    'connection_limit': None}
        listener['listener'].update(expected)

        self.assertIsNone(listener['listener'].pop('updated_at'))
        self.assertIsNotNone(api_listener.pop('updated_at'))
        self.assertNotEqual(listener, api_listener)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id, api_listener['id'],
                                            delete=True)

    def test_delete_authorized(self):
        listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
                                        self.lb_id)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings',
                         auth_strategy=constants.TESTING)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               self.project_id):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['load-balancer_member', 'member'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': self.project_id}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):

                self.delete(listener_path)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)

        response = self.get(listener_path)
        api_listener = response.json['listener']
        expected = {'name': None, 'default_pool_id': None,
                    'description': None, 'admin_state_up': True,
                    'operating_status': constants.ONLINE,
                    'provisioning_status': constants.PENDING_DELETE,
                    'connection_limit': None}
        listener['listener'].update(expected)

        self.assertIsNone(listener['listener'].pop('updated_at'))
        self.assertIsNotNone(api_listener.pop('updated_at'))
        self.assertNotEqual(listener, api_listener)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.PENDING_UPDATE)
        self.assert_final_listener_statuses(self.lb_id, api_listener['id'],
                                            delete=True)

    def test_delete_not_authorized(self):
        listener = self.create_listener(constants.PROTOCOL_HTTP, 80,
                                        self.lb_id)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])

        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               uuidutils.generate_uuid()):
            self.delete(listener_path, status=403)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)
        self.assert_correct_lb_status(self.lb_id, constants.ONLINE,
                                      constants.ACTIVE)

    def test_delete_bad_listener_id(self):
        listener_path = self.LISTENER_PATH.format(listener_id='SEAN-CONNERY')
        self.delete(listener_path, status=404)

    def test_create_listener_bad_protocol(self):
        lb_listener = {'protocol': 'SEAN_CONNERY',
                       'protocol_port': 80}
        self.post(self.LISTENERS_PATH, lb_listener, status=400)

    def test_create_listener_proxy_protocol(self):
        lb_listener = {'protocol': 'PROXY',
                       'protocol_port': 80}
        self.post(self.LISTENERS_PATH, lb_listener, status=400)

    def test_update_listener_bad_protocol(self):
        listener = self.create_listener(constants.PROTOCOL_TCP, 80, self.lb_id)
        self.set_lb_status(self.lb_id)
        new_listener = {'protocol': 'SEAN_CONNERY',
                        'protocol_port': 80}
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener'].get('id'))
        self.put(listener_path, new_listener, status=400)

    def test_update_pending_create(self):
        lb = self.create_load_balancer(uuidutils.generate_uuid())
        optionals = {'name': 'lb1', 'description': 'desc1',
                     'admin_state_up': False}
        lb.update(optionals)

        lb_listener = {'name': 'listener1', 'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'loadbalancer_id': lb['loadbalancer']['id']}
        body = self._build_body(lb_listener)
        self.post(self.LISTENERS_PATH, body, status=409)

    def test_delete_pending_update(self):
        lb = self.create_load_balancer(uuidutils.generate_uuid())
        optionals = {'name': 'lb1', 'description': 'desc1',
                     'admin_state_up': False}
        lb.update(optionals)

        self.set_lb_status(lb['loadbalancer']['id'])
        lb_listener = {'name': 'listener1', 'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'loadbalancer_id': lb['loadbalancer']['id']}
        body = self._build_body(lb_listener)
        api_listener = self.post(
            self.LISTENERS_PATH, body).json['listener']
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        self.delete(listener_path, status=409)

    def test_update_empty_body(self):
        listener = self.create_listener(constants.PROTOCOL_TCP, 80, self.lb_id)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener'].get('id'))
        self.put(listener_path, {}, status=400)

    # 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()
        tls_ref2 = uuidutils.generate_uuid()
        lb_listener = {'name': 'listener1', 'default_pool_id': None,
                       'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                       'protocol_port': 80,
                       'sni_container_refs': [sni1, sni2],
                       'default_tls_container_ref': tls_ref,
                       'loadbalancer_id': self.lb_id}

        body = self._build_body(lb_listener)
        api_listener = self.post(
            self.LISTENERS_PATH, body).json['listener']
        self.set_lb_status(self.lb_id)
        lb_listener_put = {
            'default_tls_container_ref': tls_ref2,
            'sni_container_refs': [sni1, sni2]
        }
        body = self._build_body(lb_listener_put)
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        self.cert_manager_mock().get_cert.side_effect = [
            Exception("bad cert"), None, Exception("bad cert")]
        self.cert_manager_mock().get_secret.side_effect = [
            Exception("bad secret"), Exception("bad secret")]
        response = self.put(listener_path, body, status=400).json
        self.assertIn(tls_ref2, response['faultstring'])
        self.assertIn(sni1, response['faultstring'])
        self.assertNotIn(sni2, response['faultstring'])
        self.assertNotIn(tls_ref, response['faultstring'])

    def test_update_pending_update(self):
        lb = self.create_load_balancer(uuidutils.generate_uuid())
        optionals = {'name': 'lb1', 'description': 'desc1',
                     'admin_state_up': False}
        lb.update(optionals)
        self.set_lb_status(lb['loadbalancer']['id'])
        lb_listener = {'name': 'listener1', 'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'loadbalancer_id': lb['loadbalancer']['id']}
        body = self._build_body(lb_listener)
        api_listener = self.post(
            self.LISTENERS_PATH, body).json['listener']
        self.set_lb_status(lb['loadbalancer']['id'])
        self.put(self.LB_PATH.format(lb_id=lb['loadbalancer']['id']),
                 {'loadbalancer': {'name': 'hi'}})
        lb_listener_put = {'name': 'listener1_updated'}
        body = self._build_body(lb_listener_put)
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        self.put(listener_path, body, status=409)

    def test_update_pending_delete(self):
        lb = self.create_load_balancer(uuidutils.generate_uuid(),
                                       name='lb1', description='desc1',
                                       admin_state_up=False)
        lb_id = lb['loadbalancer'].get('id')
        self.set_lb_status(lb_id)
        lb_listener = {'name': 'listener1', 'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'loadbalancer_id': lb_id}
        body = self._build_body(lb_listener)
        api_listener = self.post(
            self.LISTENERS_PATH, body).json.get(self.root_tag)
        self.set_lb_status(lb_id)
        self.delete(self.LB_PATH.format(lb_id=lb_id),
                    params={'cascade': "true"})
        lb_listener_put = {'name': 'listener1_updated'}
        body = self._build_body(lb_listener_put)
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        self.put(listener_path, body, status=409)

    def test_update_deleted(self):
        lb = self.create_load_balancer(uuidutils.generate_uuid(),
                                       name='lb1', description='desc1',
                                       admin_state_up=False)
        lb_id = lb['loadbalancer'].get('id')
        self.set_lb_status(lb_id)
        lb_listener = {'name': 'listener1', 'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'loadbalancer_id': lb_id}
        body = self._build_body(lb_listener)
        api_listener = self.post(
            self.LISTENERS_PATH, body).json.get(self.root_tag)
        # This updates the child objects
        self.set_lb_status(lb_id, status=constants.DELETED)
        lb_listener_put = {'name': 'listener1_updated'}
        body = self._build_body(lb_listener_put)
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        self.put(listener_path, body, status=404)

    def test_delete_pending_delete(self):
        lb = self.create_load_balancer(uuidutils.generate_uuid(),
                                       name='lb1', description='desc1',
                                       admin_state_up=False)
        lb_id = lb['loadbalancer'].get('id')
        self.set_lb_status(lb_id)
        lb_listener = {'name': 'listener1', 'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'loadbalancer_id': lb_id}
        body = self._build_body(lb_listener)
        api_listener = self.post(
            self.LISTENERS_PATH, body).json.get(self.root_tag)
        self.set_lb_status(lb_id)
        self.delete(self.LB_PATH.format(lb_id=lb_id),
                    params={'cascade': "true"})
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        self.delete(listener_path, status=409)

    def test_delete_already_deleted(self):
        lb = self.create_load_balancer(uuidutils.generate_uuid(),
                                       name='lb1', description='desc1',
                                       admin_state_up=False)
        lb_id = lb['loadbalancer'].get('id')
        self.set_lb_status(lb_id)
        lb_listener = {'name': 'listener1', 'description': 'desc1',
                       'admin_state_up': False,
                       'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80, 'connection_limit': 10,
                       'loadbalancer_id': lb_id}
        body = self._build_body(lb_listener)
        api_listener = self.post(
            self.LISTENERS_PATH, body).json.get(self.root_tag)
        # This updates the child objects
        self.set_lb_status(lb_id, status=constants.DELETED)
        listener_path = self.LISTENER_PATH.format(
            listener_id=api_listener['id'])
        self.delete(listener_path, status=404)

    # 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,
                                        default_tls_container_ref=cert_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(cert_id, get_listener['default_tls_container_ref'])

    # 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):
        cert_id_orig = uuidutils.generate_uuid()
        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,
            default_tls_container_ref=cert_id_orig)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(cert_id_orig,
                         get_listener.get('default_tls_container_ref'))
        self.put(listener_path,
                 self._build_body({'default_tls_container_ref': cert_id}))
        get_listener = self.get(listener_path).json['listener']
        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',
                         allow_tls_terminated_listeners=False)
        cert_id = uuidutils.generate_uuid()
        listener = self.create_listener(constants.PROTOCOL_TERMINATED_HTTPS,
                                        80, self.lb_id,
                                        default_tls_container_ref=cert_id,
                                        status=400)
        self.assertIn(
            'The selected protocol is not allowed in this deployment: {}'
            .format(constants.PROTOCOL_TERMINATED_HTTPS),
            listener.get('faultstring'))

    # 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_alpn(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        cert_id = uuidutils.generate_uuid()
        alpn_protocols = [lib_consts.ALPN_PROTOCOL_HTTP_2,
                          lib_consts.ALPN_PROTOCOL_HTTP_1_1]
        listener = self.create_listener(constants.PROTOCOL_TERMINATED_HTTPS,
                                        80, self.lb_id,
                                        default_tls_container_ref=cert_id,
                                        alpn_protocols=['h2', 'http/1.1'])
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(alpn_protocols, get_listener['alpn_protocols'])

    # 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_alpn_negative(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        cert_id = uuidutils.generate_uuid()
        req_dict = {'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                    'protocol_port': 80,
                    'loadbalancer_id': self.lb_id,
                    'default_tls_container_ref': cert_id,
                    'alpn_protocols': [lib_consts.ALPN_PROTOCOL_HTTP_1_1,
                                       'invalid-proto']}
        res = self.post(self.LISTENERS_PATH, self._build_body(req_dict),
                        status=400)
        fault = res.json['faultstring']
        self.assertIn(
            'Invalid input for field/attribute alpn_protocols', fault)
        self.assertIn('Value should be a valid ALPN protocol ID', fault)
        self.assert_correct_status(lb_id=self.lb_id)

    # 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_alpn(self, mock_cert_data):
        cert_id = uuidutils.generate_uuid()
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        alpn_protocols_orig = [lib_consts.ALPN_PROTOCOL_HTTP_1_0]
        alpn_protocols = [lib_consts.ALPN_PROTOCOL_HTTP_2,
                          lib_consts.ALPN_PROTOCOL_HTTP_1_1]
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            default_tls_container_ref=cert_id,
            alpn_protocols=alpn_protocols_orig)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(alpn_protocols_orig,
                         get_listener.get('alpn_protocols'))
        self.put(listener_path,
                 self._build_body({'alpn_protocols': alpn_protocols}))
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(alpn_protocols, get_listener.get('alpn_protocols'))

    # 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_alpn_negative(self, mock_cert_data):
        cert_id = uuidutils.generate_uuid()
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        alpn_protocols_orig = [lib_consts.ALPN_PROTOCOL_HTTP_1_0]
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 80, self.lb_id,
            default_tls_container_ref=cert_id,
            alpn_protocols=alpn_protocols_orig)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual(alpn_protocols_orig,
                         get_listener.get('alpn_protocols'))

        req_dict = {'alpn_protocols': [
            lib_consts.ALPN_PROTOCOL_HTTP_1_1, 'invalid-proto']}
        res = self.put(self.LISTENERS_PATH, self._build_body(req_dict),
                       status=400)
        fault = res.json['faultstring']
        self.assertIn(
            'Invalid input for field/attribute alpn_protocols', fault)
        self.assertIn('Value should be a valid ALPN protocol ID', fault)
        self.assert_correct_status(lb_id=self.lb_id)

    # 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_TERMINATED_HTTPS,
                                        80, self.lb_id,
                                        sni_container_refs=[sni_id1, sni_id2])
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertCountEqual([sni_id1, sni_id2],
                              get_listener['sni_container_refs'])

    # 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_tls_terminated_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_TERMINATED_HTTPS, 80, self.lb_id,
            default_tls_container_ref=uuidutils.generate_uuid())
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual([], get_listener.get('sni_container_refs'))
        self.put(listener_path,
                 self._build_body({'sni_container_refs': [sni_id1, sni_id2]}))
        get_listener = self.get(listener_path).json['listener']
        self.assertCountEqual([sni_id1, sni_id2],
                              get_listener.get('sni_container_refs'))

    # 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_non_tls_terminated_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,
                                        self.lb_id)
        self.set_lb_status(self.lb_id)
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener']['id'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual([], get_listener.get('sni_container_refs'))
        body = self._build_body({'sni_container_refs': [sni_id1, sni_id2]})
        response = self.put(listener_path, body, status=400).json
        self.assertEqual(
            "Validation failure: Certificate container references are not "
            "allowed on HTTP protocol listeners.",
            response['faultstring'])
        get_listener = self.get(listener_path).json['listener']
        self.assertEqual([], get_listener.get('sni_container_refs'))

    # 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_valid_insert_headers(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        lb_listener = {'protocol': 'HTTP',
                       'protocol_port': 80,
                       'loadbalancer_id': self.lb_id,
                       'insert_headers': {'X-Forwarded-For': 'true',
                                          'X-Forwarded-Port': 'true',
                                          'X-Forwarded-Proto': 'true'}}
        body = self._build_body(lb_listener)
        self.post(self.LISTENERS_PATH, body, status=201)

        # test client certificate http headers
        self.set_lb_status(self.lb_id)
        header = {}
        for name in constants.SUPPORTED_SSL_HEADERS:
            header[name] = 'true'
        lb_listener = {'protocol': constants.PROTOCOL_TERMINATED_HTTPS,
                       'protocol_port': 1801,
                       'loadbalancer_id': self.lb_id,
                       'insert_headers': header,
                       'default_tls_container_ref': uuidutils.generate_uuid()}
        body = self._build_body(lb_listener)
        self.post(self.LISTENERS_PATH, body, status=201)

    def test_create_with_bad_insert_headers(self):
        lb_listener = {'protocol': constants.PROTOCOL_HTTP,
                       'protocol_port': 80,
                       'loadbalancer_id': self.lb_id,
                       'insert_headers': {'X-Forwarded-Four': 'true'}}
        body = self._build_body(lb_listener)
        self.post(self.LISTENERS_PATH, body, status=400)

        # test client certificate http headers
        for name in constants.SUPPORTED_SSL_HEADERS:
            header = {}
            header[name] = 'true'
            lb_listener['insert_headers'] = header
            body = self._build_body(lb_listener)
            listener = self.post(self.LISTENERS_PATH, body, status=400).json
            self.assertIn('{} is not a valid option for {}'.format(
                [name],
                f'{constants.PROTOCOL_HTTP} protocol listener.'),
                listener.get('faultstring'))

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_update_with_valid_insert_headers(self, mock_cert_data):
        cert1 = data_models.TLSContainer(certificate='cert 1')
        mock_cert_data.return_value = {'tls_cert': cert1}
        listener = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id)
        self.set_lb_status(self.lb_id)
        new_listener = self._build_body(
            {'insert_headers': {'X-Forwarded-For': 'true',
                                'X-Forwarded-Port': 'true',
                                'X-Forwarded-Proto': 'true'}})
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener'].get('id'))
        update_listener = self.put(
            listener_path, new_listener, status=200).json
        self.assertNotEqual(
            listener[self.root_tag]['insert_headers'],
            update_listener[self.root_tag]['insert_headers'])

        self.set_lb_status(self.lb_id)
        # test client certificate http headers
        cert1_id = uuidutils.generate_uuid()
        listener = self.create_listener(
            constants.PROTOCOL_TERMINATED_HTTPS, 443, self.lb_id,
            default_tls_container_ref=cert1_id)
        self.set_lb_status(self.lb_id)
        header = {}
        for name in constants.SUPPORTED_SSL_HEADERS:
            header[name] = 'true'
        new_listener[self.root_tag]['insert_headers'] = header
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener'].get('id'))
        update_listener = self.put(
            listener_path, new_listener, status=200).json
        self.assertNotEqual(
            listener[self.root_tag]['insert_headers'],
            update_listener[self.root_tag]['insert_headers'])

    def test_update_with_bad_insert_headers(self):
        listener = self.create_listener(
            constants.PROTOCOL_HTTP, 80, self.lb_id)
        self.set_lb_status(self.lb_id)
        new_listener = self._build_body(
            {'insert_headers': {'X-Bad-Header': 'true'}})
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener'].get('id'))
        update_listener = self.put(
            listener_path, new_listener, status=400).json
        self.assertIn('[\'X-Bad-Header\'] is not a valid option for '
                      'insert_headers',
                      update_listener.get('faultstring'))

        # test client certificate http headers
        header = {}
        for name in constants.SUPPORTED_SSL_HEADERS:
            header[name] = 'true'
        new_listener[self.root_tag]['insert_headers'] = header
        # as the order of output faultstring is not stable, so we just check
        # the status.
        self.put(listener_path, new_listener, status=400).json

    def _test_update_protocol_insert_headers_mismatch(self, protocol):
        listener = self.create_listener(
            protocol, 80, self.lb_id)
        self.set_lb_status(self.lb_id)
        new_listener = self._build_body(
            {'insert_headers': {'X-Forwarded-Port': 'true'}})
        listener_path = self.LISTENER_PATH.format(
            listener_id=listener['listener'].get('id'))
        update_listener = self.put(
            listener_path, new_listener, status=400).json
        self.assertIn(protocol, update_listener['faultstring'])
        self.assert_correct_status(lb_id=self.lb_id)

    def test_update_protocol_HTTPS_insert_headers(self):
        self._test_update_protocol_insert_headers_mismatch(
            constants.PROTOCOL_HTTPS)

    def test_update_protocol_TCP_insert_headers(self):
        self._test_update_protocol_insert_headers_mismatch(
            constants.PROTOCOL_TCP)

    def test_update_protocol_UDP_insert_headers(self):
        self._test_update_protocol_insert_headers_mismatch(
            constants.PROTOCOL_UDP)

    def _getStats(self, listener_id):
        res = self.get(self.LISTENER_PATH.format(
            listener_id=listener_id + "/stats"))
        return res.json.get('stats')

    def test_statistics(self):
        lb = self.create_load_balancer(
            uuidutils.generate_uuid()).get('loadbalancer')
        self.set_lb_status(lb['id'])
        li = self.create_listener(
            constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener')
        amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id'])
        ls = self.create_listener_stats_dynamic(
            listener_id=li.get('id'),
            amphora_id=amphora.id,
            bytes_in=random.randint(1, 9),
            bytes_out=random.randint(1, 9),
            total_connections=random.randint(1, 9),
            request_errors=random.randint(1, 9))
        self.session.commit()

        response = self._getStats(li['id'])
        self.assertEqual(ls['bytes_in'], response['bytes_in'])
        self.assertEqual(ls['bytes_out'], response['bytes_out'])
        self.assertEqual(ls['total_connections'],
                         response['total_connections'])
        self.assertEqual(ls['active_connections'],
                         response['active_connections'])
        self.assertEqual(ls['request_errors'],
                         response['request_errors'])

    def test_statistics_authorized(self):
        project_id = uuidutils.generate_uuid()
        lb = self.create_load_balancer(
            uuidutils.generate_uuid(),
            project_id=project_id).get('loadbalancer')
        self.set_lb_status(lb['id'])
        li = self.create_listener(
            constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener')
        amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id'])
        ls = self.create_listener_stats_dynamic(
            listener_id=li.get('id'),
            amphora_id=amphora.id,
            bytes_in=random.randint(1, 9),
            bytes_out=random.randint(1, 9),
            total_connections=random.randint(1, 9),
            request_errors=random.randint(1, 9))
        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
        self.session.commit()

        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               project_id):
            override_credentials = {
                'service_user_id': None,
                'user_domain_id': None,
                'is_admin_project': True,
                'service_project_domain_id': None,
                'service_project_id': None,
                'roles': ['load-balancer_member', 'member'],
                'user_id': None,
                'is_admin': False,
                'service_user_domain_id': None,
                'project_domain_id': None,
                'service_roles': [],
                'project_id': project_id}
            with mock.patch(
                    "oslo_context.context.RequestContext.to_policy_values",
                    return_value=override_credentials):
                response = self._getStats(li['id'])
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)

        self.assertEqual(ls['bytes_in'], response['bytes_in'])
        self.assertEqual(ls['bytes_out'], response['bytes_out'])
        self.assertEqual(ls['total_connections'],
                         response['total_connections'])
        self.assertEqual(ls['active_connections'],
                         response['active_connections'])
        self.assertEqual(ls['request_errors'],
                         response['request_errors'])

    def test_statistics_not_authorized(self):
        lb = self.create_load_balancer(
            uuidutils.generate_uuid()).get('loadbalancer')
        self.set_lb_status(lb['id'])
        li = self.create_listener(
            constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener')
        amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id'])
        self.create_listener_stats_dynamic(
            listener_id=li.get('id'),
            amphora_id=amphora.id,
            bytes_in=random.randint(1, 9),
            bytes_out=random.randint(1, 9),
            total_connections=random.randint(1, 9),
            request_errors=random.randint(1, 9))
        self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
        auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
        self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
        with mock.patch.object(octavia.common.context.RequestContext,
                               'project_id',
                               uuidutils.generate_uuid()):
            res = self.get(self.LISTENER_PATH.format(
                listener_id=li['id'] + "/stats"), status=403)
        self.conf.config(group='api_settings', auth_strategy=auth_strategy)
        self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json)

    def test_statistics_get_deleted(self):
        lb = self.create_load_balancer(
            uuidutils.generate_uuid()).get('loadbalancer')
        self.set_lb_status(lb['id'])
        li = self.create_listener(
            constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener')
        amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id'])
        self.create_listener_stats_dynamic(
            listener_id=li.get('id'),
            amphora_id=amphora.id,
            bytes_in=random.randint(1, 9),
            bytes_out=random.randint(1, 9),
            total_connections=random.randint(1, 9),
            request_errors=random.randint(1, 9))
        self.set_lb_status(lb['id'], status=constants.DELETED)
        self.get(self.LISTENER_PATH.format(
            listener_id=li.get('id') + "/stats"), status=404)

    @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
    def test_listener_pool_protocol_map_post(self, mock_cert_data):
        cert = data_models.TLSContainer(certificate='cert')
        mock_cert_data.return_value = {'sni_certs': [cert]}
        valid_map = constants.VALID_LISTENER_POOL_PROTOCOL_MAP
        port = 1
        for listener_proto in valid_map:
            for pool_proto in valid_map[listener_proto]:
                port = port + 1
                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)
                listener = {'protocol': listener_proto,
                            'protocol_port': port,
                            'loadbalancer_id': self.lb_id,
                            'default_pool_id': pool.get('id')}
                if listener_proto == constants.PROTOCOL_TERMINATED_HTTPS:
                    listener.update(
                        {'sni_container_refs': [uuidutils.generate_uuid()]})
                body = self._build_body(listener)
                self.post(self.LISTENERS_PATH, body, status=201)
                self.set_object_status(self.lb_repo, self.lb_id)

        invalid_map = c_const.INVALID_LISTENER_POOL_PROTOCOL_MAP
        port = 1
        for listener_proto in invalid_map:
            for pool_proto in invalid_map[listener_proto]:
                port = port + 1
                if pool_proto == constants.PROTOCOL_TERMINATED_HTTPS:
                    pool = self.create_pool(
                        self.lb_id, pool_proto,
                        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)
                    expect_error_msg = (
                        "Validation failure: The pool protocol '%s' is "
                        "invalid while the listener protocol is '%s'.") % (
                        pool_proto, listener_proto)
                    listener = {'protocol': listener_proto,
                                'protocol_port': port,
                                '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')
    def test_listener_pool_protocol_map_put(self, mock_cert_data):
        cert = data_models.TLSContainer(certificate='cert')
        mock_cert_data.return_value = {'sni_certs': [cert]}
        valid_map = constants.VALID_LISTENER_POOL_PROTOCOL_MAP
        port = 1
        for listener_proto in valid_map:
            for pool_proto in valid_map[listener_proto]:
                port = port + 1
                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)
                opts = {}
                if listener_proto == constants.PROTOCOL_TERMINATED_HTTPS:
                    opts['sni_container_refs'] = [uuidutils.generate_uuid()]
                listener = self.create_listener(
                    listener_proto, port, self.lb_id, **opts).get('listener')
                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=200)
                self.set_object_status(self.lb_repo, self.lb_id)

        invalid_map = c_const.INVALID_LISTENER_POOL_PROTOCOL_MAP
        port = 100
        for listener_proto in invalid_map:
            opts = {}
            if listener_proto == constants.PROTOCOL_TERMINATED_HTTPS:
                opts['sni_container_refs'] = [uuidutils.generate_uuid()]
            listener = self.create_listener(
                listener_proto, port, self.lb_id, **opts).get('listener')
            self.set_object_status(self.lb_repo, self.lb_id)
            port = port + 1
            for pool_proto in invalid_map[listener_proto]:
                expect_error_msg = ("Validation failure: The pool protocol "
                                    "'%s' is invalid while the listener "
                                    "protocol is '%s'.") % (pool_proto,
                                                            listener_proto)
                if pool_proto == constants.PROTOCOL_TERMINATED_HTTPS:
                    pool = self.create_pool(
                        self.lb_id, pool_proto,
                        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)
                    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)
