def load_class_by_alias_or_classname(namespace, name): """Load class using stevedore alias or the class name :param namespace: namespace where the alias is defined :param name: alias or class name of the class to be loaded :returns class if calls can be loaded :raises ImportError if class cannot be loaded """ if not name: LOG.error(_LE("Alias or class name is not set")) raise ImportError(_("Class not found.")) try: # Try to resolve class by alias mgr = driver.DriverManager(namespace, name) class_to_load = mgr.driver except RuntimeError: e1_info = sys.exc_info() # Fallback to class name try: class_to_load = importutils.import_class(name) except (ImportError, ValueError): LOG.error(_LE("Error loading class by alias"), exc_info=e1_info) LOG.error(_LE("Error loading class by class name"), exc_info=True) raise ImportError(_("Class not found.")) return class_to_load
def get_sorts(request, attr_info): """Extract sort_key and sort_dir from request. Return as: [(key1, value1), (key2, value2)] """ sort_keys = list_args(request, "sort_key") sort_dirs = list_args(request, "sort_dir") if len(sort_keys) != len(sort_dirs): msg = _("The number of sort_keys and sort_dirs must be same") raise exc.HTTPBadRequest(explanation=msg) valid_dirs = [constants.SORT_DIRECTION_ASC, constants.SORT_DIRECTION_DESC] absent_keys = [x for x in sort_keys if x not in attr_info] if absent_keys: msg = _("%s is invalid attribute for sort_keys") % absent_keys raise exc.HTTPBadRequest(explanation=msg) invalid_dirs = [x for x in sort_dirs if x not in valid_dirs] if invalid_dirs: msg = (_("%(invalid_dirs)s is invalid value for sort_dirs, " "valid value is '%(asc)s' and '%(desc)s'") % { 'invalid_dirs': invalid_dirs, 'asc': constants.SORT_DIRECTION_ASC, 'desc': constants.SORT_DIRECTION_DESC }) raise exc.HTTPBadRequest(explanation=msg) return list( zip(sort_keys, [x == constants.SORT_DIRECTION_ASC for x in sort_dirs]))
def fill_default_value(attr_info, res_dict, exc_cls=ValueError, check_allow_post=True): for attr, attr_vals in six.iteritems(attr_info): if attr_vals['allow_post']: if 'default' not in attr_vals and attr not in res_dict: msg = _("Failed to parse request. Required " "attribute '%s' not specified") % attr raise exc_cls(msg) res_dict[attr] = res_dict.get(attr, attr_vals.get('default')) elif check_allow_post: if attr in res_dict: msg = _("Attribute '%s' not allowed in POST") % attr raise exc_cls(msg)
def show(self, request, id, **kwargs): """Returns detailed information about the requested entity.""" try: # NOTE(salvatore-orlando): The following ensures that fields # which are needed for authZ policy validation are not stripped # away by the plugin before returning. field_list, added_fields = self._do_field_list( api_common.list_args(request, "fields")) parent_id = kwargs.get(self._parent_id_name) # Ensure policy engine is initialized policy.init() return { self._resource: self._view(request.context, self._item(request, id, do_authz=True, field_list=field_list, parent_id=parent_id), fields_to_strip=added_fields) } except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg)
def _handle_action(request, id, **kwargs): arg_list = [request.context, id] # Ensure policy engine is initialized policy.init() # Fetch the resource and verify if the user can access it try: parent_id = kwargs.get(self._parent_id_name) resource = self._item(request, id, do_authz=True, field_list=None, parent_id=parent_id) except oslo_policy.PolicyNotAuthorized: msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) body = kwargs.pop('body', None) # Explicit comparison with None to distinguish from {} if body is not None: arg_list.append(body) # It is ok to raise a 403 because accessibility to the # object was checked earlier in this method policy.enforce(request.context, name, resource, pluralized=self._collection) ret_value = getattr(self._plugin, name)(*arg_list, **kwargs) # It is simply impossible to predict whether one of this # actions alters resource usage. For instance a tenant port # is created when a router interface is added. Therefore it is # important to mark as dirty resources whose counters have # been altered by this operation resource_registry.set_resources_dirty(request.context) return ret_value
def populate_project_info(attributes): """ Ensure that both project_id and tenant_id attributes are present. If either project_id or tenant_id is present in attributes then ensure that both are present. If neither are present then attributes is not updated. :param attributes: a dictionary of resource/API attributes :type attributes: dict :return: the updated attributes dictionary :rtype: dict """ if 'tenant_id' in attributes and 'project_id' not in attributes: # TODO(HenryG): emit a deprecation warning here attributes['project_id'] = attributes['tenant_id'] elif 'project_id' in attributes and 'tenant_id' not in attributes: # Backward compatibility for code still using tenant_id attributes['tenant_id'] = attributes['project_id'] if attributes.get('project_id') != attributes.get('tenant_id'): msg = _("'project_id' and 'tenant_id' do not match") raise webob.exc.HTTPBadRequest(msg) return attributes
class SecurityGroupInUse(nexception.InUse): message = _("Security Group %(id)s %(reason)s.") def __init__(self, **kwargs): if 'reason' not in kwargs: kwargs['reason'] = _("in use") super(SecurityGroupInUse, self).__init__(**kwargs)
def show(self, request, id): # NOTE(dprince): the extensions alias is used as the 'id' for show ext = self.extension_manager.extensions.get(id, None) if not ext: raise webob.exc.HTTPNotFound( _("Extension with alias %s does not exist") % id) return dict(extension=self._translate(ext))
def _ensure_driver_unique(self, driver): for k, v in self.providers.items(): if v['driver'] == driver: msg = (_("Driver %s is not unique across providers") % driver) LOG.error(msg) raise n_exc.Invalid(msg)
def verify_attributes(res_dict, attr_info): populate_project_info(attr_info) extra_keys = set(res_dict.keys()) - set(attr_info.keys()) if extra_keys: msg = _("Unrecognized attribute(s) '%s'") % ', '.join(extra_keys) raise webob.exc.HTTPBadRequest(msg)
def _validate_privileges(context, res_dict): if ('project_id' in res_dict and res_dict['project_id'] != context.project_id and not context.is_admin): msg = _("Specifying 'project_id' or 'tenant_id' other than " "authenticated project in request requires admin privileges") raise webob.exc.HTTPBadRequest(msg)
def set_tracked_resource(self, resource_name, model_class, override=False): # Do not do anything if tracking is disabled by config if not cfg.CONF.QUOTAS.track_quota_usage: return if isinstance(self._resources.get(resource_name), resource.CountableResource): raise RuntimeError( _("Resource %s is already registered as a " "countable resource.") % resource_name) current_model_class = self._tracked_resource_mappings.setdefault( resource_name, model_class) # Check whether setdefault also set the entry in the dict if current_model_class != model_class: LOG.debug( "A model class is already defined for %(resource)s: " "%(current_model_class)s. Override:%(override)s", { 'resource': resource_name, 'current_model_class': current_model_class, 'override': override }) if override: self._tracked_resource_mappings[resource_name] = model_class LOG.debug("Tracking information for resource: %s configured", resource_name)
def __call__(self, environ, start_response): r"""Subclasses will probably want to implement __call__ like this: @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): # Any of the following objects work as responses: # Option 1: simple string res = 'message\n' # Option 2: a nicely formatted HTTP exception page res = exc.HTTPForbidden(explanation='Nice try') # Option 3: a webob Response object (in case you need to play with # headers, or you want to be treated like an iterable, or or or) res = Response(); res.app_iter = open('somefile') # Option 4: any wsgi app to be run next res = self.application # Option 5: you can get a Response object for a wsgi app, too, to # play with headers etc res = req.get_response(self.application) # You can then just return your response... return res # ... or set req.response and return None. req.response = res See the end of http://pythonpaste.org/webob/modules/dec.html for more info. """ raise NotImplementedError(_('You must implement __call__'))
def inner(self, context, *args, **kwargs): # FIXME(kevinbenton): get rid of all uses of this flag if (context.session.is_active and getattr(context, 'GUARD_TRANSACTION', True)): raise RuntimeError( _("Method %s cannot be called within a " "transaction.") % f) return f(self, context, *args, **kwargs)
def convert_to_uuid_list_or_none(value_list): if value_list is None: return for sg_id in value_list: if not uuidutils.is_uuid_like(sg_id): msg = _("'%s' is not an integer or uuid") % sg_id raise nexception.InvalidInput(error_message=msg) return value_list
class NetworkVlanRangeError(e.NeutronException): message = _("Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'.") def __init__(self, **kwargs): # Convert vlan_range tuple to 'start:end' format for display if isinstance(kwargs['vlan_range'], tuple): kwargs['vlan_range'] = "%d:%d" % kwargs['vlan_range'] super(NetworkVlanRangeError, self).__init__(**kwargs)
def delete(self, request, id, **kwargs): """Deletes the specified entity.""" if request.body: msg = _('Request body is not supported in DELETE.') raise webob.exc.HTTPBadRequest(msg) self._notifier.info(request.context, self._resource + '.delete.start', {self._resource + '_id': id}) return self._delete(request, id, **kwargs)
def __call__(self, request): """WSGI method that controls (de)serialization and method dispatch.""" LOG.info(_LI("%(method)s %(url)s"), { "method": request.method, "url": request.url }) try: action, args, accept = self.deserializer.deserialize(request) except exception.InvalidContentType: msg = _("Unsupported Content-Type") LOG.exception(_LE("InvalidContentType: %s"), msg) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except n_exc.MalformedRequestBody: msg = _("Malformed request body") LOG.exception(_LE("MalformedRequestBody: %s"), msg) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) try: action_result = self.dispatch(request, action, args) except webob.exc.HTTPException as ex: LOG.info(_LI("HTTP exception thrown: %s"), ex) action_result = Fault(ex, self._fault_body_function) except Exception: LOG.exception(_LE("Internal error")) # Do not include the traceback to avoid returning it to clients. action_result = Fault(webob.exc.HTTPServerError(), self._fault_body_function) if isinstance(action_result, dict) or action_result is None: response = self.serializer.serialize(action_result, accept, action=action) else: response = action_result try: LOG.info(_LI("%(url)s returned with HTTP %(status)d"), dict(url=request.url, status=response.status_int)) except AttributeError as e: LOG.info(_LI("%(url)s returned a fault: %(exception)s"), dict(url=request.url, exception=e)) return response
class NeutronPrimaryKeyMissing(exceptions.BadRequest): message = _("For class %(object_type)s missing primary keys: " "%(missing_keys)s") def __init__(self, object_class, missing_keys): super(NeutronPrimaryKeyMissing, self).__init__( object_type=reflection.get_class_name(object_class, fully_qualified=False), missing_keys=missing_keys)
def _ensure_default_unique(self, type, default): if not default: return for k, v in self.providers.items(): if k[0] == type and v['default']: msg = _("Multiple default providers " "for service %s") % type LOG.error(msg) raise n_exc.Invalid(msg)
def port_rule_masking(port_min, port_max): """Translate a range [port_min, port_max] into a set of bitwise matches. Each match has the form 'port/mask'. The port and mask are 16-bit numbers written in hexadecimal prefixed by 0x. Each 1-bit in mask requires that the corresponding bit in port must match. Each 0-bit in mask causes the corresponding bit to be ignored. """ # Let binary representation of port_min and port_max be n bits long and # have first m bits in common, 0 <= m <= n. # If remaining (n - m) bits of given ports define 2^(n-m) values, then # [port_min, port_max] range is covered by a single rule. # For example: # n = 6 # port_min = 16 (binary 010000) # port_max = 23 (binary 010111) # Ports have m=3 bits in common with the remaining (n-m)=3 bits # covering range [0, 2^3), which equals to a single 010xxx rule. The algo # will return [0x0010/fff8]. # Else [port_min, port_max] range will be split into 2: range [port_min, T) # and [T, port_max]. Let p_m be the common part of port_min and port_max # with other (n-m) bits set to 0. Then T = p_m | 1 << (n-m-1). # For example: # n = 7 # port_min = 40 (binary 0101000) # port_max = 105 (binary 1101001) # Ports have m=0 bits in common, p_m=000000. Then T=1000000 and the # initial range [40, 105] is divided into [40, 64) and [64, 105]. # Each of the ranges will be processed separately, then the generated rules # will be merged. # Check port_max >= port_min. if port_max < port_min: raise ValueError(_("'port_max' is smaller than 'port_min'")) bitdiff = port_min ^ port_max if bitdiff == 0: # port_min == port_max return [_hex_format(port_min)] # for python3.x, bit_length could be used here top_bit = 1 while top_bit <= bitdiff: top_bit <<= 1 if (port_min & (top_bit - 1) == 0 and port_max & (top_bit - 1) == top_bit - 1): # special case, range of 2^k ports is covered return [_hex_format(port_min, top_bit - 1)] top_bit >>= 1 rules = [] rules.extend(_gen_rules_port_min(port_min, top_bit)) rules.extend(_gen_rules_port_max(port_max, top_bit)) return rules
class NeutronDbObjectDuplicateEntry(exceptions.Conflict): message = _("Failed to create a duplicate %(object_type)s: " "for attribute(s) %(attributes)s with value(s) %(values)s") def __init__(self, object_class, db_exception): super(NeutronDbObjectDuplicateEntry, self).__init__( object_type=reflection.get_class_name(object_class, fully_qualified=False), attributes=db_exception.columns, values=db_exception.value)
def _get_limit_param(request): """Extract integer limit from request or fail.""" try: limit = int(request.GET.get('limit', 0)) if limit >= 0: return limit except ValueError: pass msg = _("Limit must be an integer 0 or greater and not '%d'") raise exceptions.BadRequest(resource='limit', msg=msg)
def deserialize(self, datastring, content_type): """Deserialize a string to a dictionary. The string must be in the format of a supported MIME type. """ try: return self.get_deserialize_handler(content_type).deserialize( datastring) except Exception: raise webob.exc.HTTPBadRequest(_("Could not deserialize data"))
def update(self, request, id, body=None, **kwargs): """Updates the specified entity's attributes.""" try: payload = body.copy() except AttributeError: msg = _("Invalid format: %s") % request.body raise exceptions.BadRequest(resource='body', msg=msg) payload['id'] = id self._notifier.info(request.context, self._resource + '.update.start', payload) return self._update(request, id, body, **kwargs)
def __init__(self, kind, match): # Process the match try: self.target_field = re.findall(r'^\%\((.*)\)s$', match)[0] except IndexError: err_reason = (_("Unable to identify a target field from:%s. " "Match should be in the form %%(<field_name>)s") % match) LOG.exception(err_reason) raise exceptions.PolicyInitError(policy="%s:%s" % (kind, match), reason=err_reason) super(OwnerCheck, self).__init__(kind, match)
def add_provider(self, provider): self._ensure_driver_unique(provider['driver']) self._ensure_default_unique(provider['service_type'], provider['default']) provider_type = (provider['service_type'], provider['name']) if provider_type in self.providers: msg = (_("Multiple providers specified for service " "%s") % provider['service_type']) LOG.error(msg) raise n_exc.Invalid(msg) self.providers[provider_type] = {'driver': provider['driver'], 'default': provider['default']}
def parse_service_provider_opt(service_module='neutron'): """Parse service definition opts and returns result.""" def validate_name(name): if len(name) > attr.NAME_MAX_LEN: raise n_exc.Invalid( _("Provider name %(name)s is limited by %(len)s characters") % {'name': name, 'len': attr.NAME_MAX_LEN}) neutron_mod = NeutronModule(service_module) svc_providers_opt = neutron_mod.service_providers() LOG.debug("Service providers = %s", svc_providers_opt) res = [] for prov_def in svc_providers_opt: split = prov_def.split(':') try: svc_type, name, driver = split[:3] except ValueError: raise n_exc.Invalid(_("Invalid service provider format")) validate_name(name) name = normalize_provider_name(name) default = False if len(split) == 4 and split[3]: if split[3] == 'default': default = True else: msg = (_("Invalid provider format. " "Last part should be 'default' or empty: %s") % prov_def) LOG.error(msg) raise n_exc.Invalid(msg) driver = get_provider_driver_class(driver) res.append({'service_type': svc_type, 'name': name, 'driver': driver, 'default': default}) return res
def prepare_request_body(context, body, is_create, resource, attr_info, allow_bulk=False): """Verifies required attributes are in request body. Also checking that an attribute is only specified if it is allowed for the given operation (create/update). Attribute with default values are considered to be optional. body argument must be the deserialized body. """ collection = resource + "s" if not body: raise webob.exc.HTTPBadRequest(_("Resource body required")) LOG.debug("Request body: %(body)s", {'body': body}) try: if collection in body: if not allow_bulk: raise webob.exc.HTTPBadRequest( _("Bulk operation " "not supported")) if not body[collection]: raise webob.exc.HTTPBadRequest(_("Resources required")) bulk_body = [ Controller.prepare_request_body( context, item if resource in item else {resource: item}, is_create, resource, attr_info, allow_bulk) for item in body[collection] ] return {collection: bulk_body} res_dict = body.get(resource) except (AttributeError, TypeError): msg = _("Body contains invalid data") raise webob.exc.HTTPBadRequest(msg) if res_dict is None: msg = _("Unable to find '%s' in request body") % resource raise webob.exc.HTTPBadRequest(msg) attributes.populate_tenant_id(context, res_dict, attr_info, is_create) attributes.verify_attributes(res_dict, attr_info) if is_create: # POST attributes.fill_default_value(attr_info, res_dict, webob.exc.HTTPBadRequest) else: # PUT for attr, attr_vals in six.iteritems(attr_info): if attr in res_dict and not attr_vals['allow_put']: msg = _("Cannot update read-only attribute %s") % attr raise webob.exc.HTTPBadRequest(msg) attributes.convert_value(attr_info, res_dict, webob.exc.HTTPBadRequest) return body
def is_cidr_host(cidr): """Determines if the cidr passed in represents a single host network :param cidr: Either an ipv4 or ipv6 cidr. :returns: True if the cidr is /32 for ipv4 or /128 for ipv6. :raises ValueError: raises if cidr does not contain a '/'. This disallows plain IP addresses specifically to avoid ambiguity. """ if '/' not in str(cidr): raise ValueError(_("cidr doesn't contain a '/'")) net = netaddr.IPNetwork(cidr) if net.version == 4: return net.prefixlen == n_const.IPv4_BITS return net.prefixlen == n_const.IPv6_BITS