Exemple #1
0
    def _validate_listener(self, lock_session, listener_dict):
        """Validate listener for wrong protocol or duplicate listeners

        Update the load balancer db when provisioning status changes.
        """
        if (listener_dict and listener_dict.get('insert_headers') and list(
                set(listener_dict['insert_headers'].keys()) -
                set(constants.SUPPORTED_HTTP_HEADERS))):
            raise exceptions.InvalidOption(
                value=listener_dict.get('insert_headers'),
                option='insert_headers')

        try:
            sni_containers = listener_dict.pop('sni_containers', [])
            db_listener = self.repositories.listener.create(
                lock_session, **listener_dict)
            if sni_containers:
                for container in sni_containers:
                    sni_dict = {
                        'listener_id': db_listener.id,
                        'tls_container_id': container.get('tls_container_id')
                    }
                    self.repositories.sni.create(lock_session, **sni_dict)
                db_listener = self.repositories.listener.get(lock_session,
                                                             id=db_listener.id)
            return db_listener
        except odb_exceptions.DBDuplicateEntry as de:
            if ['id'] == de.columns:
                raise exceptions.IDAlreadyExists()
            elif set(['load_balancer_id', 'protocol_port']) == set(de.columns):
                raise exceptions.DuplicateListenerEntry(
                    port=listener_dict.get('protocol_port'))
        except odb_exceptions.DBError:
            raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
                                           option='protocol')
Exemple #2
0
 def _validate_insert_headers(self, insert_header_list, listener_protocol):
     if (listener_protocol not in
             constants.LISTENER_PROTOCOLS_SUPPORTING_HEADER_INSERTION):
         raise exceptions.InvalidOption(
             value='insert-headers',
             option=('a %s protocol listener.' % listener_protocol))
     if list(set(insert_header_list) - (
             set(constants.SUPPORTED_HTTP_HEADERS +
                 constants.SUPPORTED_SSL_HEADERS))):
         raise exceptions.InvalidOption(
             value=insert_header_list,
             option='insert_headers')
     if not listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS:
         is_matched = len(
             constants.SUPPORTED_SSL_HEADERS) > len(
             list(set(constants.SUPPORTED_SSL_HEADERS) - set(
                 insert_header_list)))
         if is_matched:
             headers = []
             for header_name in insert_header_list:
                 if header_name in constants.SUPPORTED_SSL_HEADERS:
                     headers.append(header_name)
             raise exceptions.InvalidOption(
                 value=headers,
                 option=('%s protocol listener.' % listener_protocol))
Exemple #3
0
    def _validate_listener(self, session, listener_dict):
        """Validate listener for wrong protocol or duplicate listeners

        Update the load balancer db when provisioning status changes.
        """
        lb_repo = self.repositories.load_balancer
        if (listener_dict
            and listener_dict.get('insert_headers')
            and list(set(listener_dict['insert_headers'].keys()) -
                     set(constants.SUPPORTED_HTTP_HEADERS))):
            raise exceptions.InvalidOption(
                value=listener_dict.get('insert_headers'),
                option='insert_headers')

        try:
            sni_containers = listener_dict.pop('sni_containers', [])
            db_listener = self.repositories.listener.create(
                session, **listener_dict)
            if sni_containers:
                for container in sni_containers:
                    sni_dict = {'listener_id': db_listener.id,
                                'tls_container_id': container.get(
                                    'tls_container_id')}
                    self.repositories.sni.create(session, **sni_dict)
                db_listener = self.repositories.listener.get(session,
                                                             id=db_listener.id)
        except odb_exceptions.DBDuplicateEntry as de:
            # Setting LB back to active because this is just a validation
            # failure
            lb_repo.update(session, self.load_balancer_id,
                           provisioning_status=constants.ACTIVE)
            if ['id'] == de.columns:
                raise exceptions.IDAlreadyExists()
            elif set(['load_balancer_id', 'protocol_port']) == set(de.columns):
                raise exceptions.DuplicateListenerEntry(
                    port=listener_dict.get('protocol_port'))
        except odb_exceptions.DBError:
            # Setting LB back to active because this is just a validation
            # failure
            lb_repo.update(session, self.load_balancer_id,
                           provisioning_status=constants.ACTIVE)
            raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
                                           option='protocol')
        try:
            LOG.info(_LI("Sending Creation of Listener %s to handler"),
                     db_listener.id)
            self.handler.create(db_listener)
        except Exception:
            with excutils.save_and_reraise_exception(reraise=False):
                self.repositories.listener.update(
                    session, db_listener.id,
                    provisioning_status=constants.ERROR)
        db_listener = self._get_db_listener(session, db_listener.id)
        return self._convert_db_to_type(db_listener,
                                        listener_types.ListenerResponse)
Exemple #4
0
    def _validate_listener(self, session, lb_id, listener_dict):
        """Validate listener for wrong protocol or duplicate listeners

        Update the load balancer db when provisioning status changes.
        """
        lb_repo = self.repositories.load_balancer
        if (listener_dict and listener_dict.get('insert_headers') and list(
                set(listener_dict['insert_headers'].keys()) -
                set(constants.SUPPORTED_HTTP_HEADERS))):
            raise exceptions.InvalidOption(
                value=listener_dict.get('insert_headers'),
                option='insert_headers')

        try:
            sni_containers = listener_dict.pop('sni_containers', [])
            db_listener = self.repositories.listener.create(
                session, **listener_dict)
            if sni_containers:
                for container in sni_containers:
                    sni_dict = {
                        'listener_id': db_listener.id,
                        'tls_container_id': container.get('tls_container_id')
                    }
                    self.repositories.sni.create(session, **sni_dict)
                db_listener = self.repositories.listener.get(session,
                                                             id=db_listener.id)
            return db_listener
        except odb_exceptions.DBDuplicateEntry as de:
            # Setting LB back to active because this is just a validation
            # failure
            lb_repo.update(session,
                           lb_id,
                           provisioning_status=constants.ACTIVE)
            column_list = ['load_balancer_id', 'protocol_port']
            constraint_list = ['uq_listener_load_balancer_id_protocol_port']
            if ['id'] == de.columns:
                raise exceptions.IDAlreadyExists()
            elif (set(column_list) == set(de.columns)
                  or set(constraint_list) == set(de.columns)):
                raise exceptions.DuplicateListenerEntry(
                    port=listener_dict.get('protocol_port'))
        except odb_exceptions.DBError:
            # Setting LB back to active because this is just a validation
            # failure
            lb_repo.update(session,
                           lb_id,
                           provisioning_status=constants.ACTIVE)
            raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
                                           option='protocol')
