Ejemplo n.º 1
0
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')
    try:
        rp = rp_obj.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
            })

    inv_list = rp_obj.InventoryList.get_all_by_resource_provider(context, rp)
    inventory = inv_list.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': uuid
            })

    return _send_inventory(req, rp, inventory)
Ejemplo n.º 2
0
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 = rp_obj.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
            })
    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
            })

    response = req.response
    response.status = 204
    response.content_type = None
    return response
Ejemplo n.º 3
0
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']
    context.can(policies.UPDATE)
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    resource_class = util.wsgi_path_item(req.environ, 'resource_class')

    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)

    data = _extract_inventory(req.body, schema.BASE_INVENTORY_SCHEMA)
    if data['resource_provider_generation'] != resource_provider.generation:
        raise webob.exc.HTTPConflict(
            _('resource provider generation conflict'),
            comment=errors.CONCURRENT_UPDATE)

    inventory = _make_inventory_object(resource_provider, resource_class,
                                       **data)

    try:
        _validate_inventory_capacity(
            req.environ[microversion.MICROVERSION_ENVIRON], inventory)
        resource_provider.update_inventory(inventory)
    except (exception.ConcurrentUpdateDetected,
            db_exc.DBDuplicateEntry) as exc:
        raise webob.exc.HTTPConflict(_('update conflict: %(error)s') %
                                     {'error': exc},
                                     comment=errors.CONCURRENT_UPDATE)
    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
              })
    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
              })

    return _send_inventory(req, resource_provider, inventory)
Ejemplo n.º 4
0
def delete_inventories(req):
    """DELETE all inventory for a resource provider.

    Delete inventory as required to reset all the inventory.
    If an inventory to be deleted is in use, return a 409 Conflict.
    On success return a 204 No content.
    Return 405 Method Not Allowed if the wanted microversion does not match.
    """
    context = req.environ['placement.context']
    context.can(policies.DELETE)
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(
        context, uuid)

    try:
        resource_provider.set_inventory([])
    except exception.ConcurrentUpdateDetected:
        raise webob.exc.HTTPConflict(
            _('Unable to delete inventory for resource provider '
              '%(rp_uuid)s because the inventory was updated by '
              'another process. Please retry your request.') %
            {'rp_uuid': resource_provider.uuid},
            comment=errors.CONCURRENT_UPDATE)
    except exception.InventoryInUse as ex:
        raise webob.exc.HTTPConflict(ex.format_message(),
                                     comment=errors.INVENTORY_INUSE)

    response = req.response
    response.status = 204
    response.content_type = None

    return response
Ejemplo n.º 5
0
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 = rp_obj.ResourceProvider.get_by_uuid(context, uuid)

    data = _extract_inventories(req.body, schema.PUT_INVENTORY_SCHEMA)
    if data['resource_provider_generation'] != resource_provider.generation:
        raise webob.exc.HTTPConflict(
            _('resource provider generation conflict'))

    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 = rp_obj.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
              })
    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
              })
    except (exception.ConcurrentUpdateDetected, exception.InventoryInUse,
            db_exc.DBDuplicateEntry) as exc:
        raise webob.exc.HTTPConflict(
            _('update conflict: %(error)s') % {'error': exc})
    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
              })

    return _send_inventories(req, resource_provider, inventories)
Ejemplo n.º 6
0
def set_aggregates(req):
    context = req.environ['placement.context']
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    consider_generation = want_version.matches(
        min_version=_INCLUDE_GENERATION_VERSION)
    put_schema = schema.PUT_AGGREGATES_SCHEMA_V1_1
    if consider_generation:
        put_schema = schema.PUT_AGGREGATES_SCHEMA_V1_19
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)
    data = util.extract_json(req.body, put_schema)
    if consider_generation:
        # Check for generation conflict
        rp_gen = data['resource_provider_generation']
        if resource_provider.generation != rp_gen:
            raise webob.exc.HTTPConflict(
                _("Resource provider's generation already changed. Please "
                  "update the generation and try again."))
        aggregate_uuids = data['aggregates']
    else:
        aggregate_uuids = data
    try:
        resource_provider.set_aggregates(
            aggregate_uuids, increment_generation=consider_generation)
    except (exception.ConcurrentUpdateDetected,
            db_exc.DBDuplicateEntry) as exc:
        raise webob.exc.HTTPConflict(
            _('Update conflict: %(error)s') % {'error': exc})

    return _send_aggregates(req, resource_provider, aggregate_uuids)
