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_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 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 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 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 _create_inventory(self, updates): """Create the initial inventory objects for this compute node. This is only ever called once, either for the first time when a compute is created, or after an upgrade where the required services have reached the required version. :param updates: Legacy model update dict which will be modified when we return """ rp = objects.ResourceProvider(context=self._context, uuid=self.uuid) rp.create() # NOTE(danms): Until we remove the columns from compute_nodes, # we need to constantly zero out each value in our updates to # signal that we wrote the value into inventory instead. cpu = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.VCPU, total=self.vcpus, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.cpu_allocation_ratio) cpu.create() updates['vcpus'] = 0 mem = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.MEMORY_MB, total=self.memory_mb, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.ram_allocation_ratio) mem.create() updates['memory_mb'] = 0 # FIXME(danms): Eventually we want to not write this record # if the compute host is on shared storage. We'll need some # indication from it to that effect, so for now we always # write it so that we can make all the usual machinery depend # on these records instead of the legacy columns. disk = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=self.local_gb, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.disk_allocation_ratio) disk.create() updates['local_gb'] = 0
def test_save(self, mock_db_save): obj = objects.Inventory(context=self.context, id=_INVENTORY_ID, reserved=4) obj.save() mock_db_save.assert_called_once_with(self.context, _INVENTORY_ID, {'reserved': 4})
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 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_create_and_update_inventory(self): resource_provider = objects.ResourceProvider( context=self.context, uuid=uuidsentinel.inventory_resource_provider, name='foo', ) resource_provider.create() resource_class = fields.ResourceClass.DISK_GB disk_inventory = objects.Inventory(context=self.context, resource_provider=resource_provider, **DISK_INVENTORY) disk_inventory.create() self.assertEqual(resource_class, disk_inventory.resource_class) self.assertEqual(resource_provider, disk_inventory.resource_provider) self.assertEqual(DISK_INVENTORY['allocation_ratio'], disk_inventory.allocation_ratio) self.assertEqual(DISK_INVENTORY['total'], disk_inventory.total) disk_inventory.total = 32 disk_inventory.save() inventories = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, resource_provider.uuid) self.assertEqual(1, len(inventories)) self.assertEqual(32, inventories[0].total) inventories[0].total = 33 inventories[0].save() reloaded_inventories = ( objects.InventoryList.get_all_by_resource_provider_uuid( self.context, resource_provider.uuid)) self.assertEqual(33, reloaded_inventories[0].total)
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_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 create_inventory(self): """Create the initial inventory objects for this compute node. This is only ever called once, either for the first time when a compute is created, or after an upgrade where the required services have reached the required version. """ rp = objects.ResourceProvider(context=self._context, uuid=self.uuid) rp.create() cpu = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.VCPU, total=self.vcpus, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.cpu_allocation_ratio) cpu.create() mem = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.MEMORY_MB, total=self.memory_mb, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.ram_allocation_ratio) mem.create() # FIXME(danms): Eventually we want to not write this record # if the compute host is on shared storage. We'll need some # indication from it to that effect, so for now we always # write it so that we can make all the usual machinery depend # on these records instead of the legacy columns. disk = objects.Inventory(context=self._context, resource_provider=rp, resource_class=fields.ResourceClass.DISK_GB, total=self.local_gb, reserved=0, min_unit=1, max_unit=1, step_size=1, allocation_ratio=self.disk_allocation_ratio) disk.create()
def test_create_inventory_with_uncreated_provider(self): resource_provider = objects.ResourceProvider( context=self.context, uuid=uuidsentinel.inventory_resource_provider) disk_inventory = objects.Inventory(context=self.context, resource_provider=resource_provider, **DISK_INVENTORY) self.assertRaises(exception.ObjectActionError, disk_inventory.create)
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_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_create_requires_resource_provider(self): inventory_dict = dict(_INVENTORY_DB) inventory_dict.pop('id') inventory_dict.pop('resource_provider_id') inventory_dict.pop('resource_class_id') inventory_dict['resource_class'] = _RESOURCE_CLASS_NAME inventory = objects.Inventory(context=self.context, **inventory_dict) error = self.assertRaises(exception.ObjectActionError, inventory.create) self.assertIn('resource_provider required', str(error))
def _create_resource_provider(self, inventory): """Helper method to create a resource provider with inventory""" ctxt = context.get_admin_context() rp_uuid = uuidutils.generate_uuid() rp = objects.ResourceProvider(context=ctxt, name=rp_uuid, uuid=rp_uuid) rp.create() inventory = objects.Inventory(context=ctxt, resource_provider=rp, **inventory) inventory.create() return rp
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 test_update_inventory_not_found(self): 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='DISK_GB', total=2048) disk_inv.obj_set_defaults() error = self.assertRaises(exception.NotFound, rp.update_inventory, disk_inv) self.assertIn('No inventory of class DISK_GB found', str(error))
def test_create_requires_created_resource_provider(self): rp = objects.ResourceProvider(context=self.context, uuid=uuids.inventory_resource_provider) inventory_dict = dict(_INVENTORY_DB) inventory_dict.pop('id') inventory_dict.pop('resource_provider_id') inventory_dict.pop('resource_class_id') inventory_dict['resource_provider'] = rp inventory = objects.Inventory(context=self.context, **inventory_dict) error = self.assertRaises(exception.ObjectActionError, inventory.create) self.assertIn('resource_provider required', str(error))
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_set_defaults(self): rp = objects.ResourceProvider(id=_RESOURCE_PROVIDER_ID, uuid=_RESOURCE_PROVIDER_UUID) kwargs = dict(resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, total=16) inv = objects.Inventory(self.context, **kwargs) inv.obj_set_defaults() self.assertEqual(0, inv.reserved) self.assertEqual(1, inv.min_unit) self.assertEqual(1, inv.max_unit) self.assertEqual(1, inv.step_size) self.assertEqual(1.0, inv.allocation_ratio)
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 _make_inventory_object(resource_provider, **data): """Single place to catch malformed Inventories.""" # TODO(cdent): Some of the validation checks that are done here # could be done via JSONschema (using, for example, "minimum": # 0) for non-negative integers. It's not clear if that is # duplication or decoupling so leaving it as this for now. try: inventory = objects.Inventory(resource_provider=resource_provider, **data) except (ValueError, TypeError) as exc: raise webob.exc.HTTPBadRequest( 'Bad inventory %s for resource provider %s: %s' % (data['resource_class'], resource_provider.uuid, exc), json_formatter=util.json_error_formatter) return inventory
def test_capacity(self): rp = objects.ResourceProvider(id=_RESOURCE_PROVIDER_ID, uuid=_RESOURCE_PROVIDER_UUID) kwargs = dict(resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, total=16, reserved=16) inv = objects.Inventory(self.context, **kwargs) inv.obj_set_defaults() self.assertEqual(0, inv.capacity) inv.reserved = 15 self.assertEqual(1, inv.capacity) inv.allocation_ratio = 2.0 self.assertEqual(2, inv.capacity)
def test_add_invalid_inventory(self): 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=2048) disk_inv.obj_set_defaults() error = self.assertRaises(exception.InvalidInventoryCapacity, rp.add_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 test_create(self, mock_db_create): rp = objects.ResourceProvider(id=_RESOURCE_PROVIDER_ID, uuid=_RESOURCE_PROVIDER_UUID) obj = 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) obj.create() self.assertEqual(_INVENTORY_ID, obj.id) expected = dict(_INVENTORY_DB) expected.pop('id') mock_db_create.assert_called_once_with(self.context, expected)
def test_destroy_resource_provider_destroy_inventory(self): resource_provider = objects.ResourceProvider( context=self.context, uuid=uuidsentinel.fake_resource_provider, name=uuidsentinel.fake_resource_name, ) resource_provider.create() disk_inventory = objects.Inventory(context=self.context, resource_provider=resource_provider, **DISK_INVENTORY) disk_inventory.create() inventories = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, resource_provider.uuid) self.assertEqual(1, len(inventories)) resource_provider.destroy() inventories = objects.InventoryList.get_all_by_resource_provider_uuid( self.context, resource_provider.uuid) self.assertEqual(0, len(inventories))