Exemple #5
0
    def post(self, health_monitor_):
        """Creates a health monitor on a pool."""
        context = pecan.request.context.get('octavia_context')
        health_monitor = health_monitor_.healthmonitor
        pool = self._get_db_pool(context.session, health_monitor.pool_id)
        health_monitor.project_id = pool.project_id

        self._auth_validate_action(context, health_monitor.project_id,
                                   constants.RBAC_POST)

        lock_session = db_api.get_session(autocommit=False)
        if self.repositories.check_quota_met(
                context.session,
                lock_session,
                data_models.HealthMonitor,
                health_monitor.project_id):
            lock_session.rollback()
            raise exceptions.QuotaException

        hm_dict = db_prepare.create_health_monitor(
            health_monitor.to_dict(render_unsets=True))
        try:
            self._test_lb_and_listener_and_pool_statuses(
                lock_session, health_monitor)
            db_hm = self._validate_create_hm(lock_session, hm_dict)
            lock_session.commit()
        except odb_exceptions.DBError:
            lock_session.rollback()
            raise exceptions.InvalidOption(
                value=hm_dict.get('type'), option='type')
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()

        return self._send_hm_to_handler(context.session, db_hm)
Exemple #6
0
    def post(self, flavor_profile_):
        """Creates a flavor Profile."""
        flavorprofile = flavor_profile_.flavorprofile
        context = pecan_request.context.get('octavia_context')
        self._auth_validate_action(context, context.project_id,
                                   constants.RBAC_POST)
        # Do a basic JSON validation on the metadata
        try:
            flavor_data_dict = jsonutils.loads(flavorprofile.flavor_data)
        except Exception as e:
            raise exceptions.InvalidOption(
                value=flavorprofile.flavor_data,
                option=constants.FLAVOR_DATA) from e

        # Validate that the provider driver supports the metadata
        driver = driver_factory.get_driver(flavorprofile.provider_name)
        driver_utils.call_provider(driver.name, driver.validate_flavor,
                                   flavor_data_dict)

        lock_session = db_api.get_session(autocommit=False)
        try:
            flavorprofile_dict = flavorprofile.to_dict(render_unsets=True)
            flavorprofile_dict['id'] = uuidutils.generate_uuid()
            db_flavor_profile = self.repositories.flavor_profile.create(
                lock_session, **flavorprofile_dict)
            lock_session.commit()
        except odb_exceptions.DBDuplicateEntry as e:
            lock_session.rollback()
            raise exceptions.IDAlreadyExists() from e
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()
        result = self._convert_db_to_type(
            db_flavor_profile, profile_types.FlavorProfileResponse)
        return profile_types.FlavorProfileRootResponse(flavorprofile=result)
Exemple #7
0
    def _validate_create_pool(self, lock_session, pool_dict, listener_id=None):
        """Validate creating pool on load balancer.

        Update database for load balancer and (optional) listener based on
        provisioning status.
        """
        # Make sure we have a client CA if they specify a CRL
        if (pool_dict.get('crl_container_id')
                and not pool_dict.get('ca_tls_certificate_id')):
            raise exceptions.ValidationException(
                detail=_("A CA certificate reference is required to "
                         "specify a revocation list."))

        tls_certificate_id = pool_dict.get('tls_certificate_id', None)
        tls_refs = [tls_certificate_id] if tls_certificate_id else []
        self._validate_tls_refs(tls_refs)

        # Validate the client CA cert and optional client CRL
        if pool_dict.get('ca_tls_certificate_id'):
            self._validate_client_ca_and_crl_refs(
                pool_dict.get('ca_tls_certificate_id'),
                pool_dict.get('crl_container_id', None))

        try:
            return self.repositories.create_pool_on_load_balancer(
                lock_session, pool_dict, listener_id=listener_id)
        except odb_exceptions.DBDuplicateEntry:
            raise exceptions.IDAlreadyExists()
        except odb_exceptions.DBError:
            # TODO(blogan): will have to do separate validation protocol
            # before creation or update since the exception messages
            # do not give any information as to what constraint failed
            raise exceptions.InvalidOption(value='', option='')
Exemple #8
0
    def post(self, health_monitor_):
        """Creates a health monitor on a pool."""
        context = pecan.request.context.get('octavia_context')
        health_monitor = health_monitor_.healthmonitor

        if (not CONF.api_settings.allow_ping_health_monitors
                and health_monitor.type == consts.HEALTH_MONITOR_PING):
            raise exceptions.DisabledOption(option='type',
                                            value=consts.HEALTH_MONITOR_PING)

        pool = self._get_db_pool(context.session, health_monitor.pool_id)

        health_monitor.project_id, provider = self._get_lb_project_id_provider(
            context.session, pool.load_balancer_id)

        self._auth_validate_action(context, health_monitor.project_id,
                                   consts.RBAC_POST)

        # Load the driver early as it also provides validation
        driver = driver_factory.get_driver(provider)

        lock_session = db_api.get_session(autocommit=False)
        try:
            if self.repositories.check_quota_met(context.session, lock_session,
                                                 data_models.HealthMonitor,
                                                 health_monitor.project_id):
                raise exceptions.QuotaException(
                    resource=data_models.HealthMonitor._name())

            hm_dict = db_prepare.create_health_monitor(
                health_monitor.to_dict(render_unsets=True))

            self._test_lb_and_listener_and_pool_statuses(
                lock_session, health_monitor)
            db_hm = self._validate_create_hm(lock_session, hm_dict)

            # Prepare the data for the driver data model
            provider_healthmon = (driver_utils.db_HM_to_provider_HM(db_hm))

            # Dispatch to the driver
            LOG.info("Sending create Health Monitor %s to provider %s",
                     db_hm.id, driver.name)
            driver_utils.call_provider(driver.name,
                                       driver.health_monitor_create,
                                       provider_healthmon)

            lock_session.commit()
        except odb_exceptions.DBError:
            lock_session.rollback()
            raise exceptions.InvalidOption(value=hm_dict.get('type'),
                                           option='type')
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()

        db_hm = self._get_db_hm(context.session, db_hm.id)
        result = self._convert_db_to_type(db_hm,
                                          hm_types.HealthMonitorResponse)
        return hm_types.HealthMonitorRootResponse(healthmonitor=result)
