Beispiel #1
0
    def test_reshape(self):
        """We set up the following scenario:

        BEFORE: single compute node setup

          A single compute node with:
            - VCPU, MEMORY_MB, DISK_GB inventory
            - Two instances consuming CPU, RAM and DISK from that compute node

        AFTER: hierarchical + shared storage setup

          A compute node parent provider with:
            - MEMORY_MB
          Two NUMA node child providers containing:
            - VCPU
          Shared storage provider with:
            - DISK_GB
          Both instances have their resources split among the providers and
          shared storage accordingly
        """
        # First create our consumers
        i1_uuid = uuids.instance1
        i1_consumer = consumer_obj.Consumer(self.ctx,
                                            uuid=i1_uuid,
                                            user=self.user_obj,
                                            project=self.project_obj)
        i1_consumer.create()

        i2_uuid = uuids.instance2
        i2_consumer = consumer_obj.Consumer(self.ctx,
                                            uuid=i2_uuid,
                                            user=self.user_obj,
                                            project=self.project_obj)
        i2_consumer.create()

        cn1 = self._create_provider('cn1')
        tb.add_inventory(cn1, 'VCPU', 16)
        tb.add_inventory(cn1, 'MEMORY_MB', 32768)
        tb.add_inventory(cn1, 'DISK_GB', 1000)

        # Allocate both instances against the single compute node
        for consumer in (i1_consumer, i2_consumer):
            allocs = [
                rp_obj.Allocation(self.ctx,
                                  resource_provider=cn1,
                                  resource_class='VCPU',
                                  consumer=consumer,
                                  used=2),
                rp_obj.Allocation(self.ctx,
                                  resource_provider=cn1,
                                  resource_class='MEMORY_MB',
                                  consumer=consumer,
                                  used=1024),
                rp_obj.Allocation(self.ctx,
                                  resource_provider=cn1,
                                  resource_class='DISK_GB',
                                  consumer=consumer,
                                  used=100),
            ]
            alloc_list = rp_obj.AllocationList(self.ctx, objects=allocs)
            alloc_list.replace_all()

        # Verify we have the allocations we expect for the BEFORE scenario
        before_allocs_i1 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i1_uuid)
        self.assertEqual(3, len(before_allocs_i1))
        self.assertEqual(cn1.uuid, before_allocs_i1[0].resource_provider.uuid)
        before_allocs_i2 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i2_uuid)
        self.assertEqual(3, len(before_allocs_i2))
        self.assertEqual(cn1.uuid, before_allocs_i2[2].resource_provider.uuid)

        # Before we issue the actual reshape() call, we need to first create
        # the child providers and sharing storage provider. These are actions
        # that the virt driver or external agent is responsible for performing
        # *before* attempting any reshape activity.
        cn1_numa0 = self._create_provider('cn1_numa0', parent=cn1.uuid)
        cn1_numa1 = self._create_provider('cn1_numa1', parent=cn1.uuid)
        ss = self._create_provider('ss')

        # OK, now emulate the call to POST /reshaper that will be triggered by
        # a virt driver wanting to replace the world and change its modeling
        # from a single provider to a nested provider tree along with a sharing
        # storage provider.
        after_inventories = {
            # cn1 keeps the RAM only
            cn1:
            rp_obj.InventoryList(self.ctx,
                                 objects=[
                                     rp_obj.Inventory(
                                         self.ctx,
                                         resource_provider=cn1,
                                         resource_class='MEMORY_MB',
                                         total=32768,
                                         reserved=0,
                                         max_unit=32768,
                                         min_unit=1,
                                         step_size=1,
                                         allocation_ratio=1.0),
                                 ]),
            # each NUMA node gets half of the CPUs
            cn1_numa0:
            rp_obj.InventoryList(self.ctx,
                                 objects=[
                                     rp_obj.Inventory(
                                         self.ctx,
                                         resource_provider=cn1_numa0,
                                         resource_class='VCPU',
                                         total=8,
                                         reserved=0,
                                         max_unit=8,
                                         min_unit=1,
                                         step_size=1,
                                         allocation_ratio=1.0),
                                 ]),
            cn1_numa1:
            rp_obj.InventoryList(self.ctx,
                                 objects=[
                                     rp_obj.Inventory(
                                         self.ctx,
                                         resource_provider=cn1_numa1,
                                         resource_class='VCPU',
                                         total=8,
                                         reserved=0,
                                         max_unit=8,
                                         min_unit=1,
                                         step_size=1,
                                         allocation_ratio=1.0),
                                 ]),
            # The sharing provider gets a bunch of disk
            ss:
            rp_obj.InventoryList(self.ctx,
                                 objects=[
                                     rp_obj.Inventory(self.ctx,
                                                      resource_provider=ss,
                                                      resource_class='DISK_GB',
                                                      total=100000,
                                                      reserved=0,
                                                      max_unit=1000,
                                                      min_unit=1,
                                                      step_size=1,
                                                      allocation_ratio=1.0),
                                 ]),
        }
        # We do a fetch from the DB for each instance to get its latest
        # generation. This would be done by the resource tracker or scheduler
        # report client before issuing the call to reshape() because the
        # consumers representing the two instances above will have had their
        # generations incremented in the original call to PUT
        # /allocations/{consumer_uuid}
        i1_consumer = consumer_obj.Consumer.get_by_uuid(self.ctx, i1_uuid)
        i2_consumer = consumer_obj.Consumer.get_by_uuid(self.ctx, i2_uuid)
        after_allocs = rp_obj.AllocationList(
            self.ctx,
            objects=[
                # instance1 gets VCPU from NUMA0, MEMORY_MB from cn1 and DISK_GB
                # from the sharing storage provider
                rp_obj.Allocation(self.ctx,
                                  resource_provider=cn1_numa0,
                                  resource_class='VCPU',
                                  consumer=i1_consumer,
                                  used=2),
                rp_obj.Allocation(self.ctx,
                                  resource_provider=cn1,
                                  resource_class='MEMORY_MB',
                                  consumer=i1_consumer,
                                  used=1024),
                rp_obj.Allocation(self.ctx,
                                  resource_provider=ss,
                                  resource_class='DISK_GB',
                                  consumer=i1_consumer,
                                  used=100),
                # instance2 gets VCPU from NUMA1, MEMORY_MB from cn1 and DISK_GB
                # from the sharing storage provider
                rp_obj.Allocation(self.ctx,
                                  resource_provider=cn1_numa1,
                                  resource_class='VCPU',
                                  consumer=i2_consumer,
                                  used=2),
                rp_obj.Allocation(self.ctx,
                                  resource_provider=cn1,
                                  resource_class='MEMORY_MB',
                                  consumer=i2_consumer,
                                  used=1024),
                rp_obj.Allocation(self.ctx,
                                  resource_provider=ss,
                                  resource_class='DISK_GB',
                                  consumer=i2_consumer,
                                  used=100),
            ])
        rp_obj.reshape(self.ctx, after_inventories, after_allocs)

        # Verify that the inventories have been moved to the appropriate
        # providers in the AFTER scenario

        # The root compute node should only have MEMORY_MB, nothing else
        cn1_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, cn1)
        self.assertEqual(1, len(cn1_inv))
        self.assertEqual('MEMORY_MB', cn1_inv[0].resource_class)
        self.assertEqual(32768, cn1_inv[0].total)
        # Each NUMA node should only have half the original VCPU, nothing else
        numa0_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, cn1_numa0)
        self.assertEqual(1, len(numa0_inv))
        self.assertEqual('VCPU', numa0_inv[0].resource_class)
        self.assertEqual(8, numa0_inv[0].total)
        numa1_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, cn1_numa1)
        self.assertEqual(1, len(numa1_inv))
        self.assertEqual('VCPU', numa1_inv[0].resource_class)
        self.assertEqual(8, numa1_inv[0].total)
        # The sharing storage provider should only have DISK_GB, nothing else
        ss_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, ss)
        self.assertEqual(1, len(ss_inv))
        self.assertEqual('DISK_GB', ss_inv[0].resource_class)
        self.assertEqual(100000, ss_inv[0].total)

        # Verify we have the allocations we expect for the AFTER scenario
        after_allocs_i1 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i1_uuid)
        self.assertEqual(3, len(after_allocs_i1))
        # Our VCPU allocation should be in the NUMA0 node
        vcpu_alloc = alloc_for_rc(after_allocs_i1, 'VCPU')
        self.assertIsNotNone(vcpu_alloc)
        self.assertEqual(cn1_numa0.uuid, vcpu_alloc.resource_provider.uuid)
        # Our DISK_GB allocation should be in the sharing provider
        disk_alloc = alloc_for_rc(after_allocs_i1, 'DISK_GB')
        self.assertIsNotNone(disk_alloc)
        self.assertEqual(ss.uuid, disk_alloc.resource_provider.uuid)
        # And our MEMORY_MB should remain on the root compute node
        ram_alloc = alloc_for_rc(after_allocs_i1, 'MEMORY_MB')
        self.assertIsNotNone(ram_alloc)
        self.assertEqual(cn1.uuid, ram_alloc.resource_provider.uuid)

        after_allocs_i2 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i2_uuid)
        self.assertEqual(3, len(after_allocs_i2))
        # Our VCPU allocation should be in the NUMA1 node
        vcpu_alloc = alloc_for_rc(after_allocs_i2, 'VCPU')
        self.assertIsNotNone(vcpu_alloc)
        self.assertEqual(cn1_numa1.uuid, vcpu_alloc.resource_provider.uuid)
        # Our DISK_GB allocation should be in the sharing provider
        disk_alloc = alloc_for_rc(after_allocs_i2, 'DISK_GB')
        self.assertIsNotNone(disk_alloc)
        self.assertEqual(ss.uuid, disk_alloc.resource_provider.uuid)
        # And our MEMORY_MB should remain on the root compute node
        ram_alloc = alloc_for_rc(after_allocs_i2, 'MEMORY_MB')
        self.assertIsNotNone(ram_alloc)
        self.assertEqual(cn1.uuid, ram_alloc.resource_provider.uuid)
