def _validate_network_and_fill_or_validate_subnet(load_balancer): network = validate.network_exists_optionally_contains_subnet( network_id=load_balancer.vip_network_id, subnet_id=load_balancer.vip_subnet_id) if not load_balancer.vip_subnet_id: network_driver = utils.get_network_driver() if load_balancer.vip_address: for subnet_id in network.subnets: subnet = network_driver.get_subnet(subnet_id) if validate.is_ip_member_of_cidr(load_balancer.vip_address, subnet.cidr): load_balancer.vip_subnet_id = subnet_id break if not load_balancer.vip_subnet_id: raise exceptions.ValidationException(detail=_( "Supplied network does not contain a subnet for " "VIP address specified." )) else: # If subnet and IP are not provided, pick the first subnet, # preferring ipv4 for subnet_id in network.subnets: # Use the first subnet, in case there are no ipv4 subnets if not load_balancer.vip_subnet_id: load_balancer.vip_subnet_id = subnet_id subnet = network_driver.get_subnet(subnet_id) if subnet.ip_version == 4: load_balancer.vip_subnet_id = subnet_id break if not load_balancer.vip_subnet_id: raise exceptions.ValidationException(detail=_( "Supplied network does not contain a subnet." ))
def _validate_pool_request_for_udp(self, request): if request.session_persistence: if (request.session_persistence.type == constants.SESSION_PERSISTENCE_SOURCE_IP and not self._is_only_specified_in_request( request.session_persistence, check_exist_attrs=['type', 'persistence_timeout', 'persistence_granularity'])): raise exceptions.ValidationException(detail=_( "session_persistence %s type for UDP protocol " "only accepts: type, persistence_timeout, " "persistence_granularity.") % ( constants.SESSION_PERSISTENCE_SOURCE_IP)) if request.session_persistence.cookie_name: raise exceptions.ValidationException(detail=_( "Cookie names are not supported for %s pools.") % constants.PROTOCOL_UDP) if request.session_persistence.type in [ constants.SESSION_PERSISTENCE_HTTP_COOKIE, constants.SESSION_PERSISTENCE_APP_COOKIE]: raise exceptions.ValidationException(detail=_( "Session persistence of type %(type)s is not supported " "for %(protocol)s protocol pools.") % { 'type': request.session_persistence.type, 'protocol': constants.PROTOCOL_UDP})
def _validate_port_and_fill_or_validate_subnet(load_balancer): port = validate.port_exists(port_id=load_balancer.vip_port_id) validate.check_port_in_use(port) load_balancer.vip_network_id = port.network_id # validate the request vip port whether applied the qos_policy and # store the port_qos_policy to loadbalancer obj if possible. The # default behavior is that if 'vip_qos_policy_id' is specified in the # request, it will override the qos_policy applied on vip_port. port_qos_policy_id = port.qos_policy_id if (port_qos_policy_id and isinstance(load_balancer.vip_qos_policy_id, wtypes.UnsetType)): load_balancer.vip_qos_policy_id = port_qos_policy_id # Identify the subnet for this port if load_balancer.vip_subnet_id: validate.subnet_exists(subnet_id=load_balancer.vip_subnet_id) else: if load_balancer.vip_address: for port_fixed_ip in port.fixed_ips: if port_fixed_ip.ip_address == load_balancer.vip_address: load_balancer.vip_subnet_id = port_fixed_ip.subnet_id break if not load_balancer.vip_subnet_id: raise exceptions.ValidationException(detail=_( "Specified VIP address not found on the " "specified VIP port.")) elif len(port.fixed_ips) == 1: load_balancer.vip_subnet_id = port.fixed_ips[0].subnet_id else: raise exceptions.ValidationException(detail=_( "VIP port's subnet could not be determined. Please " "specify either a VIP subnet or address."))
def _get_resources_by_filters(self, resource_type, unique_item=False, **filters): """Retrieves item(s) from filters. By default, a list is returned. If unique_item set to True, only the first resource is returned. """ try: resource = getattr(self.neutron_client, 'list_%ss' % resource_type)(**filters) conversion_function = getattr( utils, 'convert_%s_dict_to_model' % resource_type) if not resource['%ss' % resource_type]: # no items found raise neutron_client_exceptions.NotFound() if unique_item: return conversion_function(resource['%ss' % resource_type][0]) return list(map(conversion_function, resource['%ss' % resource_type])) except neutron_client_exceptions.NotFound: message = _('{resource_type} not found ' '({resource_type} Filters: {filters}.').format( resource_type=resource_type, filters=filters) raise getattr(base, '%sNotFound' % ''.join( [w.capitalize() for w in resource_type.split('_')]))(message) except Exception: message = _('Error retrieving {resource_type} ' '({resource_type} Filters: {filters}.').format( resource_type=resource_type, filters=filters) LOG.exception(message) raise base.NetworkException(message)
def _validate_client_ca_and_crl_refs(self, client_ca_ref, crl_ref): context = pecan.request.context.get('octavia_context') bad_refs = [] try: self.cert_manager.set_acls(context, client_ca_ref) ca_pem = self.cert_manager.get_secret(context, client_ca_ref) except Exception: bad_refs.append(client_ca_ref) pem_crl = None if crl_ref: try: self.cert_manager.set_acls(context, crl_ref) pem_crl = self.cert_manager.get_secret(context, crl_ref) except Exception: bad_refs.append(crl_ref) if bad_refs: raise exceptions.CertificateRetrievalException(ref=bad_refs) ca_cert = None try: # Test if it needs to be UTF-8 encoded try: ca_pem = ca_pem.encode('utf-8') except AttributeError: pass ca_cert = x509.load_pem_x509_certificate(ca_pem, default_backend()) except Exception as e: raise exceptions.ValidationException(detail=_( "The client authentication CA certificate is invalid. " "It must be a valid x509 PEM format certificate. " "Error: %s") % str(e)) # Validate the CRL is for the client CA if pem_crl: ca_pub_key = ca_cert.public_key() crl = None # Test if it needs to be UTF-8 encoded try: pem_crl = pem_crl.encode('utf-8') except AttributeError: pass try: crl = x509.load_pem_x509_crl(pem_crl, default_backend()) except Exception as e: raise exceptions.ValidationException(detail=_( "The client authentication certificate revocation list " "is invalid. It must be a valid x509 PEM format " "certificate revocation list. Error: %s") % str(e)) if not crl.is_signature_valid(ca_pub_key): raise exceptions.ValidationException(detail=_( "The CRL specified is not valid for client certificate " "authority reference supplied."))
def _validate_pool_PUT(self, pool, db_pool): if db_pool.protocol == constants.PROTOCOL_UDP: self._validate_pool_request_for_udp(pool) else: if (pool.session_persistence and ( pool.session_persistence.persistence_timeout or pool.session_persistence.persistence_granularity)): raise exceptions.ValidationException(detail=_( "persistence_timeout and persistence_granularity " "is only for UDP protocol pools.")) if pool.session_persistence: sp_dict = pool.session_persistence.to_dict(render_unsets=False) validate.check_session_persistence(sp_dict) crl_ref = None if (pool.crl_container_ref and pool.crl_container_ref != wtypes.Unset): crl_ref = pool.crl_container_ref elif db_pool.crl_container_id: crl_ref = db_pool.crl_container_id ca_ref = None db_ca_ref = db_pool.ca_tls_certificate_id if pool.ca_tls_container_ref != wtypes.Unset: if not pool.ca_tls_container_ref and db_ca_ref and crl_ref: raise exceptions.ValidationException(detail=_( "A CA reference cannot be removed when a " "certificate revocation list is present.")) if not pool.ca_tls_container_ref and not db_ca_ref and crl_ref: raise exceptions.ValidationException(detail=_( "A CA reference is required to " "specify a certificate revocation list.")) if pool.ca_tls_container_ref: ca_ref = pool.ca_tls_container_ref elif db_ca_ref: ca_ref = db_ca_ref elif crl_ref and not db_ca_ref: raise exceptions.ValidationException(detail=_( "A CA reference is required to " "specify a certificate revocation list.")) if pool.tls_container_ref: self._validate_tls_refs([pool.tls_container_ref]) # Validate the client CA cert and optional client CRL if ca_ref: self._validate_client_ca_and_crl_refs(ca_ref, crl_ref)
def _wait_for_load_balancer_status(self, load_balancer_id, provisioning_status='ACTIVE', operating_status='ONLINE', delete=False): interval_time = 1 timeout = 600 end_time = time.time() + timeout while time.time() < end_time: try: lb = self.load_balancers_client.get_load_balancer( load_balancer_id) except lib_exc.NotFound as e: if delete: return else: raise e LOG.info(('provisioning_status: {0} operating_status: {1}'.format( lb.get('provisioning_status'), lb.get('operating_status')))) if delete and lb.get('provisioning_status') == 'DELETED': break elif (lb.get('provisioning_status') == provisioning_status and lb.get('operating_status') == operating_status): break elif (lb.get('provisioning_status') == 'ERROR' or lb.get('operating_status') == 'ERROR'): raise Exception( _("Wait for load balancer for load balancer: {lb_id} " "ran for {timeout} seconds and an ERROR was encountered " "with provisioning status: {provisioning_status} and " "operating status: {operating_status}").format( timeout=timeout, lb_id=lb.get('id'), provisioning_status=provisioning_status, operating_status=operating_status)) time.sleep(interval_time) else: raise Exception( _("Wait for load balancer ran for {timeout} seconds and did " "not observe {lb_id} reach {provisioning_status} " "provisioning status and {operating_status} " "operating status.").format( timeout=timeout, lb_id=lb.get('id'), provisioning_status=provisioning_status, operating_status=operating_status)) return lb
def _check_load_balancing(self): """Check Load Balancing 1. Send NUM requests on the floating ip associated with the VIP 2. Check that the requests are shared between the two servers """ LOG.info(_('Checking load balancing...')) self._wait_for_http_service(self.vip_ip) LOG.info(_('Connection to {vip} is valid').format(vip=self.vip_ip)) counters = self._send_concurrent_requests(self.vip_ip, ["server1", "server2"]) for member, counter in six.iteritems(counters): self.assertGreater(counter, 0, 'Member %s never balanced' % member) LOG.info(_('Done checking load balancing...'))
def validate_non_negative_int(self, key, value): if value < 0: data = {'key': key, 'value': value} raise ValueError(_('The %(key)s field can not have ' 'negative value. ' 'Current value is %(value)d.') % data) return value
def _query(self, query): """Send the given query to the haproxy statistics socket. :returns: the output of a successful query as a string with trailing newlines removed, or raise an Exception if the query fails. """ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: sock.connect(self.socket) except socket.error: raise Exception(_("HAProxy '{0}' query failed.").format(query)) try: sock.send(six.b(query + '\n')) data = u'' while True: x = sock.recv(1024) if not x: break data += x.decode('ascii') if ( isinstance(x, six.binary_type)) else x return data.rstrip() finally: sock.close()
def network_allowed_by_config(network_id): if CONF.networking.valid_vip_networks: valid_networks = map(str.lower, CONF.networking.valid_vip_networks) if network_id not in valid_networks: raise exceptions.ValidationException(detail=_( 'Supplied VIP network_id is not allowed by the configuration ' 'of this deployment.'))
def check_port_in_use(port): """Raise an exception when a port is used.""" if port.device_id: raise exceptions.ValidationException(detail=_( "Port %(port_id)s is already used by device %(device_id)s ") % {'port_id': port.id, 'device_id': port.device_id}) return False
def sign_cert(cls, csr, validity=None, **kwargs): """Signs a certificate using Anchor based on the specified CSR :param csr: A Certificate Signing Request :param validity: Will be ignored for now :param kwargs: Will be ignored for now :return: Signed certificate :raises Exception: if certificate signing fails """ LOG.debug("Signing a certificate request using Anchor") try: LOG.debug('Certificate: %s', csr) r = requests.post(CONF.anchor.url, data={ 'user': CONF.anchor.username, 'secret': CONF.anchor.password, 'encoding': 'pem', 'csr': csr}) if r.status_code != 200: LOG.debug('Anchor returned: %s', r.content) raise AnchorException(_("Anchor returned Status Code : " "{0}").format(str(r.status_code))) return r.content except Exception as e: LOG.error("Unable to sign certificate.") raise exceptions.CertificateGenerationException(msg=e)
def delete(self, id, cascade=False): """Deletes a load balancer.""" context = pecan.request.context.get('octavia_context') cascade = strutils.bool_from_string(cascade) db_lb = self._get_db_lb(context.session, id, show_deleted=False) self._auth_validate_action(context, db_lb.project_id, constants.RBAC_DELETE) # Load the driver early as it also provides validation driver = driver_factory.get_driver(db_lb.provider) with db_api.get_lock_session() as lock_session: if (db_lb.listeners or db_lb.pools) and not cascade: msg = _("Cannot delete Load Balancer %s - " "it has children") % id LOG.warning(msg) raise exceptions.ValidationException(detail=msg) self._test_lb_status(lock_session, id, lb_status=constants.PENDING_DELETE) LOG.info("Sending delete Load Balancer %s to provider %s", id, driver.name) provider_loadbalancer = ( driver_utils.db_loadbalancer_to_provider_loadbalancer(db_lb)) driver_utils.call_provider(driver.name, driver.loadbalancer_delete, provider_loadbalancer, cascade)
def _delete_vip_security_group(self, sec_grp): """Deletes a security group in neutron. Retries upon an exception because removing a security group from a neutron port does not happen immediately. """ attempts = 0 while attempts <= CONF.networking.max_retries: try: self.neutron_client.delete_security_group(sec_grp) LOG.info("Deleted security group %s", sec_grp) return except neutron_client_exceptions.NotFound: LOG.info("Security group %s not found, will assume it is " "already deleted", sec_grp) return except Exception: LOG.warning("Attempt %(attempt)s to remove security group " "%(sg)s failed.", {'attempt': attempts + 1, 'sg': sec_grp}) attempts += 1 time.sleep(CONF.networking.retry_interval) message = _("All attempts to remove security group {0} have " "failed.").format(sec_grp) LOG.exception(message) raise base.DeallocateVIPException(message)
def _get_provider(self, session, load_balancer): """Decide on the provider for this load balancer.""" provider = None if not isinstance(load_balancer.flavor_id, wtypes.UnsetType): try: provider = self.repositories.flavor.get_flavor_provider( session, load_balancer.flavor_id) except sa_exception.NoResultFound: raise exceptions.ValidationException( detail=_("Invalid flavor_id.")) # No provider specified and no flavor specified, use conf default if (isinstance(load_balancer.provider, wtypes.UnsetType) and not provider): provider = CONF.api_settings.default_provider_driver # Both provider and flavor specified, they must match elif (not isinstance(load_balancer.provider, wtypes.UnsetType) and provider): if provider != load_balancer.provider: raise exceptions.ProviderFlavorMismatchError( flav=load_balancer.flavor_id, prov=load_balancer.provider) # No flavor, but provider, use the provider specified elif not provider: provider = load_balancer.provider # Otherwise, use the flavor provider we found above return provider
def plug_port(self, amphora, port): try: interface = self.compute.attach_network_or_port( compute_id=amphora.compute_id, network_id=None, ip_address=None, port_id=port.id) plugged_interface = self._nova_interface_to_octavia_interface( amphora.compute_id, interface) except nova_client_exceptions.NotFound as e: if 'Instance' in str(e): raise base.AmphoraNotFound(str(e)) if 'Network' in str(e): raise base.NetworkNotFound(str(e)) raise base.PlugNetworkException(str(e)) except nova_client_exceptions.Conflict: LOG.info('Port %(portid)s is already plugged, ' 'skipping', {'portid': port.id}) plugged_interface = n_data_models.Interface( compute_id=amphora.compute_id, network_id=port.network_id, port_id=port.id, fixed_ips=port.fixed_ips) except Exception: message = _('Error plugging amphora (compute_id: ' '{compute_id}) into port ' '{port_id}.').format( compute_id=amphora.compute_id, port_id=port.id) LOG.exception(message) raise base.PlugNetworkException(message) return plugged_interface
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 call_provider(provider, driver_method, *args, **kwargs): """Wrap calls to the provider driver to handle driver errors. This allows Octavia to return user friendly errors when a provider driver has an issue. :param driver_method: Method in the driver to call. :raises ProviderDriverError: Catch all driver error. :raises ProviderNotImplementedError: The driver doesn't support this action. :raises ProviderUnsupportedOptionError: The driver doesn't support a provided option. """ try: return driver_method(*args, **kwargs) except (driver_exceptions.DriverError, lib_exceptions.DriverError) as e: LOG.exception("Provider '%s' raised a driver error: %s", provider, e.operator_fault_string) raise exceptions.ProviderDriverError(prov=provider, user_msg=e.user_fault_string) except (driver_exceptions.NotImplementedError, lib_exceptions.NotImplementedError, NotImplementedError) as e: op_fault_string = ( e.operator_fault_string if hasattr(e, "operator_fault_string") else _("This feature is not implemented by this provider.")) usr_fault_string = ( e.user_fault_string if hasattr(e, "user_fault_string") else _("This feature is not implemented by the provider.")) LOG.info("Provider '%s' raised a not implemented error: %s", provider, op_fault_string) raise exceptions.ProviderNotImplementedError( prov=provider, user_msg=usr_fault_string) except (driver_exceptions.UnsupportedOptionError, lib_exceptions.UnsupportedOptionError) as e: LOG.info("Provider '%s' raised an unsupported option error: " "%s", provider, e.operator_fault_string) raise exceptions.ProviderUnsupportedOptionError( prov=provider, user_msg=e.user_fault_string) except Exception as e: LOG.exception("Provider '%s' raised an unknown error: %s", provider, e) raise exceptions.ProviderDriverError(prov=provider, user_msg=e)
def _get_resource(self, resource_type, resource_id): try: resource = getattr(self.neutron_client, 'show_%s' % resource_type)(resource_id) return getattr(utils, 'convert_%s_dict_to_model' % resource_type)(resource) except neutron_client_exceptions.NotFound: message = _('{resource_type} not found ' '({resource_type} id: {resource_id}).').format( resource_type=resource_type, resource_id=resource_id) raise getattr(base, '%sNotFound' % ''.join( [w.capitalize() for w in resource_type.split('_')]))(message) except Exception: message = _('Error retrieving {resource_type} ' '({resource_type} id: {resource_id}.').format( resource_type=resource_type, resource_id=resource_id) LOG.exception(message) raise base.NetworkException(message)
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 _add_vip_address_pair(self, port_id, vip_address): try: self._add_allowed_address_pair_to_port(port_id, vip_address) except neutron_client_exceptions.PortNotFoundClient as e: raise base.PortNotFound(str(e)) except Exception: message = _('Error adding allowed address pair {ip} ' 'to port {port_id}.').format(ip=vip_address, port_id=port_id) LOG.exception(message) raise base.PlugVIPException(message)
def do_upgrade(config, cmd): if not CONF.command.revision and not CONF.command.delta: raise SystemExit(_('You must provide a revision or relative delta')) revision = CONF.command.revision or '' if '-' in revision: raise SystemExit(_('Negative relative revision (downgrade) not ' 'supported')) delta = CONF.command.delta if delta: if '+' in revision: raise SystemExit(_('Use either --delta or relative revision, ' 'not both')) if delta < 0: raise SystemExit(_('Negative delta (downgrade) not supported')) revision = '%s+%d' % (revision, delta) do_alembic_command(config, cmd, revision, sql=CONF.command.sql)
def _test_lb_and_listener_statuses(self, session, lb_id, listener_ids): """Verify load balancer is in a mutable state.""" # We need to verify that any listeners referencing this pool are also # mutable if not self.repositories.test_and_set_lb_and_listeners_prov_status( session, lb_id, constants.PENDING_UPDATE, constants.PENDING_UPDATE, listener_ids=listener_ids): LOG.info("Pool cannot be created or modified because the Load " "Balancer is in an immutable state") raise exceptions.ImmutableObject(resource=_('Load Balancer'), id=lb_id)
def _validate_pool(self, session, lb_id, pool_id, listener_protocol): """Validate pool given exists on same load balancer as listener.""" db_pool = self.repositories.pool.get( session, load_balancer_id=lb_id, id=pool_id) if not db_pool: raise exceptions.NotFound( resource=data_models.Pool._name(), id=pool_id) if (db_pool.protocol == constants.PROTOCOL_UDP and db_pool.protocol != listener_protocol): msg = _("Listeners of type %s can only have pools of " "type UDP.") % constants.PROTOCOL_UDP raise exceptions.ValidationException(detail=msg)
def check_session_persistence(SP_dict): try: if SP_dict['cookie_name']: if SP_dict['type'] != constants.SESSION_PERSISTENCE_APP_COOKIE: raise exceptions.ValidationException(detail=_( 'Field "cookie_name" can only be specified with session ' 'persistence of type "APP_COOKIE".')) bad_cookie_name = re.compile(r'[\x00-\x20\x22\x28-\x29\x2c\x2f' r'\x3a-\x40\x5b-\x5d\x7b\x7d\x7f]+') valid_chars = re.compile(r'[\x00-\xff]+') if (bad_cookie_name.search(SP_dict['cookie_name']) or not valid_chars.search(SP_dict['cookie_name'])): raise exceptions.ValidationException(detail=_( 'Supplied "cookie_name" is invalid.')) if (SP_dict['type'] == constants.SESSION_PERSISTENCE_APP_COOKIE and not SP_dict['cookie_name']): raise exceptions.ValidationException(detail=_( 'Field "cookie_name" must be specified when using the ' '"APP_COOKIE" session persistence type.')) except exceptions.ValidationException: raise except Exception: raise exceptions.ValidationException(detail=_( 'Invalid session_persistence provided.'))
def _validate_vip_request_object(self, load_balancer): allowed_network_objects = [] if CONF.networking.allow_vip_port_id: allowed_network_objects.append('vip_port_id') if CONF.networking.allow_vip_network_id: allowed_network_objects.append('vip_network_id') if CONF.networking.allow_vip_subnet_id: allowed_network_objects.append('vip_subnet_id') msg = _("use of %(object)s is disallowed by this deployment's " "configuration.") if (load_balancer.vip_port_id and not CONF.networking.allow_vip_port_id): raise exceptions.ValidationException( detail=msg % {'object': 'vip_port_id'}) if (load_balancer.vip_network_id and not CONF.networking.allow_vip_network_id): raise exceptions.ValidationException( detail=msg % {'object': 'vip_network_id'}) if (load_balancer.vip_subnet_id and not CONF.networking.allow_vip_subnet_id): raise exceptions.ValidationException( detail=msg % {'object': 'vip_subnet_id'}) if not (load_balancer.vip_port_id or load_balancer.vip_network_id or load_balancer.vip_subnet_id): raise exceptions.VIPValidationException( objects=', '.join(allowed_network_objects)) # Validate the port id if load_balancer.vip_port_id: self._validate_port_and_fill_or_validate_subnet(load_balancer) # If no port id, validate the network id (and subnet if provided) elif load_balancer.vip_network_id: self._validate_network_and_fill_or_validate_subnet(load_balancer) # Validate just the subnet id elif load_balancer.vip_subnet_id: subnet = validate.subnet_exists( subnet_id=load_balancer.vip_subnet_id) load_balancer.vip_network_id = subnet.network_id if load_balancer.vip_qos_policy_id: validate.qos_policy_exists( qos_policy_id=load_balancer.vip_qos_policy_id) validate.network_allowed_by_config(load_balancer.vip_network_id)
def allocate_vip(self, load_balancer): if load_balancer.vip.port_id: LOG.info('Port %s already exists. Nothing to be done.', load_balancer.vip.port_id) port = self.get_port(load_balancer.vip.port_id) return self._port_to_vip(port, load_balancer) fixed_ip = {} if load_balancer.vip.subnet_id: fixed_ip['subnet_id'] = load_balancer.vip.subnet_id if load_balancer.vip.ip_address: fixed_ip['ip_address'] = load_balancer.vip.ip_address # Make sure we are backward compatible with older neutron if self._check_extension_enabled(PROJECT_ID_ALIAS): project_id_key = 'project_id' else: project_id_key = 'tenant_id' # It can be assumed that network_id exists port = {'port': {'name': 'octavia-lb-' + load_balancer.id, 'network_id': load_balancer.vip.network_id, 'admin_state_up': False, 'device_id': 'lb-{0}'.format(load_balancer.id), 'device_owner': OCTAVIA_OWNER, project_id_key: load_balancer.project_id}} if fixed_ip: port['port']['fixed_ips'] = [fixed_ip] try: new_port = self.neutron_client.create_port(port) except Exception as e: message = _('Error creating neutron port on network ' '{network_id}.').format( network_id=load_balancer.vip.network_id) LOG.exception(message) raise base.AllocateVIPException( message, orig_msg=getattr(e, 'message', None), orig_code=getattr(e, 'status_code', None), ) new_port = utils.convert_port_dict_to_model(new_port) return self._port_to_vip(new_port, load_balancer)
def _wait_for_pool_session_persistence(self, pool_id, sp_type=None): interval_time = 1 timeout = 10 end_time = time.time() + timeout while time.time() < end_time: pool = self.pools_client.get_pool(self.load_balancer['id'], pool_id) sp = pool.get('session_persistence', None) if (not (sp_type or sp) or pool['session_persistence']['type'] == sp_type): return pool time.sleep(interval_time) raise Exception( _("Wait for pool ran for {timeout} seconds and did " "not observe {pool_id} update session persistence type " "to {type}.").format( timeout=timeout, pool_id=pool_id, type=sp_type))
def plug_network(self, compute_id, network_id, ip_address=None): try: interface = self.compute.attach_network_or_port( compute_id=compute_id, network_id=network_id, ip_address=ip_address) except nova_client_exceptions.NotFound as e: if 'Instance' in str(e): raise base.AmphoraNotFound(str(e)) if 'Network' in str(e): raise base.NetworkNotFound(str(e)) raise base.PlugNetworkException(str(e)) except Exception: message = _('Error plugging amphora (compute_id: {compute_id}) ' 'into network {network_id}.').format( compute_id=compute_id, network_id=network_id) LOG.exception(message) raise base.PlugNetworkException(message) return self._nova_interface_to_octavia_interface(compute_id, interface)
class ProviderDriverError(APIException): msg = _("Provider '%(prov)s' reports error: %(user_msg)s") code = 500
def qos_extension_enabled(network_driver): if not network_driver.qos_enabled(): raise exceptions.ValidationException( detail=_("VIP QoS policy is not allowed in this deployment."))
class ProviderNotImplementedError(APIException): msg = _("Provider '%(prov)s' does not support a requested action: " "%(user_msg)s") code = 501
class NetworkServiceError(OctaviaException): message = _('The networking service had a failure: %(net_error)s')
class ProviderFlavorMismatchError(APIException): msg = _("Flavor '%(flav)s' is not compatible with provider '%(prov)s'") code = 400
class MissingVIPSecurityGroup(OctaviaException): message = _('VIP security group is missing for load balancer: %(lb_id)s')
class VIPValidationException(APIException): msg = _('Validation failure: VIP must contain one of: %(objects)s.') code = 400
class InvalidIPAddress(APIException): msg = _('The IP Address %(ip_addr)s is invalid.') code = 400
class ProviderNotFound(APIException): msg = _("Provider '%(prov)s' was not found.") code = 501
class ProviderUnsupportedOptionError(APIException): msg = _("Provider '%(prov)s' does not support a requested option: " "%(user_msg)s") code = 501
class DisabledOption(APIException): msg = _("The selected %(option)s is not allowed in this deployment: " "%(value)s") code = 400
class InvalidOption(APIException): msg = _("%(value)s is not a valid option for %(option)s") code = 400
class PolicyForbidden(APIException): msg = _("Policy does not allow this request to be performed.") code = 403
class InputFileError(OctaviaException): message = _('Error with file %(file_name)s. Reason: %(reason)s')
class InvalidSortDirection(APIException): msg = _("Supplied sort direction '%(key)s' is not valid.") code = 400
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() if (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')
class ProviderNotEnabled(APIException): msg = _("Provider '%(prov)s' is not enabled.") code = 400
class VolumeDeleteException(OctaviaException): message = _('Failed to delete volume instance.')
class InvalidLimit(APIException): msg = _("Supplied pagination limit '%(key)s' is not valid.") code = 400
class InvalidMarker(APIException): msg = _("Supplied pagination marker '%(key)s' is not valid.") code = 400
def _validate_listener_PUT(self, listener, db_listener): # TODO(rm_work): Do we need something like this? What do we do on an # empty body for a PUT? if not listener: raise exceptions.ValidationException( detail='No listener object supplied.') # Check for UDP compatibility if (db_listener.protocol == constants.PROTOCOL_UDP and self._is_tls_or_insert_header(listener.to_dict())): raise exceptions.ValidationException( detail=_("%s protocol listener does not support TLS or header " "insertion.") % constants.PROTOCOL_UDP) # Check for certs when not TERMINATED_HTTPS if (db_listener.protocol != constants.PROTOCOL_TERMINATED_HTTPS and self._has_tls_container_refs(listener.to_dict())): raise exceptions.ValidationException( detail=_( "Certificate container references are only allowed on " "%s protocol listeners.") % constants.PROTOCOL_TERMINATED_HTTPS) # Make sure we have a client CA cert if they enable client auth if ((listener.client_authentication != wtypes.Unset and listener.client_authentication != constants.CLIENT_AUTH_NONE) and not (db_listener.client_ca_tls_certificate_id or listener.client_ca_tls_container_ref)): raise exceptions.ValidationException(detail=_( "Client authentication setting %s requires a client CA " "container reference.") % listener.client_authentication) if listener.insert_headers: self._validate_insert_headers(list(listener.insert_headers.keys()), db_listener.protocol) sni_containers = listener.sni_container_refs or [] tls_refs = [sni for sni in sni_containers] if listener.default_tls_container_ref: tls_refs.append(listener.default_tls_container_ref) self._validate_tls_refs(tls_refs) ca_ref = None if (listener.client_ca_tls_container_ref and listener.client_ca_tls_container_ref != wtypes.Unset): ca_ref = listener.client_ca_tls_container_ref elif db_listener.client_ca_tls_certificate_id: ca_ref = db_listener.client_ca_tls_certificate_id crl_ref = None if (listener.client_crl_container_ref and listener.client_crl_container_ref != wtypes.Unset): crl_ref = listener.client_crl_container_ref elif db_listener.client_crl_container_id: crl_ref = db_listener.client_crl_container_id if crl_ref and not ca_ref: raise exceptions.ValidationException( detail=_("A client authentication CA reference is required to " "specify a client authentication revocation list.")) if ca_ref or crl_ref: self._validate_client_ca_and_crl_refs(ca_ref, crl_ref)
class VolumeGetException(OctaviaException): message = _('Failed to retrieve volume instance.')
def post(self, pool_): """Creates a pool on a load balancer or listener. Note that this can optionally take a listener_id with which the pool should be associated as the listener's default_pool. If specified, the pool creation will fail if the listener specified already has a default_pool. """ # For some API requests the listener_id will be passed in the # pool_dict: pool = pool_.pool context = pecan.request.context.get('octavia_context') if pool.loadbalancer_id: pool.project_id = self._get_lb_project_id(context.session, pool.loadbalancer_id) elif pool.listener_id: listener = self.repositories.listener.get( context.session, id=pool.listener_id) pool.project_id = listener.project_id pool.loadbalancer_id = listener.load_balancer_id else: msg = _("Must provide at least one of: " "loadbalancer_id, listener_id") raise exceptions.ValidationException(detail=msg) self._auth_validate_action(context, pool.project_id, constants.RBAC_POST) lock_session = db_api.get_session(autocommit=False) try: if self.repositories.check_quota_met( context.session, lock_session, data_models.Pool, pool.project_id): raise exceptions.QuotaException listener_repo = self.repositories.listener pool_dict = db_prepare.create_pool( pool.to_dict(render_unsets=True)) listener_id = pool_dict.pop('listener_id', None) if listener_id: if listener_repo.has_default_pool(lock_session, listener_id): raise exceptions.DuplicatePoolEntry() self._test_lb_and_listener_statuses( lock_session, lb_id=pool_dict['load_balancer_id'], listener_ids=[listener_id] if listener_id else []) db_pool = self._validate_create_pool( lock_session, pool_dict, listener_id) lock_session.commit() except Exception: with excutils.save_and_reraise_exception(): lock_session.rollback() return self._send_pool_to_handler(context.session, db_pool, listener_id=listener_id)
LOG = logging.getLogger(__name__) EXTRA_LOG_LEVEL_DEFAULTS = [ 'neutronclient.v2_0.client=INFO', ] TLS_PROTOCOL_CHOICES = [ p[9:].replace('_', '.') for p in ssl._PROTOCOL_NAMES.values() ] core_opts = [ cfg.HostnameOpt('host', default=utils.get_hostname(), sample_default='<server-hostname.example.com>', help=_("The hostname Octavia is running on")), cfg.StrOpt('octavia_plugins', default='hot_plug_plugin', help=_("Name of the controller plugin to use")), ] api_opts = [ cfg.IPOpt('bind_host', default='127.0.0.1', help=_("The host IP to bind to")), cfg.PortOpt('bind_port', default=9876, help=_("The port to bind to")), cfg.StrOpt( 'auth_strategy', default=constants.KEYSTONE, choices=[constants.NOAUTH, constants.KEYSTONE, constants.TESTING], help=_("The auth strategy for API requests.")),
class L7RuleValidation(APIException): msg = _("Error parsing L7Rule: %(error)s") code = 400
class ObjectInUse(APIException): msg = _("%(object)s %(id)s is in use and cannot be modified.") code = 409
def post(self, load_balancer): """Creates a load balancer.""" load_balancer = load_balancer.loadbalancer context = pecan.request.context.get('octavia_context') if not load_balancer.project_id and context.project_id: load_balancer.project_id = context.project_id if not load_balancer.project_id: raise exceptions.ValidationException(detail=_( "Missing project ID in request where one is required. " "An administrator should check the keystone settings " "in the Octavia configuration.")) self._auth_validate_action(context, load_balancer.project_id, constants.RBAC_POST) self._validate_vip_request_object(load_balancer) self._validate_flavor(context.session, load_balancer) self._validate_availability_zone(context.session, load_balancer) provider = self._get_provider(context.session, load_balancer) # 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.LoadBalancer, load_balancer.project_id): raise exceptions.QuotaException( resource=data_models.LoadBalancer._name()) db_lb, db_pools, db_lists = None, None, None lb_dict = db_prepare.create_load_balancer( load_balancer.to_dict(render_unsets=False)) vip_dict = lb_dict.pop('vip', {}) # Make sure we store the right provider in the DB lb_dict['provider'] = driver.name # NoneType can be weird here, have to force type a second time listeners = lb_dict.pop('listeners', []) or [] pools = lb_dict.pop('pools', []) or [] flavor_dict = self._apply_flavor_to_lb_dict( lock_session, driver, lb_dict) az_dict = self._validate_and_return_az_dict( lock_session, driver, lb_dict) db_lb = self.repositories.create_load_balancer_and_vip( lock_session, lb_dict, vip_dict) # Pass the flavor dictionary through for the provider drivers # This is a "virtual" lb_dict item that includes the expanded # flavor dict instead of just the flavor_id we store in the DB. lb_dict['flavor'] = flavor_dict # Do the same with the availability_zone dict lb_dict['availability_zone'] = az_dict # See if the provider driver wants to create the VIP port octavia_owned = False try: provider_vip_dict = driver_utils.vip_dict_to_provider_dict( vip_dict) vip_dict = driver_utils.call_provider(driver.name, driver.create_vip_port, db_lb.id, db_lb.project_id, provider_vip_dict) vip = driver_utils.provider_vip_dict_to_vip_obj(vip_dict) except exceptions.ProviderNotImplementedError: # create vip port if not exist, driver didn't want to create # the VIP port vip = self._create_vip_port_if_not_exist(db_lb) LOG.info('Created VIP port %s for provider %s.', vip.port_id, driver.name) # If a port_id wasn't passed in and we made it this far # we created the VIP if 'port_id' not in vip_dict or not vip_dict['port_id']: octavia_owned = True self.repositories.vip.update(lock_session, db_lb.id, ip_address=vip.ip_address, port_id=vip.port_id, network_id=vip.network_id, subnet_id=vip.subnet_id, octavia_owned=octavia_owned) if listeners or pools: db_pools, db_lists = self._graph_create( context.session, lock_session, db_lb, listeners, pools) # Prepare the data for the driver data model driver_lb_dict = driver_utils.lb_dict_to_provider_dict( lb_dict, vip, db_pools, db_lists) # Dispatch to the driver LOG.info("Sending create Load Balancer %s to provider %s", db_lb.id, driver.name) driver_utils.call_provider( driver.name, driver.loadbalancer_create, driver_dm.LoadBalancer.from_dict(driver_lb_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() db_lb = self._get_db_lb(context.session, db_lb.id) result = self._convert_db_to_type(db_lb, lb_types.LoadBalancerFullResponse) return lb_types.LoadBalancerFullRootResponse(loadbalancer=result)
from oslo_db import options as db_options from oslo_log import log as logging import oslo_messaging as messaging from octavia.certificates.common import local from octavia.common import constants from octavia.common import utils from octavia.i18n import _ from octavia import version LOG = logging.getLogger(__name__) core_opts = [ cfg.HostnameOpt('host', default=utils.get_hostname(), help=_("The hostname Octavia is running on")), cfg.StrOpt('octavia_plugins', default='hot_plug_plugin', help=_("Name of the controller plugin to use")), ] api_opts = [ cfg.IPOpt('bind_host', default='127.0.0.1', help=_("The host IP to bind to")), cfg.PortOpt('bind_port', default=9876, help=_("The port to bind to")), cfg.StrOpt( 'auth_strategy', default=constants.KEYSTONE, choices=[constants.NOAUTH, constants.KEYSTONE, constants.TESTING], help=_("The auth strategy for API requests.")),
class NotFound(APIException): msg = _('%(resource)s %(id)s not found.') code = 404
class InvalidSortKey(APIException): msg = _("Supplied sort key '%(key)s' is not valid.") code = 400