Exemple #9
0
    def _validate_update_fp(self, context, id, flavorprofile):
        if flavorprofile.name is None:
            raise exceptions.InvalidOption(value=None, option=constants.NAME)
        if flavorprofile.provider_name is None:
            raise exceptions.InvalidOption(value=None,
                                           option=constants.PROVIDER_NAME)
        if flavorprofile.flavor_data is None:
            raise exceptions.InvalidOption(value=None,
                                           option=constants.FLAVOR_DATA)

        # Don't allow changes to the flavor_data or provider_name if it
        # is in use.
        if (not isinstance(flavorprofile.flavor_data, wtypes.UnsetType) or
                not isinstance(flavorprofile.provider_name, wtypes.UnsetType)):
            if self.repositories.flavor.count(context.session,
                                              flavor_profile_id=id) > 0:
                raise exceptions.ObjectInUse(object='Flavor profile', id=id)
    def post(self, health_monitor):
        """Creates a health monitor on a pool."""
        context = pecan.request.context.get('octavia_context')

        health_monitor.project_id = self._get_lb_project_id(
            context.session, self.load_balancer_id)

        try:
            db_hm = self.repositories.health_monitor.get(
                context.session, pool_id=self.pool_id)
            if db_hm:
                raise exceptions.DuplicateHealthMonitor()
        except exceptions.NotFound:
            pass

        lock_session = db_api.get_session(autocommit=False)
        if self.repositories.check_quota_met(
                context.session,
                lock_session,
                data_models.HealthMonitor,
                health_monitor.project_id):
            lock_session.rollback()
            raise exceptions.QuotaException(
                resource=data_models.HealthMonitor._name()
            )

        try:
            hm_dict = db_prepare.create_health_monitor(
                health_monitor.to_dict(render_unsets=True), self.pool_id)
            self._test_lb_and_listener_statuses(lock_session)

            db_hm = self.repositories.health_monitor.create(lock_session,
                                                            **hm_dict)
            db_new_hm = self._get_db_hm(lock_session)
            lock_session.commit()
        except odb_exceptions.DBError:
            lock_session.rollback()
            raise exceptions.InvalidOption(value=hm_dict.get('type'),
                                           option='type')
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()

        try:
            LOG.info("Sending Creation of Health Monitor for Pool %s to "
                     "handler", self.pool_id)
            self.handler.create(db_hm)
        except Exception:
            for listener_id in self._get_affected_listener_ids(
                    context.session):
                with excutils.save_and_reraise_exception(reraise=False):
                    self.repositories.listener.update(
                        context.session, listener_id,
                        operating_status=constants.ERROR)
        return self._convert_db_to_type(db_new_hm,
                                        hm_types.HealthMonitorResponse)
    def _validate_create_hm(self, lock_session, hm_dict):
        """Validate creating health monitor on pool."""
        mandatory_fields = (consts.TYPE, consts.DELAY, consts.TIMEOUT,
                            consts.POOL_ID)
        for field in mandatory_fields:
            if hm_dict.get(field, None) is None:
                raise exceptions.InvalidOption(value='None', option=field)
        # MAX_RETRIES is renamed fall_threshold so handle is special
        if hm_dict.get(consts.RISE_THRESHOLD, None) is None:
            raise exceptions.InvalidOption(value='None',
                                           option=consts.MAX_RETRIES)

        if hm_dict[consts.TYPE] not in (consts.HEALTH_MONITOR_HTTP,
                                        consts.HEALTH_MONITOR_HTTPS):
            if hm_dict.get(consts.HTTP_METHOD, None):
                raise exceptions.InvalidOption(
                    value=consts.HTTP_METHOD, option='health monitors of '
                    'type {}'.format(hm_dict[consts.TYPE]))
            if hm_dict.get(consts.URL_PATH, None):
                raise exceptions.InvalidOption(
                    value=consts.URL_PATH, option='health monitors of '
                    'type {}'.format(hm_dict[consts.TYPE]))
            if hm_dict.get(consts.EXPECTED_CODES, None):
                raise exceptions.InvalidOption(
                    value=consts.EXPECTED_CODES, option='health monitors of '
                    'type {}'.format(hm_dict[consts.TYPE]))
        else:
            if not hm_dict.get(consts.HTTP_METHOD, None):
                hm_dict[consts.HTTP_METHOD] = (
                    consts.HEALTH_MONITOR_HTTP_DEFAULT_METHOD)
            if not hm_dict.get(consts.URL_PATH, None):
                hm_dict[consts.URL_PATH] = (
                    consts.HEALTH_MONITOR_DEFAULT_URL_PATH)
            if not hm_dict.get(consts.EXPECTED_CODES, None):
                hm_dict[consts.EXPECTED_CODES] = (
                    consts.HEALTH_MONITOR_DEFAULT_EXPECTED_CODES)

        if hm_dict.get('domain_name') and not hm_dict.get('http_version'):
            raise exceptions.ValidationException(
                detail=_("'http_version' must be specified when 'domain_name' "
                         "is provided."))

        if hm_dict.get('http_version') and hm_dict.get('domain_name'):
            if hm_dict['http_version'] < 1.1:
                raise exceptions.InvalidOption(
                    value='http_version %s' % hm_dict['http_version'],
                    option='health monitors HTTP 1.1 domain name health check')

        try:
            return self.repositories.health_monitor.create(
                lock_session, **hm_dict)
        except odb_exceptions.DBDuplicateEntry:
            raise exceptions.DuplicateHealthMonitor()
        except odb_exceptions.DBError:
            # TODO(blogan): will have to do separate validation protocol
            # before creation or update since the exception messages
            # do not give any information as to what constraint failed
            raise exceptions.InvalidOption(value='', option='')
Exemple #12
0
 def _validate_create_l7rule(self, lock_session, l7rule_dict):
     try:
         return self.repositories.l7rule.create(lock_session, **l7rule_dict)
     except odb_exceptions.DBDuplicateEntry:
         raise exceptions.IDAlreadyExists()
     except odb_exceptions.DBError:
         # TODO(blogan): will have to do separate validation protocol
         # before creation or update since the exception messages
         # do not give any information as to what constraint failed
         raise exceptions.InvalidOption(value='', option='')
Exemple #13
0
    def _validate_update_azp(self, context, id, availability_zone_profile):
        if availability_zone_profile.name is None:
            raise exceptions.InvalidOption(value=None, option=constants.NAME)
        if availability_zone_profile.provider_name is None:
            raise exceptions.InvalidOption(
                value=None, option=constants.PROVIDER_NAME)
        if availability_zone_profile.availability_zone_data is None:
            raise exceptions.InvalidOption(
                value=None, option=constants.AVAILABILITY_ZONE_DATA)

        # Don't allow changes to the availability_zone_data or provider_name if
        # it is in use.
        if (not isinstance(availability_zone_profile.availability_zone_data,
                           wtypes.UnsetType) or
                not isinstance(availability_zone_profile.provider_name,
                               wtypes.UnsetType)):
            if self.repositories.availability_zone.count(
                    context.session, availability_zone_profile_id=id) > 0:
                raise exceptions.ObjectInUse(
                    object='Availability Zone Profile', id=id)
