Add a Usage and UsageList object
These encapsulate the Usage information associated with the inventories of a resource provider. A single UsageList holds all the Usage for all resource classes provided by that provider. Just resource class and usage. Putting this in place to make further headway on the placement API, which needs to represent Usage info in this form. This change intentionally tries to keep the new objects relatively lightweight. Partially-Implements: blueprint generic-resource-pools Change-Id: Ie5eddae4d0cb51bccc815a932a3eefcd5d9ea687
This commit is contained in:
parent
83d2eeff77
commit
51afe54246
@ -14,6 +14,7 @@ import six
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import contains_eager
|
||||
from sqlalchemy import sql
|
||||
|
||||
from nova.db.sqlalchemy import api as db_api
|
||||
from nova.db.sqlalchemy import api_models as models
|
||||
@ -626,3 +627,63 @@ class AllocationList(base.ObjectListBase, base.NovaObject):
|
||||
context, rp_uuid)
|
||||
return base.obj_make_list(
|
||||
context, cls(context), objects.Allocation, db_allocation_list)
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class Usage(base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'resource_class': fields.ResourceClassField(read_only=True),
|
||||
'usage': fields.NonNegativeIntegerField(),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(context, target, source):
|
||||
for field in target.fields:
|
||||
if field not in ('resource_class'):
|
||||
setattr(target, field, source[field])
|
||||
|
||||
if 'resource_class' not in target:
|
||||
target.resource_class = (
|
||||
target.fields['resource_class'].from_index(
|
||||
source['resource_class_id']))
|
||||
|
||||
target._context = context
|
||||
target.obj_reset_changes()
|
||||
return target
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class UsageList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Usage'),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@db_api.placement_context_manager.reader
|
||||
def _get_all_by_resource_provider_uuid(context, rp_uuid):
|
||||
query = (context.session.query(models.Inventory.resource_class_id,
|
||||
func.coalesce(func.sum(models.Allocation.used), 0))
|
||||
.join(models.ResourceProvider,
|
||||
models.Inventory.resource_provider_id ==
|
||||
models.ResourceProvider.id)
|
||||
.outerjoin(models.Allocation,
|
||||
sql.and_(models.Inventory.resource_provider_id ==
|
||||
models.Allocation.resource_provider_id,
|
||||
models.Inventory.resource_class_id ==
|
||||
models.Allocation.resource_class_id))
|
||||
.filter(models.ResourceProvider.uuid == rp_uuid)
|
||||
.group_by(models.Inventory.resource_class_id))
|
||||
result = [dict(resource_class_id=item[0], usage=item[1])
|
||||
for item in query.all()]
|
||||
return result
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_all_by_resource_provider_uuid(cls, context, rp_uuid):
|
||||
usage_list = cls._get_all_by_resource_provider_uuid(context, rp_uuid)
|
||||
return base.obj_make_list(context, cls(context), Usage, usage_list)
|
||||
|
@ -419,14 +419,7 @@ class ResourceProviderTestCase(ResourceProviderBaseCase):
|
||||
% rp.uuid, str(error))
|
||||
|
||||
|
||||
class ResourceProviderListTestCase(test.NoDBTestCase):
|
||||
|
||||
USES_DB_SELF = True
|
||||
|
||||
def setUp(self):
|
||||
super(ResourceProviderListTestCase, self).setUp()
|
||||
self.useFixture(fixtures.Database(database='placement'))
|
||||
self.context = context.RequestContext('fake-user', 'fake-project')
|
||||
class ResourceProviderListTestCase(ResourceProviderBaseCase):
|
||||
|
||||
def test_get_all_by_filters(self):
|
||||
for rp_i in ['1', '2']:
|
||||
@ -567,3 +560,70 @@ class TestAllocation(ResourceProviderBaseCase):
|
||||
self.assertNotIn(fields.ResourceClass.IPV4_ADDRESS,
|
||||
[allocation.resource_class
|
||||
for allocation in allocations])
|
||||
|
||||
|
||||
class UsageListTestCase(ResourceProviderBaseCase):
|
||||
|
||||
def test_get_all_null(self):
|
||||
for uuid in [uuidsentinel.rp_uuid_1, uuidsentinel.rp_uuid_2]:
|
||||
rp = objects.ResourceProvider(self.context, name=uuid, uuid=uuid)
|
||||
rp.create()
|
||||
|
||||
usage_list = objects.UsageList.get_all_by_resource_provider_uuid(
|
||||
self.context, uuidsentinel.rp_uuid_1)
|
||||
self.assertEqual(0, len(usage_list))
|
||||
|
||||
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_get_inventory_no_allocation(self):
|
||||
db_rp = objects.ResourceProvider(self.context,
|
||||
name=uuidsentinel.rp_no_inv,
|
||||
uuid=uuidsentinel.rp_no_inv)
|
||||
db_rp.create()
|
||||
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(0, usage_list[0].usage)
|
||||
self.assertEqual(fields.ResourceClass.DISK_GB,
|
||||
usage_list[0].resource_class)
|
||||
|
||||
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))
|
||||
|
@ -1195,6 +1195,8 @@ object_data = {
|
||||
'TaskLogList': '1.0-cc8cce1af8a283b9d28b55fcd682e777',
|
||||
'Tag': '1.1-8b8d7d5b48887651a0e01241672e2963',
|
||||
'TagList': '1.1-55231bdb671ecf7641d6a2e9109b5d8e',
|
||||
'Usage': '1.0-b78f18c3577a38e7a033e46a9725b09b',
|
||||
'UsageList': '1.0-de53f0fd078c27cc1d43400f4e8bcef8',
|
||||
'USBDeviceBus': '1.0-e4c7dd6032e46cd74b027df5eb2d4750',
|
||||
'VirtCPUFeature': '1.0-3310718d8c72309259a6e39bdefe83ee',
|
||||
'VirtCPUModel': '1.0-6a5cc9f322729fc70ddc6733bacd57d3',
|
||||
|
Loading…
Reference in New Issue
Block a user