def start_fixture(self): super(AllocationFixture, self).start_fixture() self.context = context.get_admin_context() # Stealing from the super rp_name = os.environ['RP_NAME'] rp_uuid = os.environ['RP_UUID'] rp = objects.ResourceProvider(self.context, name=rp_name, uuid=rp_uuid) rp.create() inventory = objects.Inventory(self.context, resource_provider=rp, resource_class='DISK_GB', total=2048) inventory.obj_set_defaults() rp.add_inventory(inventory) allocation = objects.Allocation(self.context, resource_provider=rp, resource_class='DISK_GB', consumer_id=uuidutils.generate_uuid(), used=512) allocation.create() allocation = objects.Allocation(self.context, resource_provider=rp, resource_class='DISK_GB', consumer_id=uuidutils.generate_uuid(), used=512) allocation.create()
def start_fixture(self): super(AllocationFixture, self).start_fixture() self.context = context.get_admin_context() # Stealing from the super rp_name = os.environ['RP_NAME'] rp_uuid = os.environ['RP_UUID'] rp = objects.ResourceProvider(self.context, name=rp_name, uuid=rp_uuid) rp.create() # Create some DISK_GB inventory and allocations. inventory = objects.Inventory(self.context, resource_provider=rp, resource_class='DISK_GB', total=2048, step_size=10, min_unit=10, max_unit=600) inventory.obj_set_defaults() rp.add_inventory(inventory) alloc1 = objects.Allocation(self.context, resource_provider=rp, resource_class='DISK_GB', consumer_id=uuidutils.generate_uuid(), used=500) alloc2 = objects.Allocation(self.context, resource_provider=rp, resource_class='DISK_GB', consumer_id=uuidutils.generate_uuid(), used=500) alloc_list = objects.AllocationList(self.context, objects=[alloc1, alloc2]) alloc_list.create_all() # Create some VCPU inventory and allocations. inventory = objects.Inventory(self.context, resource_provider=rp, resource_class='VCPU', total=8, max_unit=4) inventory.obj_set_defaults() rp.add_inventory(inventory) alloc1 = objects.Allocation(self.context, resource_provider=rp, resource_class='VCPU', consumer_id=uuidutils.generate_uuid(), used=2) alloc2 = objects.Allocation(self.context, resource_provider=rp, resource_class='VCPU', consumer_id=uuidutils.generate_uuid(), used=4) alloc_list = objects.AllocationList(self.context, objects=[alloc1, alloc2]) alloc_list.create_all() # The ALT_RP_XXX variables are for a resource provider that has # not been created in the Allocation fixture os.environ['ALT_RP_UUID'] = uuidutils.generate_uuid() os.environ['ALT_RP_NAME'] = uuidutils.generate_uuid()
def test_get_all_multiple_providers(self): # This confirms that the join with resource provider is # behaving. rp1, allocation1 = self._make_allocation(uuidsentinel.rp1) rp2, allocation2 = self._make_allocation(uuidsentinel.rp2) allocations = objects.AllocationList.get_all_by_resource_provider_uuid( self.context, rp1.uuid) self.assertEqual(1, len(allocations)) self.assertEqual(rp1.id, allocations[0].resource_provider.id) self.assertEqual(allocation1.resource_provider.id, allocations[0].resource_provider.id) # add more allocations for the first resource provider # of the same class alloc3 = objects.Allocation( self.context, consumer_id=uuidsentinel.consumer1, resource_class=fields.ResourceClass.DISK_GB, resource_provider=rp1, used=2, ) alloc3.create() allocations = objects.AllocationList.get_all_by_resource_provider_uuid( self.context, rp1.uuid) self.assertEqual(2, len(allocations)) # add more allocations for the first resource provider # of a different class alloc4 = objects.Allocation( self.context, consumer_id=uuidsentinel.consumer1, resource_class=fields.ResourceClass.IPV4_ADDRESS, resource_provider=rp1, used=4, ) alloc4.create() allocations = objects.AllocationList.get_all_by_resource_provider_uuid( self.context, rp1.uuid) self.assertEqual(3, len(allocations)) self.assertEqual(rp1.uuid, allocations[0].resource_provider.uuid) allocations = objects.AllocationList.get_all_by_resource_provider_uuid( self.context, rp2.uuid) self.assertEqual(1, len(allocations)) self.assertEqual(rp2.uuid, allocations[0].resource_provider.uuid) self.assertIn( fields.ResourceClass.DISK_GB, [allocation.resource_class for allocation in allocations]) self.assertNotIn( fields.ResourceClass.IPV4_ADDRESS, [allocation.resource_class for allocation in allocations])
def test_create_list_and_delete_allocation(self): resource_provider = objects.ResourceProvider( context=self.context, uuid=uuidsentinel.allocation_resource_provider, name=uuidsentinel.allocation_resource_name) resource_provider.create() resource_class = fields.ResourceClass.DISK_GB disk_allocation = objects.Allocation( context=self.context, resource_provider=resource_provider, **DISK_ALLOCATION) disk_allocation.create() self.assertEqual(resource_class, disk_allocation.resource_class) self.assertEqual(resource_provider, disk_allocation.resource_provider) self.assertEqual(DISK_ALLOCATION['used'], disk_allocation.used) self.assertEqual(DISK_ALLOCATION['consumer_id'], disk_allocation.consumer_id) self.assertIsInstance(disk_allocation.id, int) allocations = objects.AllocationList.get_all_by_resource_provider_uuid( self.context, resource_provider.uuid) self.assertEqual(1, len(allocations)) self.assertEqual(DISK_ALLOCATION['used'], allocations[0].used) allocations[0].destroy() allocations = objects.AllocationList.get_all_by_resource_provider_uuid( self.context, resource_provider.uuid) self.assertEqual(0, len(allocations))
def _check_create_allocations(self, inventory_kwargs, bad_used, good_used): consumer_uuid = uuidsentinel.consumer rp_class = fields.ResourceClass.DISK_GB rp = self._make_rp_and_inventory(resource_class=rp_class, **inventory_kwargs) # allocation, bad step_size allocation = objects.Allocation(resource_provider=rp, consumer_id=consumer_uuid, resource_class=rp_class, used=bad_used) allocation_list = objects.AllocationList(self.context, objects=[allocation]) self.assertRaises(exception.InvalidAllocationConstraintsViolated, allocation_list.create_all) # correct for step size allocation.used = good_used allocation_list = objects.AllocationList(self.context, objects=[allocation]) allocation_list.create_all() # check usage self._validate_usage(rp, allocation.used)
def test_create(self, mock_ensure_cache): rp = objects.ResourceProvider(context=self.context, uuid=_RESOURCE_PROVIDER_UUID, name=_RESOURCE_PROVIDER_NAME) rp.create() inv = objects.Inventory(context=self.context, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, total=16, reserved=2, min_unit=1, max_unit=8, step_size=1, allocation_ratio=1.0) inv.create() obj = objects.Allocation(context=self.context, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, consumer_id=uuids.fake_instance, used=8) alloc_list = objects.AllocationList(self.context, objects=[obj]) alloc_list.create_all() rp_al = resource_provider.AllocationList saved_allocations = rp_al.get_all_by_resource_provider_uuid( self.context, rp.uuid) self.assertEqual(1, len(saved_allocations)) self.assertEqual(obj.used, saved_allocations[0].used)
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}, json_formatter=util.json_error_formatter) 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}, json_formatter=util.json_error_formatter) except exception.ConcurrentUpdateDetected as exc: LOG.exception(_LE("Concurrent Update")) raise webob.exc.HTTPConflict( _('Inventory changed while attempting to allocate: %(error)s') % {'error': exc}, json_formatter=util.json_error_formatter) req.response.status = 204 req.response.content_type = None return req.response
def test_create_with_id_fails(self): rp = objects.ResourceProvider(id=_RESOURCE_PROVIDER_ID, uuid=uuids.resource_provider) obj = objects.Allocation(context=self.context, id=99, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, consumer_id=uuids.fake_instance, used=8) self.assertRaises(exception.ObjectActionError, obj.create)
def _make_allocation(self, rp_uuid=None): rp_uuid = rp_uuid or uuidsentinel.allocation_resource_provider rp = objects.ResourceProvider(context=self.context, uuid=rp_uuid, name=rp_uuid) rp.create() alloc = objects.Allocation(self.context, resource_provider=rp, **DISK_ALLOCATION) alloc.create() return rp, alloc
def test_create(self, mock_db_create, mock_ensure_cache): rp = objects.ResourceProvider(id=_RESOURCE_PROVIDER_ID, uuid=uuids.resource_provider) obj = objects.Allocation(context=self.context, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, consumer_id=uuids.fake_instance, used=8) obj.create() self.assertEqual(_ALLOCATION_ID, obj.id) expected = dict(_ALLOCATION_DB) expected.pop('id') mock_db_create.assert_called_once_with(self.context, expected)
def test_custom_resource_raises(self): """Ensure that if we send an inventory object to a backversioned 1.0 receiver, that we raise ValueError if the inventory record contains a custom (non-standardized) resource class. """ values = { # NOTE(danms): We don't include an actual resource provider # here because chained backporting of that is handled by # the infrastructure and requires us to have a manifest 'resource_class': 'custom_resource', 'consumer_id': uuids.consumer_id, 'used': 1, } bdm = objects.Allocation(context=self.context, **values) self.assertRaises(ValueError, bdm.obj_to_primitive, target_version='1.0')
def test_set_inventory_over_capacity(self, mock_log): rp = objects.ResourceProvider(context=self.context, uuid=uuidsentinel.rp_uuid, name=uuidsentinel.rp_name) rp.create() disk_inv = objects.Inventory( resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=1024, reserved=15, min_unit=10, max_unit=100, step_size=10, allocation_ratio=1.0) vcpu_inv = objects.Inventory(resource_provider=rp, resource_class=fields.ResourceClass.VCPU, total=12, reserved=0, min_unit=1, max_unit=12, step_size=1, allocation_ratio=16.0) inv_list = objects.InventoryList(objects=[disk_inv, vcpu_inv]) rp.set_inventory(inv_list) self.assertFalse(mock_log.warning.called) # Allocate something reasonable for the above inventory alloc = objects.Allocation(context=self.context, resource_provider=rp, consumer_id=uuidsentinel.consumer, resource_class='DISK_GB', used=512) alloc.create() # Update our inventory to over-subscribe us after the above allocation disk_inv.total = 400 rp.set_inventory(inv_list) # We should succeed, but have logged a warning for going over on disk mock_log.warning.assert_called_once_with(mock.ANY, { 'uuid': rp.uuid, 'resource': 'DISK_GB' })
def test_create_with_id_fails(self): rp = objects.ResourceProvider(context=self.context, uuid=_RESOURCE_PROVIDER_UUID, name=_RESOURCE_PROVIDER_NAME) rp.create() inv = objects.Inventory(context=self.context, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, total=16, reserved=2, min_unit=1, max_unit=8, step_size=1, allocation_ratio=1.0) inv.create() obj = objects.Allocation(context=self.context, id=99, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, consumer_id=uuids.fake_instance, used=8) alloc_list = objects.AllocationList(self.context, objects=[obj]) self.assertRaises(exception.ObjectActionError, alloc_list.create_all)
def test_create(self, mock_ensure_cache): rp = objects.ResourceProvider(context=self.context, uuid=_RESOURCE_PROVIDER_UUID, name=_RESOURCE_PROVIDER_NAME) rp.create() inv = objects.Inventory(context=self.context, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, total=16, reserved=2, min_unit=1, max_unit=8, step_size=1, allocation_ratio=1.0) inv.create() obj = objects.Allocation(context=self.context, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, consumer_id=uuids.fake_instance, used=8) alloc_list = objects.AllocationList(self.context, objects=[obj]) self.assertNotIn("id", obj) alloc_list.create_all() self.assertIn("id", obj)
def test_allocation_checking(self): """Test that allocation check logic works with 2 resource classes on one provider. If this fails, we get a KeyError at create_all() """ consumer_uuid = uuidsentinel.consumer consumer_uuid2 = uuidsentinel.consumer2 # Create one resource provider with 2 classes rp1_name = uuidsentinel.rp1_name rp1_uuid = uuidsentinel.rp1_uuid rp1_class = fields.ResourceClass.DISK_GB rp1_used = 6 rp2_class = fields.ResourceClass.IPV4_ADDRESS rp2_used = 2 rp1 = objects.ResourceProvider(self.context, name=rp1_name, uuid=rp1_uuid) rp1.create() inv = objects.Inventory(resource_provider=rp1, resource_class=rp1_class, total=1024) inv.obj_set_defaults() inv2 = objects.Inventory(resource_provider=rp1, resource_class=rp2_class, total=255, reserved=2) inv2.obj_set_defaults() inv_list = objects.InventoryList(objects=[inv, inv2]) rp1.set_inventory(inv_list) # create the allocations for a first consumer allocation_1 = objects.Allocation(resource_provider=rp1, consumer_id=consumer_uuid, resource_class=rp1_class, used=rp1_used) allocation_2 = objects.Allocation(resource_provider=rp1, consumer_id=consumer_uuid, resource_class=rp2_class, used=rp2_used) allocation_list = objects.AllocationList( self.context, objects=[allocation_1, allocation_2]) allocation_list.create_all() # create the allocations for a second consumer, until we have # allocations for more than one consumer in the db, then we # won't actually be doing real allocation math, which triggers # the sql monster. allocation_1 = objects.Allocation(resource_provider=rp1, consumer_id=consumer_uuid2, resource_class=rp1_class, used=rp1_used) allocation_2 = objects.Allocation(resource_provider=rp1, consumer_id=consumer_uuid2, resource_class=rp2_class, used=rp2_used) allocation_list = objects.AllocationList( self.context, objects=[allocation_1, allocation_2]) # If we are joining wrong, this will be a KeyError allocation_list.create_all()
def test_allocation_list_create(self): consumer_uuid = uuidsentinel.consumer # Create two resource providers rp1_name = uuidsentinel.rp1_name rp1_uuid = uuidsentinel.rp1_uuid rp1_class = fields.ResourceClass.DISK_GB rp1_used = 6 rp2_name = uuidsentinel.rp2_name rp2_uuid = uuidsentinel.rp2_uuid rp2_class = fields.ResourceClass.IPV4_ADDRESS rp2_used = 2 rp1 = objects.ResourceProvider( self.context, name=rp1_name, uuid=rp1_uuid) rp1.create() rp2 = objects.ResourceProvider( self.context, name=rp2_name, uuid=rp2_uuid) rp2.create() # Two allocations, one for each resource provider. allocation_1 = objects.Allocation(resource_provider=rp1, consumer_id=consumer_uuid, resource_class=rp1_class, used=rp1_used) allocation_2 = objects.Allocation(resource_provider=rp2, consumer_id=consumer_uuid, resource_class=rp2_class, used=rp2_used) allocation_list = objects.AllocationList( self.context, objects=[allocation_1, allocation_2]) # There's no inventory, we have a failure. self.assertRaises(exception.InvalidInventory, allocation_list.create_all) # Add inventory for one of the two resource providers. This should also # fail, since rp2 has no inventory. inv = objects.Inventory(resource_provider=rp1, resource_class=rp1_class, total=1024) inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[inv]) rp1.set_inventory(inv_list) self.assertRaises(exception.InvalidInventory, allocation_list.create_all) # Add inventory for the second resource provider inv = objects.Inventory(resource_provider=rp2, resource_class=rp2_class, total=255, reserved=2) inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[inv]) rp2.set_inventory(inv_list) # Now the allocations will work. allocation_list.create_all() # Check that those allocations changed usage on each # resource provider. rp1_usage = objects.UsageList.get_all_by_resource_provider_uuid( self.context, rp1_uuid) rp2_usage = objects.UsageList.get_all_by_resource_provider_uuid( self.context, rp2_uuid) self.assertEqual(rp1_used, rp1_usage[0].usage) self.assertEqual(rp2_used, rp2_usage[0].usage) # redo one allocation # TODO(cdent): This does not currently behave as expected # because a new allocataion is created, adding to the total # used, not replacing. rp1_used += 1 allocation_1 = objects.Allocation(resource_provider=rp1, consumer_id=consumer_uuid, resource_class=rp1_class, used=rp1_used) allocation_list = objects.AllocationList( self.context, objects=[allocation_1]) allocation_list.create_all() rp1_usage = objects.UsageList.get_all_by_resource_provider_uuid( self.context, rp1_uuid) self.assertEqual(rp1_used, rp1_usage[0].usage) # delete the allocations for the consumer # NOTE(cdent): The database uses 'consumer_id' for the # column, presumably because some ids might not be uuids, at # some point in the future. consumer_allocations = objects.AllocationList.get_all_by_consumer_id( self.context, consumer_uuid) consumer_allocations.delete_all() rp1_usage = objects.UsageList.get_all_by_resource_provider_uuid( self.context, rp1_uuid) rp2_usage = objects.UsageList.get_all_by_resource_provider_uuid( self.context, rp2_uuid) self.assertEqual(0, rp1_usage[0].usage) self.assertEqual(0, rp2_usage[0].usage)
def start_fixture(self): super(AllocationFixture, self).start_fixture() self.context = context.get_admin_context() # For use creating and querying allocations/usages os.environ['ALT_USER_ID'] = uuidutils.generate_uuid() project_id = os.environ['PROJECT_ID'] user_id = os.environ['USER_ID'] alt_user_id = os.environ['ALT_USER_ID'] # Stealing from the super rp_name = os.environ['RP_NAME'] rp_uuid = os.environ['RP_UUID'] rp = objects.ResourceProvider( self.context, name=rp_name, uuid=rp_uuid) rp.create() # Create some DISK_GB inventory and allocations. # Each set of allocations must have the same consumer_id because only # the first allocation is used for the project/user association. consumer_id = uuidutils.generate_uuid() inventory = objects.Inventory( self.context, resource_provider=rp, resource_class='DISK_GB', total=2048, step_size=10, min_unit=10, max_unit=600) inventory.obj_set_defaults() rp.add_inventory(inventory) alloc1 = objects.Allocation( self.context, resource_provider=rp, resource_class='DISK_GB', consumer_id=consumer_id, used=500) alloc2 = objects.Allocation( self.context, resource_provider=rp, resource_class='DISK_GB', consumer_id=consumer_id, used=500) alloc_list = objects.AllocationList( self.context, objects=[alloc1, alloc2], project_id=project_id, user_id=user_id, ) alloc_list.create_all() # Create some VCPU inventory and allocations. # Each set of allocations must have the same consumer_id because only # the first allocation is used for the project/user association. consumer_id = uuidutils.generate_uuid() inventory = objects.Inventory( self.context, resource_provider=rp, resource_class='VCPU', total=10, max_unit=4) inventory.obj_set_defaults() rp.add_inventory(inventory) alloc1 = objects.Allocation( self.context, resource_provider=rp, resource_class='VCPU', consumer_id=consumer_id, used=2) alloc2 = objects.Allocation( self.context, resource_provider=rp, resource_class='VCPU', consumer_id=consumer_id, used=4) alloc_list = objects.AllocationList( self.context, objects=[alloc1, alloc2], project_id=project_id, user_id=user_id) alloc_list.create_all() # Create a couple of allocations for a different user. # Each set of allocations must have the same consumer_id because only # the first allocation is used for the project/user association. consumer_id = uuidutils.generate_uuid() alloc1 = objects.Allocation( self.context, resource_provider=rp, resource_class='DISK_GB', consumer_id=consumer_id, used=20) alloc2 = objects.Allocation( self.context, resource_provider=rp, resource_class='VCPU', consumer_id=consumer_id, used=1) alloc_list = objects.AllocationList( self.context, objects=[alloc1, alloc2], project_id=project_id, user_id=alt_user_id) alloc_list.create_all() # The ALT_RP_XXX variables are for a resource provider that has # not been created in the Allocation fixture os.environ['ALT_RP_UUID'] = uuidutils.generate_uuid() os.environ['ALT_RP_NAME'] = uuidutils.generate_uuid()