Exemple #14
0
 def post(self, health_monitor):
     """Creates a health monitor on a pool."""
     session = db_api.get_session()
     try:
         db_hm = self.repositories.health_monitor.get(session,
                                                      pool_id=self.pool_id)
         if db_hm:
             raise exceptions.DuplicateHealthMonitor()
     except exceptions.NotFound:
         pass
     hm_dict = health_monitor.to_dict()
     hm_dict['pool_id'] = self.pool_id
     # Verify load balancer is in a mutable status.  If so it can be assumed
     # that the listener is also in a mutable status because a load balancer
     # will only be ACTIVE when all it's listeners as ACTIVE.
     if not self.repositories.test_and_set_lb_and_listener_prov_status(
             session, self.load_balancer_id, self.listener_id,
             constants.PENDING_UPDATE, constants.PENDING_UPDATE):
         LOG.info(
             _LI("Health Monitor for Pool %s cannot be updated "
                 "because the Load Balancer is immutable."), self.pool_id)
         lb_repo = self.repositories.load_balancer
         db_lb = lb_repo.get(session, id=self.load_balancer_id)
         raise exceptions.ImmutableObject(resource=db_lb._name(),
                                          id=self.load_balancer_id)
     try:
         db_hm = self.repositories.health_monitor.create(session, **hm_dict)
     except odb_exceptions.DBError:
         # Setting LB and Listener back to active because this is just a
         # validation failure
         self.repositories.load_balancer.update(
             session,
             self.load_balancer_id,
             provisioning_status=constants.ACTIVE)
         self.repositories.listener.update(
             session,
             self.listener_id,
             provisioning_status=constants.ACTIVE)
         raise exceptions.InvalidOption(value=hm_dict.get('type'),
                                        option='type')
     try:
         LOG.info(
             _LI("Sending Creation of Health Monitor for Pool %s to "
                 "handler"), self.pool_id)
         self.handler.create(db_hm)
     except Exception:
         with excutils.save_and_reraise_exception(reraise=False):
             self.repositories.listener.update(
                 session,
                 self.listener_id,
                 operating_status=constants.ERROR)
     db_hm = self.repositories.health_monitor.get(session,
                                                  pool_id=self.pool_id)
     return self._convert_db_to_type(db_hm, hm_types.HealthMonitorResponse)
Exemple #15
0
    def put(self, id, flavor_profile_):
        """Updates a flavor Profile."""
        flavorprofile = flavor_profile_.flavorprofile
        context = pecan.request.context.get('octavia_context')
        self._auth_validate_action(context, context.project_id,
                                   constants.RBAC_PUT)

        # Don't allow changes to the flavor_data or provider_name if it
        # is in use.
        if (not isinstance(flavorprofile.flavor_data, wtypes.UnsetType) or
                not isinstance(flavorprofile.provider_name, wtypes.UnsetType)):
            if self.repositories.flavor.count(context.session,
                                              flavor_profile_id=id) > 0:
                raise exceptions.ObjectInUse(object='Flavor profile', id=id)

        if not isinstance(flavorprofile.flavor_data, wtypes.UnsetType):
            # Do a basic JSON validation on the metadata
            try:
                flavor_data_dict = jsonutils.loads(flavorprofile.flavor_data)
            except Exception:
                raise exceptions.InvalidOption(value=flavorprofile.flavor_data,
                                               option=constants.FLAVOR_DATA)

            if isinstance(flavorprofile.provider_name, wtypes.UnsetType):
                db_flavor_profile = self._get_db_flavor_profile(
                    context.session, id)
                provider_driver = db_flavor_profile.provider_name
            else:
                provider_driver = flavorprofile.provider_name

            # Validate that the provider driver supports the metadata
            driver = driver_factory.get_driver(provider_driver)
            driver_utils.call_provider(driver.name, driver.validate_flavor,
                                       flavor_data_dict)

        lock_session = db_api.get_session(autocommit=False)
        try:
            flavorprofile_dict = flavorprofile.to_dict(render_unsets=False)
            if flavorprofile_dict:
                self.repositories.flavor_profile.update(
                    lock_session, id, **flavorprofile_dict)
            lock_session.commit()
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()

        # Force SQL alchemy to query the DB, otherwise we get inconsistent
        # results
        context.session.expire_all()
        db_flavor_profile = self._get_db_flavor_profile(context.session, id)
        result = self._convert_db_to_type(db_flavor_profile,
                                          profile_types.FlavorProfileResponse)
        return profile_types.FlavorProfileRootResponse(flavorprofile=result)
Exemple #16
0
 def _validate_create_hm(self, lock_session, hm_dict):
     """Validate creating health monitor on pool."""
     try:
         return self.repositories.health_monitor.create(
             lock_session, **hm_dict)
     except odb_exceptions.DBDuplicateEntry:
         raise exceptions.DuplicateHealthMonitor()
     except odb_exceptions.DBError:
         # TODO(blogan): will have to do separate validation protocol
         # before creation or update since the exception messages
         # do not give any information as to what constraint failed
         raise exceptions.InvalidOption(value='', option='')
Exemple #17
0
 def _validate_update_hm(self, db_hm, health_monitor):
     if db_hm.type != consts.HEALTH_MONITOR_HTTP:
         if health_monitor.http_method != wtypes.Unset:
             raise exceptions.InvalidOption(value=consts.HTTP_METHOD,
                                            option='health monitors of '
                                            'type {}'.format(db_hm.type))
         if health_monitor.url_path != wtypes.Unset:
             raise exceptions.InvalidOption(value=consts.URL_PATH,
                                            option='health monitors of '
                                            'type {}'.format(db_hm.type))
         if health_monitor.expected_codes != wtypes.Unset:
             raise exceptions.InvalidOption(value=consts.URL_PATH,
                                            option='health monitors of '
                                            'type {}'.format(db_hm.type))
     else:
         # For HTTP health monitor these cannot be null/None
         if health_monitor.http_method is None:
             health_monitor.http_method = wtypes.Unset
         if health_monitor.url_path is None:
             health_monitor.url_path = wtypes.Unset
         if health_monitor.expected_codes is None:
             health_monitor.expected_codes = wtypes.Unset
