Merge "placement: add ResourceClass and ResourceClassList"

This commit is contained in:
Jenkins 2016-11-07 18:30:46 +00:00 committed by Gerrit Code Review
commit 4986c2a65a
5 changed files with 103 additions and 1 deletions

View File

@ -65,6 +65,13 @@ class ResourceClassCache(object):
self.id_cache = {}
self.str_cache = {}
def get_standards(self):
"""Return a list of {'id': <ID>, 'name': <NAME> for all standard
resource classes.
"""
return [{'id': fields.ResourceClass.STANDARD.index(s), 'name': s}
for s in fields.ResourceClass.STANDARD]
def id_from_string(self, rc_str):
"""Given a string representation of a resource class -- e.g. "DISK_GB"
or "IRON_SILVER" -- return the integer code for the resource class. For

View File

@ -30,6 +30,7 @@ from nova.objects import fields
_ALLOC_TBL = models.Allocation.__table__
_INV_TBL = models.Inventory.__table__
_RP_TBL = models.ResourceProvider.__table__
_RC_TBL = models.ResourceClass.__table__
_RC_CACHE = None
LOG = logging.getLogger(__name__)
@ -1007,3 +1008,51 @@ class UsageList(base.ObjectListBase, base.NovaObject):
def __repr__(self):
strings = [repr(x) for x in self.objects]
return "UsageList[" + ", ".join(strings) + "]"
@base.NovaObjectRegistry.register
class ResourceClass(base.NovaObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.IntegerField(read_only=True),
'name': fields.ResourceClassField(read_only=True),
}
@staticmethod
def _from_db_object(context, target, source):
for field in target.fields:
setattr(target, field, source[field])
target._context = context
target.obj_reset_changes()
return target
@base.NovaObjectRegistry.register
class ResourceClassList(base.ObjectListBase, base.NovaObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'objects': fields.ListOfObjectsField('ResourceClass'),
}
@staticmethod
@db_api.api_context_manager.reader
def _get_all(context):
_ensure_rc_cache(context)
standards = _RC_CACHE.get_standards()
customs = list(context.session.query(models.ResourceClass).all())
return standards + customs
@base.remotable_classmethod
def get_all(cls, context):
resource_classes = cls._get_all(context)
return base.obj_make_list(context, cls(context),
objects.ResourceClass, resource_classes)
def __repr__(self):
strings = [repr(x) for x in self.objects]
return "ResourceClassList[" + ", ".join(strings) + "]"

View File

@ -14,6 +14,7 @@ import mock
from nova.db.sqlalchemy import resource_class_cache as rc_cache
from nova import exception
from nova.objects import fields
from nova import test
from nova.tests import fixtures
@ -43,6 +44,14 @@ class TestResourceClassCache(test.TestCase):
self.assertFalse(sel_mock.called)
def test_get_standards(self):
cache = rc_cache.ResourceClassCache(self.context)
standards = cache.get_standards()
self.assertEqual(len(standards), len(fields.ResourceClass.STANDARD))
names = (rc['name'] for rc in standards)
for name in fields.ResourceClass.STANDARD:
self.assertIn(name, names)
def test_rc_cache_custom(self):
"""Test that non-standard, custom resource classes hit the database and
return appropriate results, caching the results after a single

View File

@ -18,6 +18,7 @@ from nova import context
from nova import exception
from nova import objects
from nova.objects import fields
from nova.objects import resource_provider as rp_obj
from nova import test
from nova.tests import fixtures
from nova.tests import uuidsentinel
@ -39,10 +40,14 @@ DISK_ALLOCATION = dict(
)
class ResourceProviderBaseCase(test.TestCase):
class ResourceProviderBaseCase(test.NoDBTestCase):
USES_DB_SELF = True
def setUp(self):
super(ResourceProviderBaseCase, self).setUp()
self.useFixture(fixtures.Database())
self.api_db = self.useFixture(fixtures.Database(database='api'))
self.context = context.RequestContext('fake-user', 'fake-project')
def _make_allocation(self, rp_uuid=None):
@ -894,3 +899,33 @@ class UsageListTestCase(ResourceProviderBaseCase):
usage_list = objects.UsageList.get_all_by_resource_provider_uuid(
self.context, db_rp.uuid)
self.assertEqual(2, len(usage_list))
class ResourceClassListTestCase(ResourceProviderBaseCase):
def test_get_all_no_custom(self):
"""Test that if we haven't yet added any custom resource classes, that
we only get a list of ResourceClass objects representing the standard
classes.
"""
rcs = objects.ResourceClassList.get_all(self.context)
self.assertEqual(len(fields.ResourceClass.STANDARD), len(rcs))
def test_get_all_with_custom(self):
"""Test that if we add some custom resource classes, that we get a list
of ResourceClass objects representing the standard classes as well as
the custom classes.
"""
customs = [
('IRON_NFV', 10001),
('IRON_ENTERPRISE', 10002),
]
with self.api_db.get_engine().connect() as conn:
for custom in customs:
c_name, c_id = custom
ins = rp_obj._RC_TBL.insert().values(id=c_id, name=c_name)
conn.execute(ins)
rcs = objects.ResourceClassList.get_all(self.context)
expected_count = len(fields.ResourceClass.STANDARD) + len(customs)
self.assertEqual(expected_count, len(rcs))

View File

@ -1180,6 +1180,8 @@ object_data = {
'Quotas': '1.2-1fe4cd50593aaf5d36a6dc5ab3f98fb3',
'QuotasNoOp': '1.2-e041ddeb7dc8188ca71706f78aad41c1',
'RequestSpec': '1.7-5ff3e9df208bf25f8215f1b87624970d',
'ResourceClass': '1.0-e6b367e2cf1733c5f3526f20a3286fe9',
'ResourceClassList': '1.0-4ee0d9efdfd681fed822da88376e04d2',
'ResourceProvider': '1.1-7bbcd5ea1c51782692f55489ab08dea6',
'ResourceProviderList': '1.0-82bd48d8d0f7913bbe7266f3835c81bf',
'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e',