Ejemplo n.º 7
0
def delete_allocations(req):
    context = req.environ['placement.context']
    context.can(policies.ALLOC_DELETE)
    consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid')

    allocations = rp_obj.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.HTTPNotFound(
                  _("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
Ejemplo n.º 8
0
def delete_inventories(req):
    """DELETE all inventory for a resource provider.

    Delete inventory as required to reset all the inventory.
    If an inventory to be deleted is in use, return a 409 Conflict.
    On success return a 204 No content.
    Return 405 Method Not Allowed if the wanted microversion does not match.
    """
    context = req.environ['placement.context']
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)

    inventories = rp_obj.InventoryList(objects=[])

    try:
        resource_provider.set_inventory(inventories)
    except exception.ConcurrentUpdateDetected:
        raise webob.exc.HTTPConflict(
            _('Unable to delete inventory for resource provider '
              '%(rp_uuid)s because the inventory was updated by '
              'another process. Please retry your request.') %
            {'rp_uuid': resource_provider.uuid})
    except exception.InventoryInUse as ex:
        # NOTE(mriedem): This message cannot change without impacting the
        # nova.scheduler.client.report._RE_INV_IN_USE regex.
        raise webob.exc.HTTPConflict(explanation=ex.format_message())

    response = req.response
    response.status = 204
    response.content_type = None

    return response
Ejemplo n.º 9
0
def put_trait(req):
    context = req.environ['placement.context']
    context.can(policies.TRAITS_UPDATE)
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    name = util.wsgi_path_item(req.environ, 'name')

    try:
        jsonschema.validate(name, schema.CUSTOM_TRAIT)
    except jsonschema.ValidationError:
        raise webob.exc.HTTPBadRequest(
            _('The trait is invalid. A valid trait must be no longer than '
              '255 characters, start with the prefix "CUSTOM_" and use '
              'following characters: "A"-"Z", "0"-"9" and "_"'))

    trait = rp_obj.Trait(context)
    trait.name = name

    try:
        trait.create()
        req.response.status = 201
    except exception.TraitExists:
        # Get the trait that already exists to get last-modified time.
        if want_version.matches((1, 15)):
            trait = rp_obj.Trait.get_by_name(context, name)
        req.response.status = 204

    req.response.content_type = None
    req.response.location = util.trait_url(req.environ, trait)
    if want_version.matches((1, 15)):
        req.response.last_modified = trait.created_at
        req.response.cache_control = 'no-cache'
    return req.response
Ejemplo n.º 10
0
def list_for_consumer(req):
    """List allocations associated with a consumer."""
    context = req.environ['placement.context']
    context.can(policies.ALLOC_LIST)
    consumer_id = util.wsgi_path_item(req.environ, 'consumer_uuid')
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]

    # 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 = rp_obj.AllocationList.get_all_by_consumer_id(
        context, consumer_id)

    output = _serialize_allocations_for_consumer(allocations, want_version)
    last_modified = _last_modified_from_allocations(allocations, want_version)
    allocations_json = jsonutils.dumps(output)

    response = req.response
    response.status = 200
    response.body = encodeutils.to_utf8(allocations_json)
    response.content_type = 'application/json'
    if want_version.matches((1, 15)):
        response.last_modified = last_modified
        response.cache_control = 'no-cache'
    return response
Ejemplo n.º 11
0
def list_traits_for_resource_provider(req):
    context = req.environ['placement.context']
    context.can(policies.RP_TRAIT_LIST)
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    uuid = util.wsgi_path_item(req.environ, 'uuid')

    # Resource provider object is needed for two things: If it is
    # NotFound we'll get a 404 here, which needs to happen because
    # get_all_by_resource_provider can return an empty list.
    # It is also needed for the generation, used in the outgoing
    # representation.
    try:
        rp = rp_obj.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})

    traits = rp_obj.TraitList.get_all_by_resource_provider(context, rp)
    response_body, last_modified = _serialize_traits(traits, want_version)
    response_body["resource_provider_generation"] = rp.generation

    if want_version.matches((1, 15)):
        req.response.last_modified = last_modified
        req.response.cache_control = 'no-cache'

    req.response.status = 200
    req.response.body = encodeutils.to_utf8(jsonutils.dumps(response_body))
    req.response.content_type = 'application/json'
    return req.response