Beispiel #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']
    # We're going to create several InventoryList, 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: '
                  'actual: %(actual)s, given: %(given)s') %
                {'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_obj = inventory.make_inventory_object(
                resource_provider, res_class, **inv_data)
            inv_list.append(inv_obj)
        inventory_by_rp[resource_provider] = rp_obj.InventoryList(
            objects=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)

            rp_obj.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
Beispiel #3
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']
    # We're going to create several InventoryList, 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: '
                  'actual: %(actual)s, given: %(given)s') % {
                      '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_obj = inventory.make_inventory_object(resource_provider,
                                                      res_class, **inv_data)
            inv_list.append(inv_obj)
        inventory_by_rp[resource_provider] = rp_obj.InventoryList(
            objects=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)

            rp_obj.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
Beispiel #4
0
    def test_reshape(self):
        """We set up the following scenario:

        BEFORE: single compute node setup

          A single compute node with:
            - VCPU, MEMORY_MB, DISK_GB inventory
            - Two instances consuming CPU, RAM and DISK from that compute node

        AFTER: hierarchical + shared storage setup

          A compute node parent provider with:
            - MEMORY_MB
          Two NUMA node child providers containing:
            - VCPU
          Shared storage provider with:
            - DISK_GB
          Both instances have their resources split among the providers and
          shared storage accordingly
        """
        # First create our consumers
        i1_uuid = uuids.instance1
        i1_consumer = consumer_obj.Consumer(
            self.ctx, uuid=i1_uuid, user=self.user_obj,
            project=self.project_obj)
        i1_consumer.create()

        i2_uuid = uuids.instance2
        i2_consumer = consumer_obj.Consumer(
            self.ctx, uuid=i2_uuid, user=self.user_obj,
            project=self.project_obj)
        i2_consumer.create()

        cn1 = self._create_provider('cn1')
        tb.add_inventory(cn1, 'VCPU', 16)
        tb.add_inventory(cn1, 'MEMORY_MB', 32768)
        tb.add_inventory(cn1, 'DISK_GB', 1000)

        # Allocate both instances against the single compute node
        for consumer in (i1_consumer, i2_consumer):
            allocs = [
                rp_obj.Allocation(
                    self.ctx, resource_provider=cn1,
                    resource_class='VCPU', consumer=consumer, used=2),
                rp_obj.Allocation(
                    self.ctx, resource_provider=cn1,
                    resource_class='MEMORY_MB', consumer=consumer, used=1024),
                rp_obj.Allocation(
                    self.ctx, resource_provider=cn1,
                    resource_class='DISK_GB', consumer=consumer, used=100),
            ]
            alloc_list = rp_obj.AllocationList(self.ctx, objects=allocs)
            alloc_list.replace_all()

        # Verify we have the allocations we expect for the BEFORE scenario
        before_allocs_i1 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i1_uuid)
        self.assertEqual(3, len(before_allocs_i1))
        self.assertEqual(cn1.uuid, before_allocs_i1[0].resource_provider.uuid)
        before_allocs_i2 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i2_uuid)
        self.assertEqual(3, len(before_allocs_i2))
        self.assertEqual(cn1.uuid, before_allocs_i2[2].resource_provider.uuid)

        # Before we issue the actual reshape() call, we need to first create
        # the child providers and sharing storage provider. These are actions
        # that the virt driver or external agent is responsible for performing
        # *before* attempting any reshape activity.
        cn1_numa0 = self._create_provider('cn1_numa0', parent=cn1.uuid)
        cn1_numa1 = self._create_provider('cn1_numa1', parent=cn1.uuid)
        ss = self._create_provider('ss')

        # OK, now emulate the call to POST /reshaper that will be triggered by
        # a virt driver wanting to replace the world and change its modeling
        # from a single provider to a nested provider tree along with a sharing
        # storage provider.
        after_inventories = {
            # cn1 keeps the RAM only
            cn1.uuid: rp_obj.InventoryList(self.ctx, objects=[
                rp_obj.Inventory(
                    self.ctx, resource_provider=cn1,
                    resource_class='MEMORY_MB', total=32768, reserved=0,
                    max_unit=32768, min_unit=1, step_size=1,
                    allocation_ratio=1.0),
            ]),
            # each NUMA node gets half of the CPUs
            cn1_numa0.uuid: rp_obj.InventoryList(self.ctx, objects=[
                rp_obj.Inventory(
                    self.ctx, resource_provider=cn1_numa0,
                    resource_class='VCPU', total=8, reserved=0,
                    max_unit=8, min_unit=1, step_size=1,
                    allocation_ratio=1.0),
            ]),
            cn1_numa1.uuid: rp_obj.InventoryList(self.ctx, objects=[
                rp_obj.Inventory(
                    self.ctx, resource_provider=cn1_numa1,
                    resource_class='VCPU', total=8, reserved=0,
                    max_unit=8, min_unit=1, step_size=1,
                    allocation_ratio=1.0),
            ]),
            # The sharing provider gets a bunch of disk
            ss.uuid: rp_obj.InventoryList(self.ctx, objects=[
                rp_obj.Inventory(
                    self.ctx, resource_provider=ss,
                    resource_class='DISK_GB', total=100000, reserved=0,
                    max_unit=1000, min_unit=1, step_size=1,
                    allocation_ratio=1.0),
            ]),
        }
        # We do a fetch from the DB for each instance to get its latest
        # generation. This would be done by the resource tracker or scheduler
        # report client before issuing the call to reshape() because the
        # consumers representing the two instances above will have had their
        # generations incremented in the original call to PUT
        # /allocations/{consumer_uuid}
        i1_consumer = consumer_obj.Consumer.get_by_uuid(self.ctx, i1_uuid)
        i2_consumer = consumer_obj.Consumer.get_by_uuid(self.ctx, i2_uuid)
        after_allocs = rp_obj.AllocationList(self.ctx, objects=[
            # instance1 gets VCPU from NUMA0, MEMORY_MB from cn1 and DISK_GB
            # from the sharing storage provider
            rp_obj.Allocation(
                self.ctx, resource_provider=cn1_numa0, resource_class='VCPU',
                consumer=i1_consumer, used=2),
            rp_obj.Allocation(
                self.ctx, resource_provider=cn1, resource_class='MEMORY_MB',
                consumer=i1_consumer, used=1024),
            rp_obj.Allocation(
                self.ctx, resource_provider=ss, resource_class='DISK_GB',
                consumer=i1_consumer, used=100),
            # instance2 gets VCPU from NUMA1, MEMORY_MB from cn1 and DISK_GB
            # from the sharing storage provider
            rp_obj.Allocation(
                self.ctx, resource_provider=cn1_numa1, resource_class='VCPU',
                consumer=i2_consumer, used=2),
            rp_obj.Allocation(
                self.ctx, resource_provider=cn1, resource_class='MEMORY_MB',
                consumer=i2_consumer, used=1024),
            rp_obj.Allocation(
                self.ctx, resource_provider=ss, resource_class='DISK_GB',
                consumer=i2_consumer, used=100),
        ])
        rp_obj.reshape(self.ctx, after_inventories, after_allocs)

        # Verify that the inventories have been moved to the appropriate
        # providers in the AFTER scenario

        # The root compute node should only have MEMORY_MB, nothing else
        cn1_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, cn1)
        self.assertEqual(1, len(cn1_inv))
        self.assertEqual('MEMORY_MB', cn1_inv[0].resource_class)
        self.assertEqual(32768, cn1_inv[0].total)
        # Each NUMA node should only have half the original VCPU, nothing else
        numa0_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, cn1_numa0)
        self.assertEqual(1, len(numa0_inv))
        self.assertEqual('VCPU', numa0_inv[0].resource_class)
        self.assertEqual(8, numa0_inv[0].total)
        numa1_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, cn1_numa1)
        self.assertEqual(1, len(numa1_inv))
        self.assertEqual('VCPU', numa1_inv[0].resource_class)
        self.assertEqual(8, numa1_inv[0].total)
        # The sharing storage provider should only have DISK_GB, nothing else
        ss_inv = rp_obj.InventoryList.get_all_by_resource_provider(
            self.ctx, ss)
        self.assertEqual(1, len(ss_inv))
        self.assertEqual('DISK_GB', ss_inv[0].resource_class)
        self.assertEqual(100000, ss_inv[0].total)

        # Verify we have the allocations we expect for the AFTER scenario
        after_allocs_i1 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i1_uuid)
        self.assertEqual(3, len(after_allocs_i1))
        # Our VCPU allocation should be in the NUMA0 node
        vcpu_alloc = alloc_for_rc(after_allocs_i1, 'VCPU')
        self.assertIsNotNone(vcpu_alloc)
        self.assertEqual(cn1_numa0.uuid, vcpu_alloc.resource_provider.uuid)
        # Our DISK_GB allocation should be in the sharing provider
        disk_alloc = alloc_for_rc(after_allocs_i1, 'DISK_GB')
        self.assertIsNotNone(disk_alloc)
        self.assertEqual(ss.uuid, disk_alloc.resource_provider.uuid)
        # And our MEMORY_MB should remain on the root compute node
        ram_alloc = alloc_for_rc(after_allocs_i1, 'MEMORY_MB')
        self.assertIsNotNone(ram_alloc)
        self.assertEqual(cn1.uuid, ram_alloc.resource_provider.uuid)

        after_allocs_i2 = rp_obj.AllocationList.get_all_by_consumer_id(
            self.ctx, i2_uuid)
        self.assertEqual(3, len(after_allocs_i2))
        # Our VCPU allocation should be in the NUMA1 node
        vcpu_alloc = alloc_for_rc(after_allocs_i2, 'VCPU')
        self.assertIsNotNone(vcpu_alloc)
        self.assertEqual(cn1_numa1.uuid, vcpu_alloc.resource_provider.uuid)
        # Our DISK_GB allocation should be in the sharing provider
        disk_alloc = alloc_for_rc(after_allocs_i2, 'DISK_GB')
        self.assertIsNotNone(disk_alloc)
        self.assertEqual(ss.uuid, disk_alloc.resource_provider.uuid)
        # And our MEMORY_MB should remain on the root compute node
        ram_alloc = alloc_for_rc(after_allocs_i2, 'MEMORY_MB')
        self.assertIsNotNone(ram_alloc)
        self.assertEqual(cn1.uuid, ram_alloc.resource_provider.uuid)