def list_resource_providers(req): """GET a list of resource providers. On success return a 200 and an application/json body representing a collection of resource providers. """ context = req.environ['placement.context'] context.can(policies.LIST) want_version = req.environ[microversion.MICROVERSION_ENVIRON] schema = rp_schema.GET_RPS_SCHEMA_1_0 if want_version.matches((1, 18)): schema = rp_schema.GET_RPS_SCHEMA_1_18 elif want_version.matches((1, 14)): schema = rp_schema.GET_RPS_SCHEMA_1_14 elif want_version.matches((1, 4)): schema = rp_schema.GET_RPS_SCHEMA_1_4 elif want_version.matches((1, 3)): schema = rp_schema.GET_RPS_SCHEMA_1_3 allow_forbidden = want_version.matches((1, 22)) util.validate_query_params(req, schema) filters = {} # special handling of member_of qparam since we allow multiple member_of # params at microversion 1.24. if 'member_of' in req.GET: filters['member_of'], filters['forbidden_aggs'] = ( util.normalize_member_of_qs_params(req)) qpkeys = ('uuid', 'name', 'in_tree', 'resources', 'required') for attr in qpkeys: if attr in req.GET: value = req.GET[attr] if attr == 'resources': value = util.normalize_resources_qs_param(value) elif attr == 'required': value = util.normalize_traits_qs_param( value, allow_forbidden=allow_forbidden) filters[attr] = value try: resource_providers = rp_obj.get_all_by_filters(context, filters) except exception.ResourceClassNotFound as exc: raise webob.exc.HTTPBadRequest( 'Invalid resource class in resources parameter: %(error)s' % {'error': exc}) except exception.TraitNotFound as exc: raise webob.exc.HTTPBadRequest( 'Invalid trait(s) in request: %(error)s' % {'error': exc}) response = req.response output, last_modified = _serialize_providers( req.environ, resource_providers, want_version) response.body = encodeutils.to_utf8(jsonutils.dumps(output)) response.content_type = 'application/json' if want_version.matches((1, 15)): response.last_modified = last_modified response.cache_control = 'no-cache' return response
def test_multiple(self): traits = ( 'HW_CPU_X86_VMX', 'HW_GPU_API_DIRECT3D_V12_0', 'HW_NIC_OFFLOAD_RX', 'CUSTOM_GOLD', 'STORAGE_DISK_SSD', ) self.assertEqual( set(traits), util.normalize_traits_qs_param('%s, %s,%s , %s , %s ' % traits))
def from_request(cls, req): # TODO(efried): Make it an error to specify limit more than once - # maybe when we make group_policy optional. limit = req.GET.getall('limit') # JSONschema has already confirmed that limit has the form # of an integer. if limit: limit = int(limit[0]) # TODO(efried): Make it an error to specify group_policy more than once # - maybe when we make it optional. group_policy = req.GET.getall('group_policy') or None # Schema ensures we get either "none" or "isolate" if group_policy: group_policy = group_policy[0] anchor_required_traits = None anchor_forbidden_traits = None root_required = req.GET.getall('root_required') if root_required: if len(root_required) > 1: raise webob.exc.HTTPBadRequest( "Query parameter 'root_required' may be specified only " "once.", comment=errors.ILLEGAL_DUPLICATE_QUERYPARAM) anchor_required_traits, anchor_forbidden_traits, conflicts = ( _fix_one_forbidden(util.normalize_traits_qs_param( root_required[0], allow_forbidden=True))) if conflicts: raise webob.exc.HTTPBadRequest( 'Conflicting required and forbidden traits found in ' 'root_required: %s' % ', '.join(conflicts), comment=errors.QUERYPARAM_BAD_VALUE) same_subtree = req.GET.getall('same_subtree') # Construct a list of sets of request group suffixes strings. same_subtrees = [] if same_subtree: for val in same_subtree: suffixes = set(substr.strip() for substr in val.split(',')) if '' in suffixes: raise webob.exc.HTTPBadRequest( 'Empty string (unsuffixed group) can not be specified ' 'in `same_subtree` ', comment=errors.QUERYPARAM_BAD_VALUE) same_subtrees.append(suffixes) return cls( limit=limit, group_policy=group_policy, anchor_required_traits=anchor_required_traits, anchor_forbidden_traits=anchor_forbidden_traits, same_subtrees=same_subtrees)
def _parse_request_items(req, allow_forbidden, verbose_suffix): ret = {} pattern = _QS_KEY_PATTERN_1_33 if verbose_suffix else _QS_KEY_PATTERN for key, val in req.GET.items(): match = pattern.match(key) if not match: continue # `prefix` is 'resources', 'required', 'member_of', or 'in_tree' # `suffix` is a number in microversion < 1.33, a string 1-64 # characters long of [a-zA-Z0-9_-] in microversion >= 1.33, or None prefix, suffix = match.groups() suffix = suffix or '' if suffix not in ret: ret[suffix] = RequestGroup(use_same_provider=bool(suffix)) request_group = ret[suffix] if prefix == _QS_RESOURCES: request_group.resources = util.normalize_resources_qs_param( val) elif prefix == _QS_REQUIRED: request_group.required_traits = util.normalize_traits_qs_param( val, allow_forbidden=allow_forbidden) elif prefix == _QS_MEMBER_OF: # special handling of member_of qparam since we allow multiple # member_of params at microversion 1.24. # NOTE(jaypipes): Yes, this is inefficient to do this when # there are multiple member_of query parameters, but we do this # so we can error out if someone passes an "orphaned" member_of # request group. # TODO(jaypipes): Do validation of query parameters using # JSONSchema request_group.member_of, request_group.forbidden_aggs = ( util.normalize_member_of_qs_params(req, suffix)) elif prefix == _QS_IN_TREE: request_group.in_tree = util.normalize_in_tree_qs_params( val) return ret
def test_one(self): trait = 'HW_CPU_X86_VMX' # Various whitespace permutations for fmt in ('%s', ' %s', '%s ', ' %s ', ' %s '): self.assertEqual(set([trait]), util.normalize_traits_qs_param(fmt % trait))
def list_resource_providers(req): """GET a list of resource providers. On success return a 200 and an application/json body representing a collection of resource providers. """ context = req.environ['placement.context'] want_version = req.environ[microversion.MICROVERSION_ENVIRON] schema = rp_schema.GET_RPS_SCHEMA_1_0 if want_version.matches((1, 18)): schema = rp_schema.GET_RPS_SCHEMA_1_18 elif want_version.matches((1, 14)): schema = rp_schema.GET_RPS_SCHEMA_1_14 elif want_version.matches((1, 4)): schema = rp_schema.GET_RPS_SCHEMA_1_4 elif want_version.matches((1, 3)): schema = rp_schema.GET_RPS_SCHEMA_1_3 util.validate_query_params(req, schema) filters = {} qpkeys = ('uuid', 'name', 'member_of', 'in_tree', 'resources', 'required') for attr in qpkeys: if attr in req.GET: value = req.GET[attr] # special case member_of to always make its value a # list, either by accepting the single value, or if it # starts with 'in:' splitting on ','. # NOTE(cdent): This will all change when we start using # JSONSchema validation of query params. if attr == 'member_of': if value.startswith('in:'): value = value[3:].split(',') else: value = [value] # Make sure the values are actually UUIDs. for aggr_uuid in value: if not uuidutils.is_uuid_like(aggr_uuid): raise webob.exc.HTTPBadRequest( _('Invalid uuid value: %(uuid)s') % {'uuid': aggr_uuid}) elif attr == 'resources': value = util.normalize_resources_qs_param(value) elif attr == 'required': value = util.normalize_traits_qs_param(value) filters[attr] = value try: resource_providers = rp_obj.ResourceProviderList.get_all_by_filters( context, filters) except exception.ResourceClassNotFound as exc: raise webob.exc.HTTPBadRequest( _('Invalid resource class in resources parameter: %(error)s') % {'error': exc}) except exception.TraitNotFound as exc: raise webob.exc.HTTPBadRequest( _('Invalid trait(s) in "required" parameter: %(error)s') % {'error': exc}) response = req.response output, last_modified = _serialize_providers( req.environ, resource_providers, want_version) response.body = encodeutils.to_utf8(jsonutils.dumps(output)) response.content_type = 'application/json' if want_version.matches((1, 15)): response.last_modified = last_modified response.cache_control = 'no-cache' return response