Exemple #18
0
    def _validate_create_pool(self, lock_session, pool_dict, listener_id=None):
        """Validate creating pool on load balancer.

        Update database for load balancer and (optional) listener based on
        provisioning status.
        """
        # Make sure we have a client CA if they specify a CRL
        if (pool_dict.get('crl_container_id')
                and not pool_dict.get('ca_tls_certificate_id')):
            raise exceptions.ValidationException(
                detail=_("A CA certificate reference is required to "
                         "specify a revocation list."))

        tls_certificate_id = pool_dict.get('tls_certificate_id', None)
        tls_refs = [tls_certificate_id] if tls_certificate_id else []
        self._validate_tls_refs(tls_refs)

        # Validate the client CA cert and optional client CRL
        if pool_dict.get('ca_tls_certificate_id'):
            self._validate_client_ca_and_crl_refs(
                pool_dict.get('ca_tls_certificate_id'),
                pool_dict.get('crl_container_id', None))

        # Check TLS cipher prohibit list
        if 'tls_ciphers' in pool_dict and pool_dict['tls_ciphers']:
            rejected_ciphers = validate.check_cipher_prohibit_list(
                pool_dict['tls_ciphers'])
            if rejected_ciphers:
                raise exceptions.ValidationException(detail=_(
                    'The following ciphers have been prohibited by an '
                    'administrator: ' + ', '.join(rejected_ciphers)))

        if pool_dict['tls_enabled']:
            # Validate TLS version list
            validate.check_tls_version_list(pool_dict['tls_versions'])
            # Validate TLS versions against minimum
            validate.check_tls_version_min(pool_dict['tls_versions'])
            # Validate ALPN protocol list
            validate.check_alpn_protocols(pool_dict['alpn_protocols'])

        try:
            return self.repositories.create_pool_on_load_balancer(
                lock_session, pool_dict, listener_id=listener_id)
        except odb_exceptions.DBDuplicateEntry as e:
            raise exceptions.IDAlreadyExists() from e
        except odb_exceptions.DBError as e:
            # TODO(blogan): will have to do separate validation protocol
            # before creation or update since the exception messages
            # do not give any information as to what constraint failed
            raise exceptions.InvalidOption(value='', option='') from e
Exemple #19
0
    def _validate_update_hm(self, db_hm, health_monitor):
        if db_hm.type not in (consts.HEALTH_MONITOR_HTTP,
                              consts.HEALTH_MONITOR_HTTPS):
            if health_monitor.http_method != wtypes.Unset:
                raise exceptions.InvalidOption(
                    value=consts.HTTP_METHOD, option='health monitors of '
                    'type {}'.format(db_hm.type))
            if health_monitor.url_path != wtypes.Unset:
                raise exceptions.InvalidOption(
                    value=consts.URL_PATH, option='health monitors of '
                    'type {}'.format(db_hm.type))
            if health_monitor.expected_codes != wtypes.Unset:
                raise exceptions.InvalidOption(
                    value=consts.EXPECTED_CODES, option='health monitors of '
                    'type {}'.format(db_hm.type))
        else:
            # For HTTP health monitor these cannot be null/None
            if health_monitor.http_method is None:
                health_monitor.http_method = wtypes.Unset
            if health_monitor.url_path is None:
                health_monitor.url_path = wtypes.Unset
            if health_monitor.expected_codes is None:
                health_monitor.expected_codes = wtypes.Unset

        if health_monitor.domain_name and not (
                db_hm.http_version or health_monitor.http_version):
            raise exceptions.ValidationException(
                detail=_("'http_version' must be specified when 'domain_name' "
                         "is provided."))

        if ((db_hm.http_version or health_monitor.http_version) and
                (db_hm.domain_name or health_monitor.domain_name)):
            http_version = health_monitor.http_version or db_hm.http_version
            if http_version < 1.1:
                raise exceptions.InvalidOption(
                    value='http_version %s' % http_version,
                    option='health monitors HTTP 1.1 domain name health check')
Exemple #20
0
    def _validate_create_pool(self, lock_session, pool_dict, listener_id=None):
        """Validate creating pool on load balancer.

        Update database for load balancer and (optional) listener based on
        provisioning status.
        """
        try:
            return self.repositories.create_pool_on_load_balancer(
                lock_session, pool_dict, listener_id=listener_id)
        except odb_exceptions.DBDuplicateEntry:
            raise exceptions.IDAlreadyExists()
        except odb_exceptions.DBError:
            # TODO(blogan): will have to do separate validation protocol
            # before creation or update since the exception messages
            # do not give any information as to what constraint failed
            raise exceptions.InvalidOption(value='', option='')
    def post(self, health_monitor):
        """Creates a health monitor on a pool."""
        context = pecan.request.context.get('octavia_context')
        try:
            db_hm = self.repositories.health_monitor.get(context.session,
                                                         pool_id=self.pool_id)
            if db_hm:
                raise exceptions.DuplicateHealthMonitor()
        except exceptions.NotFound:
            pass
        hm_dict = db_prepare.create_health_monitor(
            health_monitor.to_dict(render_unsets=True), self.pool_id)
        self._test_lb_and_listener_statuses(context.session)

        try:
            db_hm = self.repositories.health_monitor.create(
                context.session, **hm_dict)
        except odb_exceptions.DBError:
            # Setting LB and Listener back to active because this is just a
            # validation failure
            self.repositories.load_balancer.update(
                context.session,
                self.load_balancer_id,
                provisioning_status=constants.ACTIVE)
            for listener_id in self._get_affected_listener_ids(
                    context.session):
                self.repositories.listener.update(
                    context.session,
                    listener_id,
                    provisioning_status=constants.ACTIVE)
            raise exceptions.InvalidOption(value=hm_dict.get('type'),
                                           option='type')
        try:
            LOG.info(
                _LI("Sending Creation of Health Monitor for Pool %s to "
                    "handler"), self.pool_id)
            self.handler.create(db_hm)
        except Exception:
            for listener_id in self._get_affected_listener_ids(
                    context.session):
                with excutils.save_and_reraise_exception(reraise=False):
                    self.repositories.listener.update(
                        context.session,
                        listener_id,
                        operating_status=constants.ERROR)
        db_hm = self._get_db_hm(context.session)
        return self._convert_db_to_type(db_hm, hm_types.HealthMonitorResponse)