Ejemplo n.º 12
0
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']
    context.can(policies.UPDATE)

    data = util.extract_json(req.body, schema.PUT_RC_SCHEMA_V1_2)

    # The containing application will catch a not found here.
    rc = rc_obj.ResourceClass.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})
    except exception.ResourceClassCannotUpdateStandard:
        raise webob.exc.HTTPBadRequest(
            'Cannot update standard resource class %(rp_name)s' %
            {'rp_name': name})

    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
Ejemplo n.º 13
0
def update_resource_class(req):
    """PUT to create or validate the existence of single resource class.

    On a successful create return 201. Return 204 if the class already
    exists. If the resource class is not a custom resource class, return
    a 400. 409 might be a better choice, but 400 aligns with previous code.
    """
    name = util.wsgi_path_item(req.environ, 'name')
    context = req.environ['placement.context']
    context.can(policies.UPDATE)

    # Use JSON validation to validation resource class name.
    util.extract_json('{"name": "%s"}' % name, schema.PUT_RC_SCHEMA_V1_2)

    status = 204
    try:
        rc = rc_obj.ResourceClass.get_by_name(context, name)
    except exception.NotFound:
        try:
            rc = rc_obj.ResourceClass(context, name=name)
            rc.create()
            status = 201
        # We will not see ResourceClassCannotUpdateStandard because
        # that was already caught when validating the {name}.
        except exception.ResourceClassExists:
            # Someone just now created the class, so stick with 204
            pass

    req.response.status = status
    req.response.content_type = None
    req.response.location = util.resource_class_url(req.environ, rc)
    return req.response
Ejemplo n.º 14
0
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 = rp_obj.ResourceProvider.get_by_uuid(context, uuid)
    data = _extract_inventory(req.body, schema.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})
    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
              })

    response = req.response
    response.location = util.inventory_url(req.environ, resource_provider,
                                           resource_class)
    return _send_inventory(req, resource_provider, inventory, status=201)
Ejemplo n.º 15
0
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']
    context.can(policies.SHOW)
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    # The containing application will catch a not found here.
    rc = rc_obj.ResourceClass.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'
    if want_version.matches((1, 15)):
        req.response.cache_control = 'no-cache'
        # Non-custom resource classes will return None from pick_last_modified,
        # so the 'or' causes utcnow to be used.
        last_modified = util.pick_last_modified(None, rc) or timeutils.utcnow(
            with_timezone=True)
        req.response.last_modified = last_modified
    return req.response
Ejemplo n.º 16
0
def update_traits_for_resource_provider(req):
    context = req.environ['placement.context']
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    data = util.extract_json(req.body, schema.SET_TRAITS_FOR_RP_SCHEMA)
    rp_gen = data['resource_provider_generation']
    traits = data['traits']
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)

    if resource_provider.generation != rp_gen:
        raise webob.exc.HTTPConflict(_(
            "Resource provider's generation already changed. Please update "
            "the generation and try again."),
                                     json_formatter=util.json_error_formatter)

    trait_objs = rp_obj.TraitList.get_all(context, filters={'name_in': traits})
    traits_name = set([obj.name for obj in trait_objs])
    non_existed_trait = set(traits) - set(traits_name)
    if non_existed_trait:
        raise webob.exc.HTTPBadRequest(
            _("No such trait %s") % ', '.join(non_existed_trait))

    resource_provider.set_traits(trait_objs)

    response_body, last_modified = _serialize_traits(trait_objs, want_version)
    response_body[
        'resource_provider_generation'] = resource_provider.generation
    if want_version.matches((1, 15)):
        req.response.last_modified = last_modified
        req.response.cache_control = 'no-cache'
    req.response.status = 200
    req.response.body = encodeutils.to_utf8(jsonutils.dumps(response_body))
    req.response.content_type = 'application/json'
    return req.response
Ejemplo n.º 17
0
def set_aggregates(req):
    context = req.environ['placement.context']
    context.can(policies.UPDATE)
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    consider_generation = want_version.matches(
        min_version=_INCLUDE_GENERATION_VERSION)
    put_schema = schema.PUT_AGGREGATES_SCHEMA_V1_1
    if consider_generation:
        put_schema = schema.PUT_AGGREGATES_SCHEMA_V1_19
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(
        context, uuid)
    data = util.extract_json(req.body, put_schema)
    if consider_generation:
        # Check for generation conflict
        rp_gen = data['resource_provider_generation']
        if resource_provider.generation != rp_gen:
            raise webob.exc.HTTPConflict(
                _("Resource provider's generation already changed. Please "
                  "update the generation and try again."),
                comment=errors.CONCURRENT_UPDATE)
        aggregate_uuids = data['aggregates']
    else:
        aggregate_uuids = data
    _set_aggregates(resource_provider, aggregate_uuids,
                    increment_generation=consider_generation)

    return _send_aggregates(req, resource_provider, aggregate_uuids)
