Exemple #1
0
 def _create_allocations():
     try:
         # NOTE(melwitt): Group the consumer and allocation database updates
         # in a single transaction so that updates get rolled back
         # automatically in the event of a consumer generation conflict.
         _update_consumers_and_create_allocations(context)
     except Exception:
         with excutils.save_and_reraise_exception():
             allocation.delete_consumers(new_consumers_created)
Exemple #2
0
def reshape(req):
    context = req.environ['placement.context']
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    context.can(policies.RESHAPE)
    data = util.extract_json(req.body, schema.POST_RESHAPER_SCHEMA)
    inventories = data['inventories']
    allocations = data['allocations']

    # Get the current inventory for all the RPs included. Compare it to the
    # passed inventories; if they don't have the same resources, then something
    # is off, and reject the reshape.
    curr_resources = []
    proposed_resources = []
    for rp_uuid, inv_data in inventories.items():
        curr_resources += list(
            rp_obj.get_current_inventory_resources(context,
                                                   rp_uuid,
                                                   include_total=True))
        for rc_name, rc_vals in inv_data["inventories"].items():
            proposed_resources.append((rc_name, rc_vals["total"]))
    curr_resources.sort()
    proposed_resources.sort()
    if not curr_resources == proposed_resources:
        raise webob.exc.HTTPBadRequest("Proposed reshaped inventory does not "
                                       "match existing inventory.")

    # Now check the allocations to make sure that they match
    curr_allocs = defaultdict(int)
    proposed_allocs = defaultdict(int)

    alloc_data = [itm["allocations"] for itm in allocations.values()]
    # First, get all the unique RPs
    rp_dict_keys = [itm.keys() for itm in alloc_data]
    # Convert dict_keys data to lists
    rp_lists = [list(itm) for itm in rp_dict_keys]
    # Collapse the lists of lists to a single list, and get the unique values
    rp_set = set(list(itertools.chain(*rp_lists)))
    # Get the current allocations for each RP
    for rp in rp_set:
        alloc_dict = rp_obj.get_allocated_inventory(context, rp)
        for key, val in alloc_dict.items():
            curr_allocs[key] += val
    # Now sum up the proposed allocations. The RC name and amount are in a
    # deeply-nested dict.
    for rdict in alloc_data:
        for _, rsrc in rdict.items():
            for _, rc in rsrc.items():
                for rc_name, amt in rc.items():
                    proposed_allocs[rc_name] += amt

    if not curr_allocs == proposed_allocs:
        raise webob.HTTPBadRequest("Proposed reshaped allocations do not "
                                   "match existing allocations.")

    # The inventories and allocations match up, so now just switch the
    # connections among the entities.
    rp_obj.reshape(context, inventories, allocations)
    # ^^^ This needs to be written

    # We're going to create several lists of Inventory objects, keyed by rp
    # uuid.
    inventory_by_rp = {}

    # TODO(cdent): this has overlaps with inventory:set_inventories
    # and is a mess of bad names and lack of method extraction.
    for rp_uuid, inventory_data in inventories.items():
        try:
            resource_provider = rp_obj.ResourceProvider.get_by_uuid(
                context, rp_uuid)
        except exception.NotFound as exc:
            raise webob.exc.HTTPBadRequest(
                'Resource provider %(rp_uuid)s in inventories not found: '
                '%(error)s' % {
                    'rp_uuid': rp_uuid,
                    'error': exc
                },
                comment=errors.RESOURCE_PROVIDER_NOT_FOUND)

        # Do an early generation check.
        generation = inventory_data['resource_provider_generation']
        if generation != resource_provider.generation:
            raise webob.exc.HTTPConflict(
                'resource provider generation conflict for provider %(rp)s: '
                'actual: %(actual)s, given: %(given)s' % {
                    'rp': rp_uuid,
                    'actual': resource_provider.generation,
                    'given': generation
                },
                comment=errors.CONCURRENT_UPDATE)

        inv_list = []
        for res_class, raw_inventory in inventory_data['inventories'].items():
            inv_data = copy.copy(inventory.INVENTORY_DEFAULTS)
            inv_data.update(raw_inventory)
            inv_object = inventory.make_inventory_object(
                resource_provider, res_class, **inv_data)
            inv_list.append(inv_object)
        inventory_by_rp[resource_provider] = inv_list

    # Make the consumer objects associated with the allocations.
    consumers, new_consumers_created = allocation.inspect_consumers(
        context, allocations, want_version)

    # Nest exception handling so that any exception results in new consumer
    # objects being deleted, then reraise for translating to HTTP exceptions.
    try:
        try:
            # When these allocations are created they get resource provider
            # objects which are different instances (usually with the same
            # data) from those loaded above when creating inventory objects.
            # The reshape method below is responsible for ensuring that the
            # resource providers and their generations do not conflict.
            allocation_objects = allocation.create_allocation_list(
                context, allocations, consumers)

            reshaper.reshape(context, inventory_by_rp, allocation_objects)
        except Exception:
            with excutils.save_and_reraise_exception():
                allocation.delete_consumers(new_consumers_created)
    # Generation conflict is a (rare) possibility in a few different
    # places in reshape().
    except exception.ConcurrentUpdateDetected as exc:
        raise webob.exc.HTTPConflict('update conflict: %(error)s' %
                                     {'error': exc},
                                     comment=errors.CONCURRENT_UPDATE)
    # A NotFound here means a resource class that does not exist was named
    except exception.NotFound as exc:
        raise webob.exc.HTTPBadRequest('malformed reshaper data: %(error)s' %
                                       {'error': exc})
    # Distinguish inventory in use (has allocations on it)...
    except exception.InventoryInUse as exc:
        raise webob.exc.HTTPConflict('update conflict: %(error)s' %
                                     {'error': exc},
                                     comment=errors.INVENTORY_INUSE)
    # ...from allocations which won't fit for a variety of reasons.
    except exception.InvalidInventory as exc:
        raise webob.exc.HTTPConflict(
            'Unable to allocate inventory: %(error)s' % {'error': exc})

    req.response.status = 204
    req.response.content_type = None
    return req.response