Exemple #22
0
    def _validate_create_l7policy(self, lock_session, l7policy_dict):
        try:
            # Set the default HTTP redirect code here so it's explicit
            if ((l7policy_dict.get('redirect_url')
                 or l7policy_dict.get('redirect_prefix'))
                    and not l7policy_dict.get('redirect_http_code')):
                l7policy_dict['redirect_http_code'] = 302

            return self.repositories.l7policy.create(lock_session,
                                                     **l7policy_dict)
        except odb_exceptions.DBDuplicateEntry as e:
            raise exceptions.IDAlreadyExists() from e
        except odb_exceptions.DBError as e:
            # TODO(blogan): will have to do separate validation protocol
            # before creation or update since the exception messages
            # do not give any information as to what constraint failed
            raise exceptions.InvalidOption(value='', option='') from e
Exemple #23
0
    def post(self, availability_zone_profile_):
        """Creates an Availability Zone Profile."""
        availability_zone_profile = (
            availability_zone_profile_.availability_zone_profile)
        context = pecan_request.context.get('octavia_context')
        self._auth_validate_action(context, context.project_id,
                                   constants.RBAC_POST)
        # Do a basic JSON validation on the metadata
        try:
            availability_zone_data_dict = jsonutils.loads(
                availability_zone_profile.availability_zone_data)
        except Exception:
            raise exceptions.InvalidOption(
                value=availability_zone_profile.availability_zone_data,
                option=constants.AVAILABILITY_ZONE_DATA)

        # Validate that the provider driver supports the metadata
        driver = driver_factory.get_driver(
            availability_zone_profile.provider_name)
        driver_utils.call_provider(
            driver.name, driver.validate_availability_zone,
            availability_zone_data_dict)

        lock_session = db_api.get_session(autocommit=False)
        try:
            availability_zone_profile_dict = availability_zone_profile.to_dict(
                render_unsets=True)
            availability_zone_profile_dict['id'] = uuidutils.generate_uuid()
            db_availability_zone_profile = (
                self.repositories.availability_zone_profile.create(
                    lock_session, **availability_zone_profile_dict))
            lock_session.commit()
        except odb_exceptions.DBDuplicateEntry:
            lock_session.rollback()
            raise exceptions.IDAlreadyExists()
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()
        result = self._convert_db_to_type(
            db_availability_zone_profile,
            profile_types.AvailabilityZoneProfileResponse)
        return profile_types.AvailabilityZoneProfileRootResponse(
            availability_zone_profile=result)
Exemple #24
0
 def _validate_create_member(self, lock_session, member_dict):
     """Validate creating member on pool."""
     try:
         return self.repositories.member.create(lock_session, **member_dict)
     except odb_exceptions.DBDuplicateEntry as de:
         column_list = ['pool_id', 'ip_address', 'protocol_port']
         constraint_list = ['uq_member_pool_id_address_protocol_port']
         if ['id'] == de.columns:
             raise exceptions.IDAlreadyExists()
         elif (set(column_list) == set(de.columns) or
               set(constraint_list) == set(de.columns)):
             raise exceptions.DuplicateMemberEntry(
                 ip_address=member_dict.get('ip_address'),
                 port=member_dict.get('protocol_port'))
     except odb_exceptions.DBError:
         # TODO(blogan): will have to do separate validation protocol
         # before creation or update since the exception messages
         # do not give any information as to what constraint failed
         raise exceptions.InvalidOption(value='', option='')
Exemple #25
0
    def _validate_create_pool(self, session, sp_dict, pool_dict):
        """Validate creating pool on load balancer.

        Update database for load balancer and listener based on provisioning
        status.
        """
        try:
            db_pool = self.repositories.create_pool_on_listener(
                session, self.listener_id, pool_dict, sp_dict=sp_dict)
        except odb_exceptions.DBDuplicateEntry as de:
            if ['id'] == de.columns:
                raise exceptions.IDAlreadyExists()
        except odb_exceptions.DBError:
            # Setting LB and Listener back to active because this is just a
            # validation failure
            self.repositories.load_balancer.update(
                session,
                self.load_balancer_id,
                provisioning_status=constants.ACTIVE)
            self.repositories.listener.update(
                session,
                self.listener_id,
                provisioning_status=constants.ACTIVE)
            # TODO(blogan): will have to do separate validation protocol
            # before creation or update since the exception messages
            # do not give any information as to what constraint failed
            raise exceptions.InvalidOption(value='', option='')
        try:
            LOG.info(_LI("Sending Creation of Pool %s to handler"), db_pool.id)
            self.handler.create(db_pool)
        except Exception:
            with excutils.save_and_reraise_exception(reraise=False):
                self.repositories.listener.update(
                    session,
                    self.listener_id,
                    operating_status=constants.ERROR)
        db_pool = self.repositories.pool.get(session, id=db_pool.id)
        return self._convert_db_to_type(db_pool, pool_types.PoolResponse)
Exemple #26
0
    def _validate_update_hm(self, db_hm, health_monitor):
        if db_hm.type not in (consts.HEALTH_MONITOR_HTTP,
                              consts.HEALTH_MONITOR_HTTPS):
            if health_monitor.http_method != wtypes.Unset:
                raise exceptions.InvalidOption(value=consts.HTTP_METHOD,
                                               option='health monitors of '
                                               'type {}'.format(db_hm.type))
            if health_monitor.url_path != wtypes.Unset:
                raise exceptions.InvalidOption(value=consts.URL_PATH,
                                               option='health monitors of '
                                               'type {}'.format(db_hm.type))
            if health_monitor.expected_codes != wtypes.Unset:
                raise exceptions.InvalidOption(value=consts.EXPECTED_CODES,
                                               option='health monitors of '
                                               'type {}'.format(db_hm.type))
        if health_monitor.delay is None:
            raise exceptions.InvalidOption(value=None, option=consts.DELAY)
        if health_monitor.max_retries is None:
            raise exceptions.InvalidOption(value=None,
                                           option=consts.MAX_RETRIES)
        if health_monitor.timeout is None:
            raise exceptions.InvalidOption(value=None, option=consts.TIMEOUT)

        if health_monitor.domain_name and not (db_hm.http_version
                                               or health_monitor.http_version):
            raise exceptions.ValidationException(
                detail=_("'http_version' must be specified when 'domain_name' "
                         "is provided."))

        if ((db_hm.http_version or health_monitor.http_version)
                and (db_hm.domain_name or health_monitor.domain_name)):
            http_version = health_monitor.http_version or db_hm.http_version
            if http_version < 1.1:
                raise exceptions.InvalidOption(
                    value='http_version %s' % http_version,
                    option='health monitors HTTP 1.1 domain name health check')
