def test_destroy_fail_with_inventory(self): """Test that we raise an exception when attempting to delete a resource class that is referenced in an inventory record. """ rc = objects.ResourceClass( self.context, name='CUSTOM_IRON_NFV', ) rc.create() rp = objects.ResourceProvider( self.context, name='my rp', uuid=uuidsentinel.rp, ) rp.create() inv = objects.Inventory( resource_provider=rp, resource_class='CUSTOM_IRON_NFV', total=1, ) inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[inv]) rp.set_inventory(inv_list) self.assertRaises(exception.ResourceClassInUse, rc.destroy) rp.set_inventory(objects.InventoryList(objects=[])) rc.destroy() rc_list = objects.ResourceClassList.get_all(self.context) rc_ids = (r.id for r in rc_list) self.assertNotIn(rc.id, rc_ids)
def test_find(self): rp = objects.ResourceProvider(uuid=uuids.rp_uuid) inv_list = objects.InventoryList(objects=[ objects.Inventory(resource_provider=rp, resource_class=fields.ResourceClass.VCPU, total=24), objects.Inventory(resource_provider=rp, resource_class=fields.ResourceClass.MEMORY_MB, total=10240), ]) found = inv_list.find(fields.ResourceClass.MEMORY_MB) self.assertIsNotNone(found) self.assertEqual(10240, found.total) found = inv_list.find(fields.ResourceClass.VCPU) self.assertIsNotNone(found) self.assertEqual(24, found.total) found = inv_list.find(fields.ResourceClass.DISK_GB) self.assertIsNone(found) # Try an integer resource class identifier... found = inv_list.find( fields.ResourceClass.index(fields.ResourceClass.VCPU)) self.assertIsNotNone(found) self.assertEqual(24, found.total)
def set_inventories(req): """PUT to set all inventory for a resource provider. Create, update and delete inventory as required to reset all the inventory. If the resource generation is out of sync, return a 409. If an inventory to be deleted is in use, return a 409. If any inventory to be created or updated has settings which are invalid (for example reserved exceeds capacity), return a 400. On success return a 200 with an application/json body representing the inventories. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = objects.ResourceProvider.get_by_uuid(context, uuid) data = _extract_inventories(req.body, PUT_INVENTORY_SCHEMA) if data['resource_provider_generation'] != resource_provider.generation: raise webob.exc.HTTPConflict( _('resource provider generation conflict')) inv_list = [] for res_class, inventory_data in data['inventories'].items(): inventory = _make_inventory_object(resource_provider, res_class, **inventory_data) inv_list.append(inventory) inventories = objects.InventoryList(objects=inv_list) try: resource_provider.set_inventory(inventories) except exception.ResourceClassNotFound as exc: raise webob.exc.HTTPBadRequest( _('Unknown resource class in inventory for resource provider ' '%(rp_uuid)s: %(error)s') % { 'rp_uuid': resource_provider.uuid, 'error': exc }) 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.response, resource_provider, inventories)
def test_update_inventory_violates_allocation(self, mock_log): # Compute nodes that are reconfigured have to be able to set # their inventory to something that violates allocations so # we need to make that possible. rp, allocation = self._make_allocation() disk_inv = objects.Inventory(resource_provider=rp, resource_class='DISK_GB', total=2048) disk_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) # attempt to set inventory to less than currently allocated # amounts new_total = 1 disk_inv = objects.Inventory( resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=new_total) disk_inv.obj_set_defaults() rp.update_inventory(disk_inv) usages = objects.UsageList.get_all_by_resource_provider_uuid( self.context, rp.uuid) self.assertEqual(allocation.used, usages[0].usage) inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, rp.uuid) self.assertEqual(new_total, inv_list[0].total) mock_log.warning.assert_called_once_with(mock.ANY, { 'uuid': rp.uuid, 'resource': 'DISK_GB' })
def test_find(self): rp = objects.ResourceProvider(uuid=uuids.rp_uuid) inv_list = objects.InventoryList(objects=[ objects.Inventory(resource_provider=rp, resource_class=fields.ResourceClass.VCPU, total=24), objects.Inventory(resource_provider=rp, resource_class=fields.ResourceClass.MEMORY_MB, total=10240), ]) found = inv_list.find(fields.ResourceClass.MEMORY_MB) self.assertIsNotNone(found) self.assertEqual(10240, found.total) found = inv_list.find(fields.ResourceClass.VCPU) self.assertIsNotNone(found) self.assertEqual(24, found.total) found = inv_list.find(fields.ResourceClass.DISK_GB) self.assertIsNone(found) # Try an integer resource class identifier... self.assertRaises(ValueError, inv_list.find, VCPU_ID) # Use an invalid string... self.assertIsNone(inv_list.find('HOUSE'))
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. """ microversion.raise_http_status_code_if_not_version(req, 405, (1, 5)) context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = objects.ResourceProvider.get_by_uuid(context, uuid) inventories = objects.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: raise webob.exc.HTTPConflict( _('Unable to delete inventory for resource provider ' '%(rp_uuid)s because the inventory is in use.') % {'rp_uuid': resource_provider.uuid}) response = req.response response.status = 204 response.content_type = None return response
def test_find(self): rp = objects.ResourceProvider(uuid=uuids.rp_uuid) inv_list = objects.InventoryList(objects=[ objects.Inventory(resource_provider=rp, resource_class=fields.ResourceClass.VCPU, total=24), objects.Inventory(resource_provider=rp, resource_class=fields.ResourceClass.MEMORY_MB, total=10240), ]) found = inv_list.find(fields.ResourceClass.MEMORY_MB) self.assertIsNotNone(found) self.assertEqual(10240, found.total) found = inv_list.find(fields.ResourceClass.VCPU) self.assertIsNotNone(found) self.assertEqual(24, found.total) found = inv_list.find(fields.ResourceClass.DISK_GB) self.assertIsNone(found) # Try an integer resource class identifier... found = inv_list.find( fields.ResourceClass.index(fields.ResourceClass.VCPU)) self.assertIsNotNone(found) self.assertEqual(24, found.total) # Use an invalid string... error = self.assertRaises(exception.NotFound, inv_list.find, 'HOUSE') self.assertIn('No such resource class', str(error))
def test_set_inventory_unknown_resource_class(self): """Test attempting to set inventory to an unknown resource class raises an exception. """ rp = objects.ResourceProvider( context=self.context, uuid=uuidsentinel.rp_uuid, name='compute-host', ) rp.create() inv = objects.Inventory( resource_provider=rp, resource_class='UNKNOWN', total=1024, reserved=15, min_unit=10, max_unit=100, step_size=10, allocation_ratio=1.0, ) inv_list = objects.InventoryList(objects=[inv]) self.assertRaises(exception.ResourceClassNotFound, rp.set_inventory, inv_list)
def test_delete_inventory_with_allocation(self): rp, allocation = self._make_allocation() disk_inv = objects.Inventory(resource_provider=rp, resource_class='DISK_GB', total=2048) disk_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) error = self.assertRaises(exception.InventoryInUse, rp.delete_inventory, 'DISK_GB') self.assertIn( "Inventory for 'DISK_GB' on resource provider '%s' in use" % rp.uuid, str(error))
def _make_rp_and_inventory(self, **kwargs): # Create one resource provider and set some inventory rp_name = uuidsentinel.rp_name rp_uuid = uuidsentinel.rp_uuid rp = objects.ResourceProvider( self.context, name=rp_name, uuid=rp_uuid) rp.create() inv = objects.Inventory(resource_provider=rp, total=1024, allocation_ratio=1, reserved=0, **kwargs) inv.obj_set_defaults() rp.set_inventory(objects.InventoryList(objects=[inv])) return rp
def test_get_all_one_allocation(self): db_rp, _ = self._make_allocation(rp_uuid=uuidsentinel.rp_uuid) inv = objects.Inventory(resource_provider=db_rp, resource_class=fields.ResourceClass.DISK_GB, total=1024) inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[inv]) db_rp.set_inventory(inv_list) usage_list = objects.UsageList.get_all_by_resource_provider_uuid( self.context, db_rp.uuid) self.assertEqual(1, len(usage_list)) self.assertEqual(2, usage_list[0].usage) self.assertEqual(fields.ResourceClass.DISK_GB, usage_list[0].resource_class)
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 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 an inventory to be updated would set capacity to exceed existing use, return a 409. If any inventory to be created or updated has settings which are invalid (for example reserved exceeds capacity), return a 400. On success return a 200 with an application/json body representing the inventories. """ context = req.environ['placement.context'] uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = objects.ResourceProvider.get_by_uuid(context, uuid) data = _extract_inventories(req.body, PUT_INVENTORY_SCHEMA) if data['resource_provider_generation'] != resource_provider.generation: raise webob.exc.HTTPConflict('resource provider generation conflict', json_formatter=util.json_error_formatter) inv_list = [] for inventory_data in data['inventories']: inventory = _make_inventory_object(resource_provider, **inventory_data) inv_list.append(inventory) inventories = objects.InventoryList(objects=inv_list) try: resource_provider.set_inventory(inventories) except (exception.ConcurrentUpdateDetected, exception.InventoryInUse, exception.InvalidInventoryNewCapacityExceeded, db_exc.DBDuplicateEntry) as exc: raise webob.exc.HTTPConflict('update conflict: %s' % exc, json_formatter=util.json_error_formatter) except exception.InvalidInventoryCapacity as exc: raise webob.exc.HTTPBadRequest( 'Unable to update inventory for resource provider %s: %s' % (resource_provider.uuid, exc), json_formatter=util.json_error_formatter) return _send_inventories(req.response, resource_provider, inventories)
def test_get_all_multiple_inv(self): db_rp = objects.ResourceProvider(self.context, name=uuidsentinel.rp_no_inv, uuid=uuidsentinel.rp_no_inv) db_rp.create() disk_inv = objects.Inventory( resource_provider=db_rp, resource_class=fields.ResourceClass.DISK_GB, total=1024) disk_inv.obj_set_defaults() vcpu_inv = objects.Inventory( resource_provider=db_rp, resource_class=fields.ResourceClass.VCPU, total=24) vcpu_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[disk_inv, vcpu_inv]) db_rp.set_inventory(inv_list) usage_list = objects.UsageList.get_all_by_resource_provider_uuid( self.context, db_rp.uuid) self.assertEqual(2, len(usage_list))
def test_update_inventory_violates_allocation(self): rp, allocation = self._make_allocation() disk_inv = objects.Inventory(resource_provider=rp, resource_class='DISK_GB', total=2048) disk_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) # attempt to set inventory to less than currently allocated # amounts disk_inv = objects.Inventory( resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=1) disk_inv.obj_set_defaults() error = self.assertRaises( exception.InvalidInventoryNewCapacityExceeded, rp.update_inventory, disk_inv) self.assertIn("Invalid inventory for '%s'" % fields.ResourceClass.DISK_GB, str(error)) self.assertIn("on resource provider '%s'." % rp.uuid, str(error))
def start_fixture(self): super(SharedStorageFixture, self).start_fixture() self.context = context.get_admin_context() # These UUIDs are staticly defined here because the JSONPath querying # needed in the allocation-candidates.yaml gabbits cannot refer to an # ENVIRON variable because the $ sign is a token in the JSONPath # parser. os.environ['CN1_UUID'] = 'c1c1c1c1-2894-4df1-aa6b-c61fa72ed22d' os.environ['CN2_UUID'] = 'c2c2c2c2-beef-49a0-98a0-b998b88debfd' os.environ['SS_UUID'] = 'dddddddd-61a6-472e-b8c1-74796e803066' os.environ['AGG_UUID'] = 'aaaaaaaa-04b3-458c-9a9f-361aad56f41c' cn1_uuid = os.environ['CN1_UUID'] cn2_uuid = os.environ['CN2_UUID'] ss_uuid = os.environ['SS_UUID'] agg_uuid = os.environ['AGG_UUID'] cn1 = objects.ResourceProvider(self.context, name='cn1', uuid=cn1_uuid) cn1.create() cn2 = objects.ResourceProvider(self.context, name='cn2', uuid=cn2_uuid) cn2.create() ss = objects.ResourceProvider(self.context, name='ss', uuid=ss_uuid) ss.create() # Populate compute node inventory for VCPU and RAM for cn in (cn1, cn2): vcpu_inv = objects.Inventory(self.context, resource_provider=cn, resource_class='VCPU', total=24, reserved=0, max_unit=24, min_unit=1, step_size=1, allocation_ratio=16.0) vcpu_inv.obj_set_defaults() ram_inv = objects.Inventory(self.context, resource_provider=cn, resource_class='MEMORY_MB', total=128 * 1024, reserved=0, max_unit=128 * 1024, min_unit=256, step_size=256, allocation_ratio=1.5) ram_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[vcpu_inv, ram_inv]) cn.set_inventory(inv_list) # Populate shared storage provider with DISK_GB inventory disk_inv = objects.Inventory(self.context, resource_provider=ss, resource_class='DISK_GB', total=2000, reserved=100, max_unit=2000, min_unit=10, step_size=10, allocation_ratio=1.0) disk_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[disk_inv]) ss.set_inventory(inv_list) # Mark the shared storage pool as having inventory shared among any # provider associated via aggregate t = objects.Trait.get_by_name( self.context, "MISC_SHARES_VIA_AGGREGATE", ) ss.set_traits(objects.TraitList(objects=[t])) # Now associate the shared storage pool and both compute nodes with the # same aggregate cn1.set_aggregates([agg_uuid]) cn2.set_aggregates([agg_uuid]) ss.set_aggregates([agg_uuid])
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 test_provider_modify_inventory(self): rp = objects.ResourceProvider(context=self.context, uuid=uuidsentinel.rp_uuid, name=uuidsentinel.rp_name) rp.create() saved_generation = rp.generation 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) # set to new list inv_list = objects.InventoryList(objects=[disk_inv, vcpu_inv]) rp.set_inventory(inv_list) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation new_inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(2, len(new_inv_list)) resource_classes = [inv.resource_class for inv in new_inv_list] self.assertIn(fields.ResourceClass.VCPU, resource_classes) self.assertIn(fields.ResourceClass.DISK_GB, resource_classes) # reset list to just disk_inv inv_list = objects.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation new_inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(1, len(new_inv_list)) resource_classes = [inv.resource_class for inv in new_inv_list] self.assertNotIn(fields.ResourceClass.VCPU, resource_classes) self.assertIn(fields.ResourceClass.DISK_GB, resource_classes) self.assertEqual(1024, new_inv_list[0].total) # update existing disk inv to new settings disk_inv = objects.Inventory( resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=2048, reserved=15, min_unit=10, max_unit=100, step_size=10, allocation_ratio=1.0) rp.update_inventory(disk_inv) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation new_inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(1, len(new_inv_list)) self.assertEqual(2048, new_inv_list[0].total) # fail when inventory bad disk_inv = objects.Inventory( resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=2048, reserved=2048) disk_inv.obj_set_defaults() error = self.assertRaises(exception.InvalidInventoryCapacity, rp.update_inventory, disk_inv) self.assertIn("Invalid inventory for '%s'" % fields.ResourceClass.DISK_GB, str(error)) self.assertIn("on resource provider '%s'." % rp.uuid, str(error)) # generation has not bumped self.assertEqual(saved_generation, rp.generation) # delete inventory rp.delete_inventory(fields.ResourceClass.DISK_GB) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation new_inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) result = new_inv_list.find(fields.ResourceClass.DISK_GB) self.assertIsNone(result) self.assertRaises(exception.NotFound, rp.delete_inventory, fields.ResourceClass.DISK_GB) # check inventory list is empty inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(0, len(inv_list)) # add some inventory rp.add_inventory(vcpu_inv) inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(1, len(inv_list)) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation # add same inventory again self.assertRaises(db_exc.DBDuplicateEntry, rp.add_inventory, vcpu_inv) # generation has not bumped self.assertEqual(saved_generation, rp.generation) # fail when generation wrong rp.generation = rp.generation - 1 self.assertRaises(exception.ConcurrentUpdateDetected, rp.set_inventory, inv_list)
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_provider_set_inventory(self): rp = objects.ResourceProvider(context=self.context, uuid=uuidsentinel.rp_uuid, name=uuidsentinel.rp_name) rp.create() saved_generation = rp.generation 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) # set to new list inv_list = objects.InventoryList(objects=[disk_inv, vcpu_inv]) rp.set_inventory(inv_list) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation new_inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(2, len(new_inv_list)) resource_classes = [inv.resource_class for inv in new_inv_list] self.assertIn(fields.ResourceClass.VCPU, resource_classes) self.assertIn(fields.ResourceClass.DISK_GB, resource_classes) # reset list to just disk_inv inv_list = objects.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation new_inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(1, len(new_inv_list)) resource_classes = [inv.resource_class for inv in new_inv_list] self.assertNotIn(fields.ResourceClass.VCPU, resource_classes) self.assertIn(fields.ResourceClass.DISK_GB, resource_classes) self.assertEqual(1024, new_inv_list[0].total) # update existing disk inv to new settings disk_inv = objects.Inventory( resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=2048, reserved=15, min_unit=10, max_unit=100, step_size=10, allocation_ratio=1.0) inv_list = objects.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation new_inv_list = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, uuidsentinel.rp_uuid) self.assertEqual(1, len(new_inv_list)) self.assertEqual(2048, new_inv_list[0].total) # fail when generation wrong rp.generation = rp.generation - 1 self.assertRaises(exception.ConcurrentUpdateDetected, rp.set_inventory, inv_list)
def start_fixture(self): super(SharedStorageFixture, self).start_fixture() self.context = context.get_admin_context() cn1_uuid = uuidutils.generate_uuid() cn2_uuid = uuidutils.generate_uuid() ss_uuid = uuidutils.generate_uuid() agg_uuid = uuidutils.generate_uuid() os.environ['CN1_UUID'] = cn1_uuid os.environ['CN2_UUID'] = cn2_uuid os.environ['SS_UUID'] = ss_uuid os.environ['AGG_UUID'] = agg_uuid cn1 = objects.ResourceProvider(self.context, name='cn1', uuid=cn1_uuid) cn1.create() cn2 = objects.ResourceProvider(self.context, name='cn2', uuid=cn2_uuid) cn2.create() ss = objects.ResourceProvider(self.context, name='ss', uuid=ss_uuid) ss.create() # Populate compute node inventory for VCPU and RAM for cn in (cn1, cn2): vcpu_inv = objects.Inventory(self.context, resource_provider=cn, resource_class='VCPU', total=24, reserved=0, max_unit=24, min_unit=1, step_size=1, allocation_ratio=16.0) vcpu_inv.obj_set_defaults() ram_inv = objects.Inventory(self.context, resource_provider=cn, resource_class='MEMORY_MB', total=128 * 1024, reserved=0, max_unit=128 * 1024, min_unit=256, step_size=256, allocation_ratio=1.5) ram_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[vcpu_inv, ram_inv]) cn.set_inventory(inv_list) # Populate shared storage provider with DISK_GB inventory disk_inv = objects.Inventory(self.context, resource_provider=ss, resource_class='DISK_GB', total=2000, reserved=100, max_unit=2000, min_unit=10, step_size=10, allocation_ratio=1.0) disk_inv.obj_set_defaults() inv_list = objects.InventoryList(objects=[disk_inv]) ss.set_inventory(inv_list) # Mark the shared storage pool as having inventory shared among any # provider associated via aggregate t = objects.Trait.get_by_name( self.context, "MISC_SHARES_VIA_AGGREGATE", ) ss.set_traits(objects.TraitList(objects=[t])) # Now associate the shared storage pool and both compute nodes with the # same aggregate cn1.set_aggregates([agg_uuid]) cn2.set_aggregates([agg_uuid]) ss.set_aggregates([agg_uuid])