[placement] Unregister the ResourceClass object

Unregister the ResourceClass object because we do not need RPC
and versioning for the objects in nova.objects.resource_provider.

There are three primary changes here:

* unregistering the ResourceClass class
* where objects.ResourceClass is used, point directly to the
  nova.objects.resource_provider package instead
* move the CUSTOM_NAMESPACE for resource class names to the the fields
  object. This avoids an import loop and also makes more sense as
  the field represents the name.

Partially-Implements: bp placement-deregister-objects
Change-Id: If537c786ea1a61383d82bad9a1de86c25c6cdbd2
This commit is contained in:
Chris Dent 2017-09-08 17:09:48 +01:00
parent 5cabc535af
commit 99f0387b94
8 changed files with 55 additions and 56 deletions

View File

@ -22,7 +22,6 @@ from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
from nova.i18n import _
from nova import objects
from nova.objects import resource_provider as rp_obj
@ -78,7 +77,7 @@ def create_resource_class(req):
data = util.extract_json(req.body, POST_RC_SCHEMA_V1_2)
try:
rc = objects.ResourceClass(context, name=data['name'])
rc = rp_obj.ResourceClass(context, name=data['name'])
rc.create()
except exception.ResourceClassExists:
raise webob.exc.HTTPConflict(
@ -107,7 +106,7 @@ def delete_resource_class(req):
name = util.wsgi_path_item(req.environ, 'name')
context = req.environ['placement.context']
# The containing application will catch a not found here.
rc = objects.ResourceClass.get_by_name(context, name)
rc = rp_obj.ResourceClass.get_by_name(context, name)
try:
rc.destroy()
except exception.ResourceClassCannotDeleteStandard as exc:
@ -135,7 +134,7 @@ def get_resource_class(req):
name = util.wsgi_path_item(req.environ, 'name')
context = req.environ['placement.context']
# The containing application will catch a not found here.
rc = objects.ResourceClass.get_by_name(context, name)
rc = rp_obj.ResourceClass.get_by_name(context, name)
req.response.body = encodeutils.to_utf8(jsonutils.dumps(
_serialize_resource_class(req.environ, rc))
@ -179,7 +178,7 @@ def update_resource_class(req):
data = util.extract_json(req.body, PUT_RC_SCHEMA_V1_2)
# The containing application will catch a not found here.
rc = objects.ResourceClass.get_by_name(context, name)
rc = rp_obj.ResourceClass.get_by_name(context, name)
rc.name = data['name']
@ -219,10 +218,10 @@ def update_resource_class(req):
status = 204
try:
rc = objects.ResourceClass.get_by_name(context, name)
rc = rp_obj.ResourceClass.get_by_name(context, name)
except exception.NotFound:
try:
rc = objects.ResourceClass(context, name=name)
rc = rp_obj.ResourceClass(context, name=name)
rc.create()
status = 201
# We will not see ResourceClassCannotUpdateStandard because

View File

@ -22,7 +22,6 @@ import six
from nova import exception
from nova.i18n import _
from nova.network import model as network_model
from nova import objects
# Import field errors from oslo.versionedobjects
@ -463,6 +462,9 @@ class OSType(BaseNovaEnum):
class ResourceClass(StringField):
"""Classes of resources provided to consumers."""
CUSTOM_NAMESPACE = 'CUSTOM_'
"""All non-standard resource classes must begin with this string."""
VCPU = 'VCPU'
MEMORY_MB = 'MEMORY_MB'
DISK_GB = 'DISK_GB'
@ -486,12 +488,12 @@ class ResourceClass(StringField):
V1_0 = (VCPU, MEMORY_MB, DISK_GB, PCI_DEVICE, SRIOV_NET_VF, NUMA_SOCKET,
NUMA_CORE, NUMA_THREAD, NUMA_MEMORY_MB, IPV4_ADDRESS)
@staticmethod
def normalize_name(rc_name):
@classmethod
def normalize_name(cls, rc_name):
if rc_name is None:
return None
norm_name = rc_name.upper()
cust_prefix = objects.ResourceClass.CUSTOM_NAMESPACE
cust_prefix = cls.CUSTOM_NAMESPACE
norm_name = cust_prefix + norm_name
# Replace some punctuation characters with underscores
norm_name = re.sub('[^0-9A-Z]+', '_', norm_name)

View File

@ -1983,13 +1983,8 @@ class UsageList(base.ObjectListBase, base.NovaObject):
return "UsageList[" + ", ".join(strings) + "]"
@base.NovaObjectRegistry.register
@base.NovaObjectRegistry.register_if(False)
class ResourceClass(base.NovaObject):
# Version 1.0: Initial version
VERSION = '1.0'
CUSTOM_NAMESPACE = 'CUSTOM_'
"""All non-standard resource classes must begin with this string."""
MIN_CUSTOM_RESOURCE_CLASS_ID = 10000
"""Any user-defined resource classes must have an identifier greater than
@ -2053,10 +2048,11 @@ class ResourceClass(base.NovaObject):
if self.name in fields.ResourceClass.STANDARD:
raise exception.ResourceClassExists(resource_class=self.name)
if not self.name.startswith(self.CUSTOM_NAMESPACE):
if not self.name.startswith(fields.ResourceClass.CUSTOM_NAMESPACE):
raise exception.ObjectActionError(
action='create',
reason='name must start with ' + self.CUSTOM_NAMESPACE)
reason='name must start with ' +
fields.ResourceClass.CUSTOM_NAMESPACE)
updates = self.obj_get_changes()
# There is the possibility of a race when adding resource classes, as
@ -2171,7 +2167,7 @@ class ResourceClassList(base.ObjectListBase, base.NovaObject):
def get_all(cls, context):
resource_classes = cls._get_all(context)
return base.obj_make_list(context, cls(context),
objects.ResourceClass, resource_classes)
ResourceClass, resource_classes)
def __repr__(self):
strings = [repr(x) for x in self.objects]

View File

@ -31,7 +31,6 @@ from nova import objects
from nova.objects import base as obj_base
from nova.objects import fields
from nova.objects import instance as obj_instance
from nova.objects.resource_provider import ResourceClass
from nova import rpc
@ -104,7 +103,7 @@ def _process_extra_specs(extra_specs, resources):
if key.startswith("resources:")}
resource_keys = set(resource_specs)
custom_keys = set([key for key in resource_keys
if key.startswith(ResourceClass.CUSTOM_NAMESPACE)])
if key.startswith(fields.ResourceClass.CUSTOM_NAMESPACE)])
std_keys = resource_keys - custom_keys
def validate_int(key):

View File

@ -1459,7 +1459,7 @@ class ResourceClassListTestCase(ResourceProviderBaseCase):
class ResourceClassTestCase(ResourceProviderBaseCase):
def test_get_by_name(self):
rc = objects.ResourceClass.get_by_name(
rc = rp_obj.ResourceClass.get_by_name(
self.ctx,
fields.ResourceClass.VCPU
)
@ -1471,17 +1471,17 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
def test_get_by_name_not_found(self):
self.assertRaises(exception.ResourceClassNotFound,
objects.ResourceClass.get_by_name,
rp_obj.ResourceClass.get_by_name,
self.ctx,
'CUSTOM_NO_EXISTS')
def test_get_by_name_custom(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
rc.create()
get_rc = objects.ResourceClass.get_by_name(
get_rc = rp_obj.ResourceClass.get_by_name(
self.ctx,
'CUSTOM_IRON_NFV',
)
@ -1489,7 +1489,7 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
self.assertEqual(rc.name, get_rc.name)
def test_create_fail_not_using_namespace(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
context=self.ctx,
name='IRON_NFV',
)
@ -1497,39 +1497,40 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
self.assertIn('name must start with', str(exc))
def test_create_duplicate_standard(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
context=self.ctx,
name=fields.ResourceClass.VCPU,
)
self.assertRaises(exception.ResourceClassExists, rc.create)
def test_create(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
rc.create()
min_id = objects.ResourceClass.MIN_CUSTOM_RESOURCE_CLASS_ID
min_id = rp_obj.ResourceClass.MIN_CUSTOM_RESOURCE_CLASS_ID
self.assertEqual(min_id, rc.id)
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_ENTERPRISE',
)
rc.create()
self.assertEqual(min_id + 1, rc.id)
@mock.patch.object(nova.objects.ResourceClass, "_get_next_id")
@mock.patch.object(nova.objects.resource_provider.ResourceClass,
"_get_next_id")
def test_create_duplicate_id_retry(self, mock_get):
# This order of ID generation will create rc1 with an ID of 42, try to
# create rc2 with the same ID, and then return 43 in the retry loop.
mock_get.side_effect = (42, 42, 43)
rc1 = objects.ResourceClass(
rc1 = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
rc1.create()
rc2 = objects.ResourceClass(
rc2 = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_TWO',
)
@ -1537,18 +1538,19 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
self.assertEqual(rc1.id, 42)
self.assertEqual(rc2.id, 43)
@mock.patch.object(nova.objects.ResourceClass, "_get_next_id")
@mock.patch.object(nova.objects.resource_provider.ResourceClass,
"_get_next_id")
def test_create_duplicate_id_retry_failing(self, mock_get):
"""negative case for test_create_duplicate_id_retry"""
# This order of ID generation will create rc1 with an ID of 44, try to
# create rc2 with the same ID, and then return 45 in the retry loop.
mock_get.side_effect = (44, 44, 44, 44)
rc1 = objects.ResourceClass(
rc1 = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
rc1.create()
rc2 = objects.ResourceClass(
rc2 = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_TWO',
)
@ -1556,28 +1558,28 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
self.assertRaises(exception.MaxDBRetriesExceeded, rc2.create)
def test_create_duplicate_custom(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
rc.create()
self.assertEqual(objects.ResourceClass.MIN_CUSTOM_RESOURCE_CLASS_ID,
self.assertEqual(rp_obj.ResourceClass.MIN_CUSTOM_RESOURCE_CLASS_ID,
rc.id)
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
self.assertRaises(exception.ResourceClassExists, rc.create)
def test_destroy_fail_no_id(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
self.assertRaises(exception.ObjectActionError, rc.destroy)
def test_destroy_fail_standard(self):
rc = objects.ResourceClass.get_by_name(
rc = rp_obj.ResourceClass.get_by_name(
self.ctx,
'VCPU',
)
@ -1585,7 +1587,7 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
rc.destroy)
def test_destroy(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
@ -1594,7 +1596,7 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
rc_ids = (r.id for r in rc_list)
self.assertIn(rc.id, rc_ids)
rc = objects.ResourceClass.get_by_name(
rc = rp_obj.ResourceClass.get_by_name(
self.ctx,
'CUSTOM_IRON_NFV',
)
@ -1606,7 +1608,7 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
# Verify rc cache was purged of the old entry
self.assertRaises(exception.ResourceClassNotFound,
objects.ResourceClass.get_by_name,
rp_obj.ResourceClass.get_by_name,
self.ctx,
'CUSTOM_IRON_NFV')
@ -1614,7 +1616,7 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
"""Test that we raise an exception when attempting to delete a resource
class that is referenced in an inventory record.
"""
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
@ -1644,14 +1646,14 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
self.assertNotIn(rc.id, rc_ids)
def test_save_fail_no_id(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
self.assertRaises(exception.ObjectActionError, rc.save)
def test_save_fail_standard(self):
rc = objects.ResourceClass.get_by_name(
rc = rp_obj.ResourceClass.get_by_name(
self.ctx,
'VCPU',
)
@ -1659,13 +1661,13 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
rc.save)
def test_save(self):
rc = objects.ResourceClass(
rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_IRON_NFV',
)
rc.create()
rc = objects.ResourceClass.get_by_name(
rc = rp_obj.ResourceClass.get_by_name(
self.ctx,
'CUSTOM_IRON_NFV',
)
@ -1674,7 +1676,7 @@ class ResourceClassTestCase(ResourceProviderBaseCase):
# Verify rc cache was purged of the old entry
self.assertRaises(exception.NotFound,
objects.ResourceClass.get_by_name,
rp_obj.ResourceClass.get_by_name,
self.ctx,
'CUSTOM_IRON_NFV')
@ -2855,7 +2857,7 @@ class AllocationCandidatesTestCase(ResourceProviderBaseCase):
cn.set_inventory(inv_list)
# Create a custom resource called MAGIC
magic_rc = objects.ResourceClass(
magic_rc = rp_obj.ResourceClass(
self.ctx,
name='CUSTOM_MAGIC',
)

View File

@ -20,6 +20,7 @@ import webob
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement import util
from nova import objects
from nova.objects import resource_provider as rp_obj
from nova import test
from nova.tests import uuidsentinel
@ -264,7 +265,7 @@ class TestPlacementURLs(test.NoDBTestCase):
self.resource_provider = objects.ResourceProvider(
name=uuidsentinel.rp_name,
uuid=uuidsentinel.rp_uuid)
self.resource_class = objects.ResourceClass(
self.resource_class = rp_obj.ResourceClass(
name='CUSTOM_BAREMETAL_GOLD',
id=1000)

View File

@ -1150,7 +1150,6 @@ object_data = {
'Quotas': '1.3-40fcefe522111dddd3e5e6155702cf4e',
'QuotasNoOp': '1.3-347a039fc7cfee7b225b68b5181e0733',
'RequestSpec': '1.8-35033ecef47a880f9a5e46e2269e2b97',
'ResourceClass': '1.0-e6b367e2cf1733c5f3526f20a3286fe9',
'ResourceProvider': '1.4-35e8a41d2ece17a862fac5b07ca966af',
'ResourceProviderList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e',

View File

@ -574,12 +574,13 @@ class TestResourceClass(test.NoDBTestCase):
self.context = context.RequestContext(self.user_id, self.project_id)
def test_cannot_create_with_id(self):
rc = objects.ResourceClass(self.context, id=1, name='CUSTOM_IRON_NFV')
rc = resource_provider.ResourceClass(self.context, id=1,
name='CUSTOM_IRON_NFV')
exc = self.assertRaises(exception.ObjectActionError, rc.create)
self.assertIn('already created', str(exc))
def test_cannot_create_requires_name(self):
rc = objects.ResourceClass(self.context)
rc = resource_provider.ResourceClass(self.context)
exc = self.assertRaises(exception.ObjectActionError, rc.create)
self.assertIn('name is required', str(exc))