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