Exemple #27
0
    def put(self, id, availability_zone_profile_):
        """Updates an Availability Zone Profile."""
        availability_zone_profile = (
            availability_zone_profile_.availability_zone_profile)
        context = pecan_request.context.get('octavia_context')
        self._auth_validate_action(context, context.project_id,
                                   constants.RBAC_PUT)

        self._validate_update_azp(context, id, availability_zone_profile)
        if id == constants.NIL_UUID:
            raise exceptions.NotFound(resource='Availability Zone Profile',
                                      id=constants.NIL_UUID)

        if not isinstance(availability_zone_profile.availability_zone_data,
                          wtypes.UnsetType):
            # Do a basic JSON validation on the metadata
            try:
                availability_zone_data_dict = jsonutils.loads(
                    availability_zone_profile.availability_zone_data)
            except Exception:
                raise exceptions.InvalidOption(
                    value=availability_zone_profile.availability_zone_data,
                    option=constants.FLAVOR_DATA)

            if isinstance(availability_zone_profile.provider_name,
                          wtypes.UnsetType):
                db_availability_zone_profile = (
                    self._get_db_availability_zone_profile(
                        context.session, id))
                provider_driver = db_availability_zone_profile.provider_name
            else:
                provider_driver = availability_zone_profile.provider_name

            # Validate that the provider driver supports the metadata
            driver = driver_factory.get_driver(provider_driver)
            driver_utils.call_provider(
                driver.name, driver.validate_availability_zone,
                availability_zone_data_dict)

        lock_session = db_api.get_session(autocommit=False)
        try:
            availability_zone_profile_dict = availability_zone_profile.to_dict(
                render_unsets=False)
            if availability_zone_profile_dict:
                self.repositories.availability_zone_profile.update(
                    lock_session, id, **availability_zone_profile_dict)
            lock_session.commit()
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()

        # Force SQL alchemy to query the DB, otherwise we get inconsistent
        # results
        context.session.expire_all()
        db_availability_zone_profile = self._get_db_availability_zone_profile(
            context.session, id)
        result = self._convert_db_to_type(
            db_availability_zone_profile,
            profile_types.AvailabilityZoneProfileResponse)
        return profile_types.AvailabilityZoneProfileRootResponse(
            availability_zone_profile=result)
Exemple #28
0
    def _validate_create_listener(self, lock_session, listener_dict):
        """Validate listener for wrong protocol or duplicate listeners

        Update the load balancer db when provisioning status changes.
        """
        listener_protocol = listener_dict.get('protocol')

        if listener_dict and listener_dict.get('insert_headers'):
            self._validate_insert_headers(
                listener_dict['insert_headers'].keys(), listener_protocol)

        # Check for UDP compatibility
        if (listener_protocol == constants.PROTOCOL_UDP and
                self._is_tls_or_insert_header(listener_dict)):
            raise exceptions.ValidationException(detail=_(
                "%s protocol listener does not support TLS or header "
                "insertion.") % constants.PROTOCOL_UDP)

        # Check for TLS disabled
        if (not CONF.api_settings.allow_tls_terminated_listeners and
                listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS):
            raise exceptions.DisabledOption(
                value=constants.PROTOCOL_TERMINATED_HTTPS, option='protocol')

        # Check for certs when not TERMINATED_HTTPS
        if (listener_protocol != constants.PROTOCOL_TERMINATED_HTTPS and
                self._has_tls_container_refs(listener_dict)):
            raise exceptions.ValidationException(detail=_(
                "Certificate container references are only allowed on "
                "%s protocol listeners.") %
                constants.PROTOCOL_TERMINATED_HTTPS)

        # Make sure a base certificate exists if specifying a client ca
        if (listener_dict.get('client_ca_tls_certificate_id') and
            not (listener_dict.get('tls_certificate_id') or
                 listener_dict.get('sni_containers'))):
            raise exceptions.ValidationException(detail=_(
                "An SNI or default certificate container reference must "
                "be provided with a client CA container reference."))

        # Make sure a certificate container is specified for TERMINATED_HTTPS
        if (listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS and
            not (listener_dict.get('tls_certificate_id') or
                 listener_dict.get('sni_containers'))):
            raise exceptions.ValidationException(detail=_(
                "An SNI or default certificate container reference must "
                "be provided for %s protocol listeners.") %
                constants.PROTOCOL_TERMINATED_HTTPS)

        # Make sure we have a client CA cert if they enable client auth
        if (listener_dict.get('client_authentication') !=
            constants.CLIENT_AUTH_NONE and not
                listener_dict.get('client_ca_tls_certificate_id')):
            raise exceptions.ValidationException(detail=_(
                "Client authentication setting %s requires a client CA "
                "container reference.") %
                listener_dict.get('client_authentication'))

        # Make sure we have a client CA if they specify a CRL
        if (listener_dict.get('client_crl_container_id') and
                not listener_dict.get('client_ca_tls_certificate_id')):
            raise exceptions.ValidationException(detail=_(
                "A client authentication CA reference is required to "
                "specify a client authentication revocation list."))

        # Validate the TLS containers
        sni_containers = listener_dict.pop('sni_containers', [])
        tls_refs = [sni['tls_container_id'] for sni in sni_containers]
        if listener_dict.get('tls_certificate_id'):
            tls_refs.append(listener_dict.get('tls_certificate_id'))
        self._validate_tls_refs(tls_refs)

        # Validate the client CA cert and optional client CRL
        if listener_dict.get('client_ca_tls_certificate_id'):
            self._validate_client_ca_and_crl_refs(
                listener_dict.get('client_ca_tls_certificate_id'),
                listener_dict.get('client_crl_container_id', None))

        # Validate that the L4 protocol (UDP or TCP) is not already used for
        # the specified protocol_port in this load balancer
        pcontext = pecan.request.context
        query_filter = {
            'project_id': listener_dict['project_id'],
            'load_balancer_id': listener_dict['load_balancer_id'],
            'protocol_port': listener_dict['protocol_port']
        }

        # Get listeners on the same load balancer that use the same
        # protocol port
        db_listeners = self.repositories.listener.get_all_API_list(
            lock_session, show_deleted=False,
            pagination_helper=pcontext.get(constants.PAGINATION_HELPER),
            **query_filter)[0]

        if db_listeners:
            l4_protocol = constants.L4_PROTOCOL_MAP[listener_protocol]

            # List supported protocols that share the same L4 protocol as our
            # new listener
            disallowed_protocols = [
                p
                for p in constants.L4_PROTOCOL_MAP
                if constants.L4_PROTOCOL_MAP[p] == l4_protocol
            ]

            for db_l in db_listeners:
                # Check if l4 protocol ports conflict
                if db_l.protocol in disallowed_protocols:
                    raise exceptions.DuplicateListenerEntry(
                        protocol=db_l.protocol,
                        port=listener_dict.get('protocol_port'))

        # Validate allowed CIDRs
        allowed_cidrs = listener_dict.get('allowed_cidrs', []) or []
        lb_id = listener_dict.get('load_balancer_id')
        vip_db = self.repositories.vip.get(
            lock_session, load_balancer_id=lb_id)
        vip_address = vip_db.ip_address
        self._validate_cidr_compatible_with_vip(vip_address, allowed_cidrs)

        try:
            db_listener = self.repositories.listener.create(
                lock_session, **listener_dict)
            if sni_containers:
                for container in sni_containers:
                    sni_dict = {'listener_id': db_listener.id,
                                'tls_container_id': container.get(
                                    'tls_container_id')}
                    self.repositories.sni.create(lock_session, **sni_dict)
                # DB listener needs to be refreshed
                db_listener = self.repositories.listener.get(
                    lock_session, id=db_listener.id)

            return db_listener
        except odb_exceptions.DBDuplicateEntry as de:
            column_list = ['load_balancer_id', 'protocol', 'protocol_port']
            constraint_list = ['uq_listener_load_balancer_id_protocol_port']
            if ['id'] == de.columns:
                raise exceptions.IDAlreadyExists()
            if (set(column_list) == set(de.columns) or
                    set(constraint_list) == set(de.columns)):
                raise exceptions.DuplicateListenerEntry(
                    protocol=listener_dict.get('protocol'),
                    port=listener_dict.get('protocol_port'))
        except odb_exceptions.DBError:
            raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
                                           option='protocol')