Ejemplo n.º 18
0
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']
    context.can(policies.DELETE)
    # The containing application will catch a not found here.
    try:
        resource_provider = rp_obj.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},
            comment=errors.PROVIDER_IN_USE)
    except exception.NotFound as exc:
        raise webob.exc.HTTPNotFound(
            _("No resource provider with uuid %s found for delete") % uuid)
    except exception.CannotDeleteParentResourceProvider as exc:
        raise webob.exc.HTTPConflict(
            _("Unable to delete parent resource provider %(rp_uuid)s: "
              "It has child resource providers.") % {'rp_uuid': uuid},
            comment=errors.PROVIDER_CANNOT_DELETE_PARENT)
    req.response.status = 204
    req.response.content_type = None
    return req.response
Ejemplo n.º 19
0
def _set_allocations_for_consumer(req, schema):
    context = req.environ['placement.context']
    consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid')
    data = util.extract_json(req.body, schema)
    allocation_data = data['allocations']

    # Normalize allocation data to dict.
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    if not want_version.matches((1, 12)):
        allocations_dict = {}
        # Allocation are list-ish, transform to dict-ish
        for allocation in allocation_data:
            resource_provider_uuid = allocation['resource_provider']['uuid']
            allocations_dict[resource_provider_uuid] = {
                'resources': allocation['resources']
            }
        allocation_data = allocations_dict

    # If the body includes an allocation for a resource provider
    # that does not exist, raise a 400.
    allocation_objects = []
    for resource_provider_uuid, allocation in allocation_data.items():
        new_allocations = _new_allocations(context, resource_provider_uuid,
                                           consumer_uuid,
                                           allocation['resources'],
                                           data.get('project_id'),
                                           data.get('user_id'))
        allocation_objects.extend(new_allocations)

    allocations = rp_obj.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 consumer "
              "%(consumer_uuid)s: %(error)s") % {
                  'consumer_uuid': consumer_uuid,
                  'error': exc
              })
    except exception.InvalidInventory as exc:
        raise webob.exc.HTTPConflict(
            _('Unable to allocate inventory: %(error)s') % {'error': exc})
    except exception.ConcurrentUpdateDetected as exc:
        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
Ejemplo n.º 20
0
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']
    context.can(policies.UPDATE)
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]

    # The containing application will catch a not found here.
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(
        context, uuid)

    schema = rp_schema.PUT_RESOURCE_PROVIDER_SCHEMA
    if want_version.matches((1, 14)):
        schema = rp_schema.PUT_RP_SCHEMA_V1_14

    data = util.extract_json(req.body, schema)

    for field in rp_obj.ResourceProvider.SETTABLE_FIELDS:
        if field in data:
            setattr(resource_provider, field, data[field])

    try:
        resource_provider.save()
    except db.ClientError as e:
        if "ConstraintValidationFailed" in e.message:
            raise webob.exc.HTTPConflict(
                'Conflicting resource provider %(name)s already exists.' %
                {'name': data['name']},
                comment=errors.DUPLICATE_NAME)
    except db_exc.DBDuplicateEntry:
        raise webob.exc.HTTPConflict(
            'Conflicting resource provider %(name)s already exists.' %
            {'name': data['name']},
            comment=errors.DUPLICATE_NAME)
    except exception.ObjectActionError as exc:
        raise webob.exc.HTTPBadRequest(
            'Unable to save resource provider %(rp_uuid)s: %(error)s' %
            {'rp_uuid': uuid, 'error': exc})

    response = req.response
    response.status = 200
    response.body = encodeutils.to_utf8(jsonutils.dumps(
        _serialize_provider(req.environ, resource_provider, want_version)))
    response.content_type = 'application/json'
    if want_version.matches((1, 15)):
        response.last_modified = resource_provider.updated_at
        response.cache_control = 'no-cache'
    return response
