Пример #1
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)
Пример #2
0
 def _validate_flavor(self, session, load_balancer):
     if not isinstance(load_balancer.flavor_id, wtypes.UnsetType):
         flavor = self.repositories.flavor.get(session,
                                               id=load_balancer.flavor_id)
         if not flavor:
             raise exceptions.ValidationException(
                 detail=_("Invalid flavor_id."))
         if not flavor.enabled:
             raise exceptions.DisabledOption(option='flavor',
                                             value=load_balancer.flavor_id)
Пример #3
0
 def _validate_availability_zone(self, session, load_balancer):
     if not isinstance(load_balancer.availability_zone, wtypes.UnsetType):
         az = self.repositories.availability_zone.get(
             session, name=load_balancer.availability_zone)
         if not az:
             raise exceptions.ValidationException(
                 detail=_("Invalid availability zone."))
         if not az.enabled:
             raise exceptions.DisabledOption(
                 option='availability_zone',
                 value=load_balancer.availability_zone)
Пример #4
0
    def post(self, listener_):
        """Creates a listener on a load balancer."""
        listener = listener_.listener
        context = pecan.request.context.get('octavia_context')

        load_balancer_id = listener.loadbalancer_id
        listener.project_id = self._get_lb_project_id(
            context.session, load_balancer_id)

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

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

        lock_session = db_api.get_session(autocommit=False)
        try:
            if self.repositories.check_quota_met(
                    context.session,
                    lock_session,
                    data_models.Listener,
                    listener.project_id):
                raise exceptions.QuotaException

            listener_dict = db_prepare.create_listener(
                listener.to_dict(render_unsets=True), None)

            if listener_dict['default_pool_id']:
                self._validate_pool(context.session, load_balancer_id,
                                    listener_dict['default_pool_id'])

            self._test_lb_and_listener_statuses(
                lock_session, lb_id=load_balancer_id)

            db_listener = self._validate_create_listener(
                lock_session, load_balancer_id, listener_dict)
            lock_session.commit()
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()

        return self._send_listener_to_handler(context.session, db_listener)
Пример #5
0
    def post(self, listener_):
        """Creates a listener on a load balancer."""
        listener = listener_.listener
        context = pecan.request.context.get('octavia_context')

        load_balancer_id = listener.loadbalancer_id
        listener.project_id, provider = self._get_lb_project_id_provider(
            context.session, load_balancer_id)

        self._auth_validate_action(context, listener.project_id,
                                   constants.RBAC_POST)
        if (listener.protocol == constants.PROTOCOL_UDP and
                self._is_tls_or_insert_header(listener)):
            raise exceptions.ValidationException(detail=_(
                "%s protocol listener does not support TLS or header "
                "insertion.") % constants.PROTOCOL_UDP)
        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')

        # 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.Listener,
                    listener.project_id):
                raise exceptions.QuotaException(
                    resource=data_models.Listener._name())

            listener_dict = db_prepare.create_listener(
                listener.to_dict(render_unsets=True), None)

            if listener_dict['default_pool_id']:
                self._validate_pool(context.session, load_balancer_id,
                                    listener_dict['default_pool_id'],
                                    listener.protocol)

            self._test_lb_and_listener_statuses(
                lock_session, lb_id=load_balancer_id)

            db_listener = self._validate_create_listener(
                lock_session, listener_dict)

            # Prepare the data for the driver data model
            provider_listener = (
                driver_utils.db_listener_to_provider_listener(db_listener))

            # re-inject the sni container references lost due to SNI
            # being a separate table in the DB
            provider_listener.sni_container_refs = listener.sni_container_refs

            # Dispatch to the driver
            LOG.info("Sending create Listener %s to provider %s",
                     db_listener.id, driver.name)
            driver_utils.call_provider(
                driver.name, driver.listener_create, provider_listener)

            lock_session.commit()
        except Exception:
            with excutils.save_and_reraise_exception():
                lock_session.rollback()

        db_listener = self._get_db_listener(context.session, db_listener.id)
        result = self._convert_db_to_type(db_listener,
                                          listener_types.ListenerResponse)
        return listener_types.ListenerRootResponse(listener=result)
Пример #6
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')
Пример #7
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')