Exemple #29
0
def ip_not_reserved(ip_address):
    ip_address = (ipaddress.ip_address(ip_address).exploded.upper())
    if ip_address in CONF.networking.reserved_ips:
        raise exceptions.InvalidOption(value=ip_address,
                                       option='member address')
Exemple #30
0
    def _validate_create_listener(self, lock_session, listener_dict):
        """Validate listener for wrong protocol or duplicate listeners

        Update the load balancer db when provisioning status changes.
        """
        listener_protocol = listener_dict.get('protocol')

        if listener_dict and listener_dict.get('insert_headers'):
            self._validate_insert_headers(
                listener_dict['insert_headers'].keys(), listener_protocol)

        # Check for UDP compatibility
        if (listener_protocol == constants.PROTOCOL_UDP
                and self._is_tls_or_insert_header(listener_dict)):
            raise exceptions.ValidationException(
                detail=_("%s protocol listener does not support TLS or header "
                         "insertion.") % constants.PROTOCOL_UDP)

        # Check for TLS disabled
        if (not CONF.api_settings.allow_tls_terminated_listeners
                and listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS):
            raise exceptions.DisabledOption(
                value=constants.PROTOCOL_TERMINATED_HTTPS, option='protocol')

        # Check for certs when not TERMINATED_HTTPS
        if (listener_protocol != constants.PROTOCOL_TERMINATED_HTTPS
                and self._has_tls_container_refs(listener_dict)):
            raise exceptions.ValidationException(
                detail=_(
                    "Certificate container references are only allowed on "
                    "%s protocol listeners.") %
                constants.PROTOCOL_TERMINATED_HTTPS)

        # Make sure a base certificate exists if specifying a client ca
        if (listener_dict.get('client_ca_tls_certificate_id')
                and not (listener_dict.get('tls_certificate_id')
                         or listener_dict.get('sni_containers'))):
            raise exceptions.ValidationException(detail=_(
                "An SNI or default certificate container reference must "
                "be provided with a client CA container reference."))

        # Make sure a certificate container is specified for TERMINATED_HTTPS
        if (listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS
                and not (listener_dict.get('tls_certificate_id')
                         or listener_dict.get('sni_containers'))):
            raise exceptions.ValidationException(
                detail=_(
                    "An SNI or default certificate container reference must "
                    "be provided for %s protocol listeners.") %
                constants.PROTOCOL_TERMINATED_HTTPS)

        # Make sure we have a client CA cert if they enable client auth
        if (listener_dict.get('client_authentication') !=
                constants.CLIENT_AUTH_NONE
                and not listener_dict.get('client_ca_tls_certificate_id')):
            raise exceptions.ValidationException(
                detail=_(
                    "Client authentication setting %s requires a client CA "
                    "container reference.") %
                listener_dict.get('client_authentication'))

        # Make sure we have a client CA if they specify a CRL
        if (listener_dict.get('client_crl_container_id')
                and not listener_dict.get('client_ca_tls_certificate_id')):
            raise exceptions.ValidationException(
                detail=_("A client authentication CA reference is required to "
                         "specify a client authentication revocation list."))

        # Validate the TLS containers
        sni_containers = listener_dict.pop('sni_containers', [])
        tls_refs = [sni['tls_container_id'] for sni in sni_containers]
        if listener_dict.get('tls_certificate_id'):
            tls_refs.append(listener_dict.get('tls_certificate_id'))
        self._validate_tls_refs(tls_refs)

        # Validate the client CA cert and optional client CRL
        if listener_dict.get('client_ca_tls_certificate_id'):
            self._validate_client_ca_and_crl_refs(
                listener_dict.get('client_ca_tls_certificate_id'),
                listener_dict.get('client_crl_container_id', None))

        try:
            db_listener = self.repositories.listener.create(
                lock_session, **listener_dict)
            if sni_containers:
                for container in sni_containers:
                    sni_dict = {
                        'listener_id': db_listener.id,
                        'tls_container_id': container.get('tls_container_id')
                    }
                    self.repositories.sni.create(lock_session, **sni_dict)
                db_listener = self.repositories.listener.get(lock_session,
                                                             id=db_listener.id)
            return db_listener
        except odb_exceptions.DBDuplicateEntry as de:
            column_list = ['load_balancer_id', 'protocol_port']
            constraint_list = ['uq_listener_load_balancer_id_protocol_port']
            if ['id'] == de.columns:
                raise exceptions.IDAlreadyExists()
            elif (set(column_list) == set(de.columns)
                  or set(constraint_list) == set(de.columns)):
                raise exceptions.DuplicateListenerEntry(
                    port=listener_dict.get('protocol_port'))
        except odb_exceptions.DBError:
            raise exceptions.InvalidOption(value=listener_dict.get('protocol'),
                                           option='protocol')