Ejemplo n.º 21
0
def delete_traits_for_resource_provider(req):
    context = req.environ['placement.context']
    uuid = util.wsgi_path_item(req.environ, 'uuid')

    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)
    try:
        resource_provider.set_traits(rp_obj.TraitList(objects=[]))
    except exception.ConcurrentUpdateDetected as e:
        raise webob.exc.HTTPConflict(explanation=e.format_message())

    req.response.status = 204
    req.response.content_type = None
    return req.response
Ejemplo n.º 22
0
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.
    """
    context = req.environ['placement.context']
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)
    aggregate_uuids = resource_provider.get_aggregates()

    return _send_aggregates(req, resource_provider, aggregate_uuids)
Ejemplo n.º 23
0
def delete_traits_for_resource_provider(req):
    context = req.environ['placement.context']
    context.can(policies.RP_TRAIT_DELETE)
    uuid = util.wsgi_path_item(req.environ, 'uuid')

    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)
    try:
        resource_provider.set_traits(rp_obj.TraitList(objects=[]))
    except exception.ConcurrentUpdateDetected as e:
        raise webob.exc.HTTPConflict(e.format_message(),
                                     comment=errors.CONCURRENT_UPDATE)

    req.response.status = 204
    req.response.content_type = None
    return req.response
Ejemplo n.º 24
0
def get_trait(req):
    context = req.environ['placement.context']
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    name = util.wsgi_path_item(req.environ, 'name')

    try:
        trait = rp_obj.Trait.get_by_name(context, name)
    except exception.TraitNotFound as ex:
        raise webob.exc.HTTPNotFound(explanation=ex.format_message())

    req.response.status = 204
    req.response.content_type = None
    if want_version.matches((1, 15)):
        req.response.last_modified = trait.created_at
        req.response.cache_control = 'no-cache'
    return req.response
Ejemplo n.º 25
0
def delete_trait(req):
    context = req.environ['placement.context']
    name = util.wsgi_path_item(req.environ, 'name')

    try:
        trait = rp_obj.Trait.get_by_name(context, name)
        trait.destroy()
    except exception.TraitNotFound as ex:
        raise webob.exc.HTTPNotFound(explanation=ex.format_message())
    except exception.TraitCannotDeleteStandard as ex:
        raise webob.exc.HTTPBadRequest(explanation=ex.format_message())
    except exception.TraitInUse as ex:
        raise webob.exc.HTTPConflict(explanation=ex.format_message())

    req.response.status = 204
    req.response.content_type = None
    return req.response
Ejemplo n.º 26
0
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']
    context.can(policies.LIST)
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    try:
        rp = rp_obj.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})

    inv_list = inv_obj.get_all_by_resource_provider(context, rp)

    return _send_inventories(req, rp, inv_list)
Ejemplo n.º 27
0
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)
    # Use Exception class as WSGI Application. We don't want to raise here.
    response = webob.exc.HTTPMethodNotAllowed(
        _('The method specified is not allowed for this resource.'),
        headers=headers, json_formatter=util.json_error_formatter)
    return response(environ, start_response)
Ejemplo n.º 28
0
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']
    context.can(policies.PROVIDER_USAGES)
    uuid = util.wsgi_path_item(req.environ, 'uuid')
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]

    # 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 = rp_obj.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
            })

    usage = usage_obj.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'
    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
Ejemplo n.º 29
0
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 = rp_obj.ResourceClass.get_by_name(context, name)
    try:
        rc.destroy()
    except exception.ResourceClassCannotDeleteStandard as exc:
        raise webob.exc.HTTPBadRequest(
            _('Error in delete resource class: %(error)s') % {'error': exc})
    except exception.ResourceClassInUse as exc:
        raise webob.exc.HTTPConflict(
            _('Error in delete resource class: %(error)s') % {'error': exc})
    req.response.status = 204
    req.response.content_type = None
    return req.response
Ejemplo n.º 30
0
def associate(req):
    """Associate a resource provider with one or more other resource providers.
    This is commonly used to create a sharing relationship among resource
    providers.

    On success return a 204 and an empty body.
    """
    uuid = util.wsgi_path_item(req.environ, "uuid")
    context = req.environ["placement.context"]
    context.can(policies.UPDATE)
    schema = rp_schema.POST_RPS_ASSOCIATE
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    data = util.extract_json(req.body, schema)
    target_uuids = data.get("targets", [])

    # The containing application will catch a not found here.
    resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid)
    rp_obj.associate(context, resource_provider, target_uuids)

    response = req.response
    response.status = 204