def list_traits(req): context = req.environ['placement.context'] context.can(policies.TRAITS_LIST) want_version = req.environ[microversion.MICROVERSION_ENVIRON] filters = {} util.validate_query_params(req, schema.LIST_TRAIT_SCHEMA) if 'name' in req.GET: filters = _normalize_traits_qs_param(req.GET['name']) if 'associated' in req.GET: if req.GET['associated'].lower() not in ['true', 'false']: raise webob.exc.HTTPBadRequest( _('The query parameter "associated" only accepts ' '"true" or "false"')) filters['associated'] = ( True if req.GET['associated'].lower() == 'true' else False) traits = rp_obj.TraitList.get_all(context, filters) req.response.status = 200 output, last_modified = _serialize_traits(traits, want_version) if want_version.matches((1, 15)): req.response.last_modified = last_modified req.response.cache_control = 'no-cache' req.response.body = encodeutils.to_utf8(jsonutils.dumps(output)) req.response.content_type = 'application/json' return req.response
def get_total_usages(req): """GET the sum of usages for a project or a project/user. On success return a 200 and an application/json body representing the sum/total of usages. Return 404 Not Found if the wanted microversion does not match. """ context = req.environ['placement.context'] want_version = req.environ[microversion.MICROVERSION_ENVIRON] util.validate_query_params(req, schema.GET_USAGES_SCHEMA_1_9) project_id = req.GET.get('project_id') user_id = req.GET.get('user_id') usages = rp_obj.UsageList.get_all_by_project_user(context, project_id, user_id=user_id) response = req.response usages_dict = {'usages': {resource.resource_class: resource.usage for resource in usages}} response.body = encodeutils.to_utf8(jsonutils.dumps(usages_dict)) req.response.content_type = 'application/json' if want_version.matches((1, 15)): req.response.cache_control = 'no-cache' # While it would be possible to generate a last-modified time # based on the collection of allocations that result in a usage # value (with some spelunking in the SQL) that doesn't align with # the question that is being asked in a request for usages: What # is the usage, now? So the last-modified time is set to utcnow. req.response.last_modified = timeutils.utcnow(with_timezone=True) return req.response
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 list_allocation_candidates(req): """GET a JSON object with a list of allocation requests and a JSON object of provider summary objects On success return a 200 and an application/json body representing a collection of allocation requests and provider summaries """ context = req.environ['placement.context'] context.can(policies.LIST) want_version = req.environ[microversion.MICROVERSION_ENVIRON] get_schema = schema.GET_SCHEMA_1_10 if want_version.matches((1, 25)): get_schema = schema.GET_SCHEMA_1_25 elif want_version.matches((1, 21)): get_schema = schema.GET_SCHEMA_1_21 elif want_version.matches((1, 17)): get_schema = schema.GET_SCHEMA_1_17 elif want_version.matches((1, 16)): get_schema = schema.GET_SCHEMA_1_16 util.validate_query_params(req, get_schema) requests = util.parse_qs_request_groups(req) limit = req.GET.getall('limit') # JSONschema has already confirmed that limit has the form # of an integer. if limit: limit = int(limit[0]) 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] else: # group_policy is required if more than one numbered request group was # specified. if len([rg for rg in requests.values() if rg.use_same_provider]) > 1: raise webob.exc.HTTPBadRequest( _('The "group_policy" parameter is required when specifying ' 'more than one "resources{N}" parameter.')) try: cands = rp_obj.AllocationCandidates.get_by_requests( context, requests, limit=limit, group_policy=group_policy) 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(six.text_type(exc)) response = req.response trx_cands = _transform_allocation_candidates(cands, requests, want_version) json_data = jsonutils.dumps(trx_cands) response.body = encodeutils.to_utf8(json_data) response.content_type = 'application/json' if want_version.matches((1, 15)): response.cache_control = 'no-cache' response.last_modified = timeutils.utcnow(with_timezone=True) return response
def get_total_usages(req): """GET the sum of usages for a project or a project/user. On success return a 200 and an application/json body representing the sum/total of usages. Return 404 Not Found if the wanted microversion does not match. """ project_id = req.GET.get('project_id') user_id = req.GET.get('user_id') consumer_type = req.GET.get('consumer_type') context = req.environ['placement.context'] context.can( policies.TOTAL_USAGES, target={'project_id': project_id}) want_version = req.environ[microversion.MICROVERSION_ENVIRON] want_schema = schema.GET_USAGES_SCHEMA_1_9 show_consumer_type = want_version.matches((1, 38)) if show_consumer_type: want_schema = schema.GET_USAGES_SCHEMA_V1_38 util.validate_query_params(req, want_schema) if show_consumer_type: usages = usage_obj.get_by_consumer_type( context, project_id, user_id=user_id, consumer_type=consumer_type) else: usages = usage_obj.get_all_by_project_user(context, project_id, user_id=user_id) response = req.response if show_consumer_type: usage = collections.defaultdict(dict) for resource in usages: ct = resource.consumer_type rc = resource.resource_class cc = resource.consumer_count used = resource.usage usage[ct][rc] = used usage[ct]['consumer_count'] = cc usages_dict = { 'usages': usage } else: usages_dict = {'usages': {resource.resource_class: resource.usage for resource in usages}} response.body = encodeutils.to_utf8(jsonutils.dumps(usages_dict)) req.response.content_type = 'application/json' if want_version.matches((1, 15)): req.response.cache_control = 'no-cache' # While it would be possible to generate a last-modified time # based on the collection of allocations that result in a usage # value (with some spelunking in the SQL) that doesn't align with # the question that is being asked in a request for usages: What # is the usage, now? So the last-modified time is set to utcnow. req.response.last_modified = timeutils.utcnow(with_timezone=True) return req.response
def list_allocation_candidates(req): """GET a JSON object with a list of allocation requests and a JSON object of provider summary objects On success return a 200 and an application/json body representing a collection of allocation requests and provider summaries """ context = req.environ['placement.context'] context.can(policies.LIST) want_version = req.environ[microversion.MICROVERSION_ENVIRON] get_schema = _get_schema(want_version) util.validate_query_params(req, get_schema) rqparams = lib.RequestWideParams.from_request(req) groups = lib.RequestGroup.dict_from_request(req, rqparams) if not rqparams.group_policy: # group_policy is required if more than one numbered request group was # specified. if len([rg for rg in groups.values() if rg.use_same_provider]) > 1: raise webob.exc.HTTPBadRequest( 'The "group_policy" parameter is required when specifying ' 'more than one "resources{N}" parameter.') # We can't be aware of nested architecture with old microversions nested_aware = want_version.matches((1, 29)) try: cands = ac_obj.AllocationCandidates.get_by_requests( context, groups, rqparams, nested_aware=nested_aware) 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(six.text_type(exc)) response = req.response trx_cands = _transform_allocation_candidates(cands, groups, want_version) json_data = jsonutils.dumps(trx_cands) response.body = encodeutils.to_utf8(json_data) response.content_type = 'application/json' if want_version.matches((1, 15)): response.cache_control = 'no-cache' response.last_modified = timeutils.utcnow(with_timezone=True) return response
def list_allocation_candidates(req): """GET a JSON object with a list of allocation requests and a JSON object of provider summary objects On success return a 200 and an application/json body representing a collection of allocation requests and provider summaries """ context = req.environ['placement.context'] want_version = req.environ[microversion.MICROVERSION_ENVIRON] get_schema = schema.GET_SCHEMA_1_10 if want_version.matches((1, 21)): get_schema = schema.GET_SCHEMA_1_21 elif want_version.matches((1, 17)): get_schema = schema.GET_SCHEMA_1_17 elif want_version.matches((1, 16)): get_schema = schema.GET_SCHEMA_1_16 util.validate_query_params(req, get_schema) requests = util.parse_qs_request_groups(req.GET) limit = req.GET.getall('limit') # JSONschema has already confirmed that limit has the form # of an integer. if limit: limit = int(limit[0]) try: cands = rp_obj.AllocationCandidates.get_by_requests( context, requests, limit) 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(six.text_type(exc)) response = req.response trx_cands = _transform_allocation_candidates(cands, want_version) json_data = jsonutils.dumps(trx_cands) response.body = encodeutils.to_utf8(json_data) response.content_type = 'application/json' if want_version.matches((1, 15)): response.cache_control = 'no-cache' response.last_modified = timeutils.utcnow(with_timezone=True) return response
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