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