def reshape(req):
    context = req.environ['placement.context']
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    context.can(policies.RESHAPE)
    data = util.extract_json(req.body, schema.POST_RESHAPER_SCHEMA)
    inventories = data['inventories']
    allocations = data['allocations']
    # We're going to create several lists of Inventory objects, keyed by rp
    # uuid.
    inventory_by_rp = {}

    # TODO(cdent): this has overlaps with inventory:set_inventories
    # and is a mess of bad names and lack of method extraction.
    for rp_uuid, inventory_data in inventories.items():
        try:
            resource_provider = rp_obj.ResourceProvider.get_by_uuid(
                context, rp_uuid)
        except exception.NotFound as exc:
            raise webob.exc.HTTPBadRequest(
                'Resource provider %(rp_uuid)s in inventories not found: '
                '%(error)s' % {
                    'rp_uuid': rp_uuid,
                    'error': exc
                },
                comment=errors.RESOURCE_PROVIDER_NOT_FOUND)

        # Do an early generation check.
        generation = inventory_data['resource_provider_generation']
        if generation != resource_provider.generation:
            raise webob.exc.HTTPConflict(
                'resource provider generation conflict for provider %(rp)s: '
                'actual: %(actual)s, given: %(given)s' % {
                    'rp': rp_uuid,
                    'actual': resource_provider.generation,
                    'given': generation
                },
                comment=errors.CONCURRENT_UPDATE)

        inv_list = []
        for res_class, raw_inventory in inventory_data['inventories'].items():
            inv_data = copy.copy(inventory.INVENTORY_DEFAULTS)
            inv_data.update(raw_inventory)
            inv_object = inventory.make_inventory_object(
                resource_provider, res_class, **inv_data)
            inv_list.append(inv_object)
        inventory_by_rp[resource_provider] = inv_list

    # Make the consumer objects associated with the allocations.
    consumers, new_consumers_created = allocation.inspect_consumers(
        context, allocations, want_version)

    # Nest exception handling so that any exception results in new consumer
    # objects being deleted, then reraise for translating to HTTP exceptions.
    try:
        try:
            # When these allocations are created they get resource provider
            # objects which are different instances (usually with the same
            # data) from those loaded above when creating inventory objects.
            # The reshape method below is responsible for ensuring that the
            # resource providers and their generations do not conflict.
            allocation_objects = allocation.create_allocation_list(
                context, allocations, consumers)

            reshaper.reshape(context, inventory_by_rp, allocation_objects)
        except Exception:
            with excutils.save_and_reraise_exception():
                allocation.delete_consumers(new_consumers_created)
    # Generation conflict is a (rare) possibility in a few different
    # places in reshape().
    except exception.ConcurrentUpdateDetected as exc:
        raise webob.exc.HTTPConflict('update conflict: %(error)s' %
                                     {'error': exc},
                                     comment=errors.CONCURRENT_UPDATE)
    # A NotFound here means a resource class that does not exist was named
    except exception.NotFound as exc:
        raise webob.exc.HTTPBadRequest('malformed reshaper data: %(error)s' %
                                       {'error': exc})
    # Distinguish inventory in use (has allocations on it)...
    except exception.InventoryInUse as exc:
        raise webob.exc.HTTPConflict('update conflict: %(error)s' %
                                     {'error': exc},
                                     comment=errors.INVENTORY_INUSE)
    # ...from allocations which won't fit for a variety of reasons.
    except exception.InvalidInventory as exc:
        raise webob.exc.HTTPConflict(
            'Unable to allocate inventory: %(error)s' % {'error': exc})

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