def delete_inventory(req): """DELETE to destroy a single inventory. If the inventory is in use or resource provider generation is out of sync return a 409. On success return a 204 and an empty body. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_class = util.wsgi_path_item(req.environ, 'resource_class') resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) try: resource_provider.delete_inventory(resource_class) except (exception.ConcurrentUpdateDetected, exception.InventoryInUse) as exc: raise webob.exc.HTTPConflict( _('Unable to delete inventory of class %(class)s: %(error)s') % {'class': resource_class, 'error': exc}, json_formatter=util.json_error_formatter) except exception.NotFound as exc: raise webob.exc.HTTPNotFound( _('No inventory of class %(class)s found for delete: %(error)s') % {'class': resource_class, 'error': exc}, json_formatter=util.json_error_formatter) response = req.response response.status = 204 response.content_type = None return response
def update_inventory(req): """PUT to update one inventory. If the resource generation is out of sync, return a 409. If the inventory has settings which are invalid (for example reserved exceeds capacity), return a 400. On success return a 200 with an application/json body representing the inventory. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_class = util.wsgi_path_item(req.environ, 'resource_class') resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) data = _extract_inventory(req.body, BASE_INVENTORY_SCHEMA) if data['resource_provider_generation'] != resource_provider.generation: raise webob.exc.HTTPConflict( _('resource provider generation conflict'), json_formatter=util.json_error_formatter) inventory = _make_inventory_object(resource_provider, resource_class, **data) try: resource_provider.update_inventory(inventory) except (exception.ConcurrentUpdateDetected, db_exc.DBDuplicateEntry) as exc: raise webob.exc.HTTPConflict( _('update conflict: %(error)s') % {'error': exc}, json_formatter=util.json_error_formatter) except exception.InventoryWithResourceClassNotFound as exc: raise webob.exc.HTTPBadRequest( _('No inventory record with resource class for resource provider ' '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, 'error': exc}, json_formatter=util.json_error_formatter) except exception.InvalidInventoryCapacity as exc: raise webob.exc.HTTPBadRequest( _('Unable to update inventory for resource provider ' '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, 'error': exc}, json_formatter=util.json_error_formatter) return _send_inventory(req.response, resource_provider, inventory)
def list_usages(req): """GET a dictionary of resource provider usage by resource class. If the resource provider does not exist return a 404. On success return a 200 with an application/json representation of the usage dictionary. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') # Resource provider object needed for two things: If it is # NotFound we'll get a 404 here, which needs to happen because # get_all_by_resource_provider_uuid can return an empty list. # It is also needed for the generation, used in the outgoing # representation. try: resource_provider = objects.ResourceProvider.get_by_uuid(context, uuid) except exception.NotFound as exc: raise webob.exc.HTTPNotFound( _("No resource provider with uuid %(uuid)s found: %(error)s") % { 'uuid': uuid, 'error': exc }, json_formatter=util.json_error_formatter) usage = objects.UsageList.get_all_by_resource_provider_uuid(context, uuid) response = req.response response.body = encodeutils.to_utf8( jsonutils.dumps(_serialize_usages(resource_provider, usage))) req.response.content_type = 'application/json' return req.response
def update_resource_class(req): """PUT to update a single resource class. On success return a 200 response with a representation of the updated resource class. """ name = util.wsgi_path_item(req.environ, 'name') context = req.environ['placement.context'] data = util.extract_json(req.body, PUT_RC_SCHEMA_V1_2) # The containing application will catch a not found here. rc = objects.ResourceClassObject.get_by_name(context, name) rc.name = data['name'] try: rc.save() except exception.ResourceClassExists: raise webob.exc.HTTPConflict( _('Resource class already exists: %(name)s') % {'name': rc.name}, json_formatter=util.json_error_formatter) except exception.ResourceClassCannotUpdateStandard: raise webob.exc.HTTPBadRequest( _('Cannot update standard resource class %(rp_name)s') % {'rp_name': name}, json_formatter=util.json_error_formatter) req.response.body = encodeutils.to_utf8( jsonutils.dumps(_serialize_resource_class(req.environ, rc))) req.response.status = 200 req.response.content_type = 'application/json' return req.response
def list_for_resource_provider(req): """List allocations associated with a resource provider.""" # TODO(cdent): On a shared resource provider (for example a # giant disk farm) this list could get very long. At the moment # we have no facility for limiting the output. Given that we are # using a dict of dicts for the output we are potentially limiting # ourselves in terms of sorting and filtering. context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') # confirm existence of resource provider so we get a reasonable # 404 instead of empty list try: resource_provider = objects.ResourceProvider.get_by_uuid(context, uuid) except exception.NotFound as exc: raise webob.exc.HTTPNotFound( _("Resource provider '%(rp_uuid)s' not found: %(error)s") % { 'rp_uuid': uuid, 'error': exc }) allocations = objects.AllocationList.get_all_by_resource_provider_uuid( context, uuid) allocations_json = jsonutils.dumps( _serialize_allocations_for_resource_provider(allocations, resource_provider)) req.response.status = 200 req.response.body = encodeutils.to_utf8(allocations_json) req.response.content_type = 'application/json' return req.response
def delete_allocations(req): context = req.environ['placement.context'] consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid') allocations = objects.AllocationList.get_all_by_consumer_id( context, consumer_uuid) if allocations: try: allocations.delete_all() # NOTE(pumaranikar): Following NotFound exception added in the case # when allocation is deleted from allocations list by some other # activity. In that case, delete_all() will throw a NotFound exception. except exception.NotFound as exc: raise webob.exc.HTPPNotFound( _("Allocation for consumer with id %(id)s not found." "error: %(error)s") % { 'id': consumer_uuid, 'error': exc }) else: raise webob.exc.HTTPNotFound( _("No allocations for consumer '%(consumer_uuid)s'") % {'consumer_uuid': consumer_uuid}) LOG.debug("Successfully deleted allocations %s", allocations) req.response.status = 204 req.response.content_type = None return req.response
def delete_resource_class(req): """DELETE to destroy a single resource class. On success return a 204 and an empty body. """ name = util.wsgi_path_item(req.environ, 'name') context = req.environ['placement.context'] # The containing application will catch a not found here. rc = objects.ResourceClassObject.get_by_name(context, name) try: rc.destroy() except exception.ResourceClassCannotDeleteStandard as exc: raise webob.exc.HTTPBadRequest( _('Cannot delete standard resource class %(rp_name)s: %(error)s') % { 'rp_name': name, 'error': exc }, json_formatter=util.json_error_formatter) except exception.ResourceClassInUse as exc: raise webob.exc.HTTPConflict( _('Unable to delete resource class %(rp_name)s: %(error)s') % { 'rp_name': name, 'error': exc }, json_formatter=util.json_error_formatter) req.response.status = 204 req.response.content_type = None return req.response
def update_resource_provider(req): """PUT to update a single resource provider. On success return a 200 response with a representation of the updated resource provider. """ uuid = util.wsgi_path_item(req.environ, 'uuid') context = req.environ['placement.context'] # The containing application will catch a not found here. resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) data = util.extract_json(req.body, PUT_RESOURCE_PROVIDER_SCHEMA) resource_provider.name = data['name'] try: resource_provider.save() except db_exc.DBDuplicateEntry as exc: raise webob.exc.HTTPConflict( _('Conflicting resource provider already exists: %(error)s') % {'error': exc}) except exception.ObjectActionError as exc: raise webob.exc.HTTPBadRequest( _('Unable to save resource provider %(rp_uuid)s: %(error)s') % {'rp_uuid': uuid, 'error': exc}) req.response.body = encodeutils.to_utf8(jsonutils.dumps( _serialize_provider(req.environ, resource_provider))) req.response.status = 200 req.response.content_type = 'application/json' return req.response
def set_allocations(req): context = req.environ['placement.context'] consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid') data = _extract_allocations(req.body, ALLOCATION_SCHEMA) allocation_data = data['allocations'] # If the body includes an allocation for a resource provider # that does not exist, raise a 400. allocation_objects = [] for allocation in allocation_data: resource_provider_uuid = allocation['resource_provider']['uuid'] try: resource_provider = objects.ResourceProvider.get_by_uuid( context, resource_provider_uuid) except exception.NotFound: raise webob.exc.HTTPBadRequest( _("Allocation for resource provider '%(rp_uuid)s' " "that does not exist.") % {'rp_uuid': resource_provider_uuid}) resources = allocation['resources'] for resource_class in resources: allocation = objects.Allocation( resource_provider=resource_provider, consumer_id=consumer_uuid, resource_class=resource_class, used=resources[resource_class]) allocation_objects.append(allocation) allocations = objects.AllocationList(context, objects=allocation_objects) try: allocations.create_all() LOG.debug("Successfully wrote allocations %s", allocations) # InvalidInventory is a parent for several exceptions that # indicate either that Inventory is not present, or that # capacity limits have been exceeded. except exception.NotFound as exc: raise webob.exc.HTTPBadRequest( _("Unable to allocate inventory for resource provider " "%(rp_uuid)s: %(error)s") % { 'rp_uuid': resource_provider_uuid, 'error': exc }) except exception.InvalidInventory as exc: LOG.exception(_LE("Bad inventory")) raise webob.exc.HTTPConflict( _('Unable to allocate inventory: %(error)s') % {'error': exc}) except exception.ConcurrentUpdateDetected as exc: LOG.exception(_LE("Concurrent Update")) raise webob.exc.HTTPConflict( _('Inventory changed while attempting to allocate: %(error)s') % {'error': exc}) req.response.status = 204 req.response.content_type = None return req.response
def set_aggregates(req): microversion.raise_404_if_not_version(req, (1, 1)) context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) aggregate_uuids = util.extract_json(req.body, PUT_AGGREGATES_SCHEMA) resource_provider.set_aggregates(aggregate_uuids) return _send_aggregates(req.response, aggregate_uuids)
def get_inventory(req): """GET one inventory. On success return a 200 an application/json body representing one inventory. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_class = util.wsgi_path_item(req.environ, 'resource_class') resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) inventory = objects.InventoryList.get_all_by_resource_provider_uuid( context, resource_provider.uuid).find(resource_class) if not inventory: raise webob.exc.HTTPNotFound( _('No inventory of class %(class)s for %(rp_uuid)s') % {'class': resource_class, 'rp_uuid': resource_provider.uuid}, json_formatter=util.json_error_formatter) return _send_inventory(req.response, resource_provider, inventory)
def get_resource_class(req): """Get a single resource class. On success return a 200 with an application/json body representing the resource class. """ name = util.wsgi_path_item(req.environ, 'name') context = req.environ['placement.context'] # The containing application will catch a not found here. rc = objects.ResourceClassObject.get_by_name(context, name) req.response.body = encodeutils.to_utf8( jsonutils.dumps(_serialize_resource_class(req.environ, rc))) req.response.content_type = 'application/json' return req.response
def get_aggregates(req): """GET a list of aggregates associated with a resource provider. If the resource provider does not exist return a 404. On success return a 200 with an application/json body containing a list of aggregate uuids. """ microversion.raise_404_if_not_version(req, (1, 1)) context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) aggregate_uuids = resource_provider.get_aggregates() return _send_aggregates(req.response, aggregate_uuids)
def get_resource_provider(req): """Get a single resource provider. On success return a 200 with an application/json body representing the resource provider. """ uuid = util.wsgi_path_item(req.environ, 'uuid') # The containing application will catch a not found here. context = req.environ['placement.context'] resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) req.response.body = encodeutils.to_utf8(jsonutils.dumps( _serialize_provider(req.environ, resource_provider))) req.response.content_type = 'application/json' return req.response
def handle_405(environ, start_response): """Return a 405 response when method is not allowed. If _methods are in routing_args, send an allow header listing the methods that are possible on the provided URL. """ _methods = util.wsgi_path_item(environ, '_methods') headers = {} if _methods: # Ensure allow header is a python 2 or 3 native string (thus # not unicode in python 2 but stay a string in python 3) # In the process done by Routes to save the allowed methods # to its routing table they become unicode in py2. headers['allow'] = str(_methods) raise webob.exc.HTTPMethodNotAllowed( _('The method specified is not allowed for this resource.'), headers=headers, json_formatter=util.json_error_formatter)
def list_for_consumer(req): """List allocations associated with a consumer.""" context = req.environ['placement.context'] consumer_id = util.wsgi_path_item(req.environ, 'consumer_uuid') # NOTE(cdent): There is no way for a 404 to be returned here, # only an empty result. We do not have a way to validate a # consumer id. allocations = objects.AllocationList.get_all_by_consumer_id( context, consumer_id) allocations_json = jsonutils.dumps( _serialize_allocations_for_consumer(allocations)) req.response.status = 200 req.response.body = encodeutils.to_utf8(allocations_json) req.response.content_type = 'application/json' return req.response
def get_inventories(req): """GET a list of inventories. On success return a 200 with an application/json body representing a collection of inventories. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') try: resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) except exception.NotFound as exc: raise webob.exc.HTTPNotFound( _("No resource provider with uuid %(uuid)s found : %(error)s") % {'uuid': uuid, 'error': exc}, json_formatter=util.json_error_formatter) inventories = objects.InventoryList.get_all_by_resource_provider_uuid( context, resource_provider.uuid) return _send_inventories(req.response, resource_provider, inventories)
def delete_resource_provider(req): """DELETE to destroy a single resource provider. On success return a 204 and an empty body. """ uuid = util.wsgi_path_item(req.environ, 'uuid') context = req.environ['placement.context'] # The containing application will catch a not found here. try: resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) resource_provider.destroy() except exception.ResourceProviderInUse as exc: raise webob.exc.HTTPConflict( _('Unable to delete resource provider %(rp_uuid)s: %(error)s') % {'rp_uuid': uuid, 'error': exc}) except exception.NotFound as exc: raise webob.exc.HTTPNotFound( _("No resource provider with uuid %s found for delete") % uuid) req.response.status = 204 req.response.content_type = None return req.response
def create_inventory(req): """POST to create one inventory. On success return a 201 response, a location header pointing to the newly created inventory and an application/json representation of the inventory. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) data = _extract_inventory(req.body, POST_INVENTORY_SCHEMA) resource_class = data.pop('resource_class') inventory = _make_inventory_object(resource_provider, resource_class, **data) try: resource_provider.add_inventory(inventory) except (exception.ConcurrentUpdateDetected, db_exc.DBDuplicateEntry) as exc: raise webob.exc.HTTPConflict( _('Update conflict: %(error)s') % {'error': exc}, json_formatter=util.json_error_formatter) except (exception.InvalidInventoryCapacity, exception.NotFound) as exc: raise webob.exc.HTTPBadRequest( _('Unable to create inventory for resource provider ' '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, 'error': exc}, json_formatter=util.json_error_formatter) response = req.response response.location = util.inventory_url( req.environ, resource_provider, resource_class) return _send_inventory(response, resource_provider, inventory, status=201)
def set_inventories(req): """PUT to set all inventory for a resource provider. Create, update and delete inventory as required to reset all the inventory. If the resource generation is out of sync, return a 409. If an inventory to be deleted is in use, return a 409. If any inventory to be created or updated has settings which are invalid (for example reserved exceeds capacity), return a 400. On success return a 200 with an application/json body representing the inventories. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = objects.ResourceProvider.get_by_uuid( context, uuid) data = _extract_inventories(req.body, PUT_INVENTORY_SCHEMA) if data['resource_provider_generation'] != resource_provider.generation: raise webob.exc.HTTPConflict( _('resource provider generation conflict'), json_formatter=util.json_error_formatter) inv_list = [] for res_class, inventory_data in data['inventories'].items(): inventory = _make_inventory_object( resource_provider, res_class, **inventory_data) inv_list.append(inventory) inventories = objects.InventoryList(objects=inv_list) try: resource_provider.set_inventory(inventories) except exception.ResourceClassNotFound as exc: raise webob.exc.HTTPBadRequest( _('Unknown resource class in inventory for resource provider ' '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, 'error': exc}, json_formatter=util.json_error_formatter) except exception.InventoryWithResourceClassNotFound as exc: raise webob.exc.HTTPConflict( _('Race condition detected when setting inventory. No inventory ' 'record with resource class for resource provider ' '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, 'error': exc}, json_formatter=util.json_error_formatter) except (exception.ConcurrentUpdateDetected, exception.InventoryInUse, db_exc.DBDuplicateEntry) as exc: raise webob.exc.HTTPConflict( _('update conflict: %(error)s') % {'error': exc}, json_formatter=util.json_error_formatter) except exception.InvalidInventoryCapacity as exc: raise webob.exc.HTTPBadRequest( _('Unable to update inventory for resource provider ' '%(rp_uuid)s: %(error)s') % {'rp_uuid': resource_provider.uuid, 'error': exc}, json_formatter=util.json_error_formatter) return _send_inventories(req.response, resource_provider, inventories)