object: Introduce Resource and ResourceList objs
Introduce Resource and ResourceList objects, and add new resources field to instance, also old_/new_ resources fields to migration_context, Resource object can contain a generic resource information. Change-Id: I095bb71d2a5e451f2827f646aa5b3ed31d2ccac3 Partially-Implements: blueprint virtual-persistent-memory Co-Authored-By: He Jie Xu <hejie.xu@intel.com>
This commit is contained in:
parent
6a4d6ec786
commit
384d783fc6
|
@ -65,6 +65,7 @@ def register_all():
|
|||
__import__('nova.objects.request_spec')
|
||||
__import__('nova.objects.tag')
|
||||
__import__('nova.objects.quotas')
|
||||
__import__('nova.objects.resource')
|
||||
__import__('nova.objects.security_group')
|
||||
__import__('nova.objects.security_group_rule')
|
||||
__import__('nova.objects.selection')
|
||||
|
|
|
@ -31,6 +31,23 @@ from nova.objects import fields as obj_fields
|
|||
from nova import utils
|
||||
|
||||
|
||||
def all_things_equal(obj_a, obj_b):
|
||||
if obj_b is None:
|
||||
return False
|
||||
|
||||
for name in obj_a.fields:
|
||||
set_a = name in obj_a
|
||||
set_b = name in obj_b
|
||||
if set_a != set_b:
|
||||
return False
|
||||
elif not set_a:
|
||||
continue
|
||||
|
||||
if getattr(obj_a, name) != getattr(obj_b, name):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_attrname(name):
|
||||
"""Return the mangled name of the attribute's underlying storage."""
|
||||
# FIXME(danms): This is just until we use o.vo's class properties
|
||||
|
|
|
@ -90,6 +90,24 @@ IPV4Network = fields.IPV4Network
|
|||
IPV6Network = fields.IPV6Network
|
||||
|
||||
|
||||
class ResourceClass(fields.StringPattern):
|
||||
|
||||
PATTERN = r"^[A-Z0-9_]+$"
|
||||
_REGEX = re.compile(PATTERN)
|
||||
|
||||
@staticmethod
|
||||
def coerce(obj, attr, value):
|
||||
if isinstance(value, six.string_types):
|
||||
uppered = value.upper()
|
||||
if ResourceClass._REGEX.match(uppered):
|
||||
return uppered
|
||||
raise ValueError(_("Malformed Resource Class %s") % value)
|
||||
|
||||
|
||||
class ResourceClassField(AutoTypedField):
|
||||
AUTO_TYPE = ResourceClass()
|
||||
|
||||
|
||||
class SetOfStringsField(AutoTypedField):
|
||||
AUTO_TYPE = Set(fields.String())
|
||||
|
||||
|
|
|
@ -56,10 +56,11 @@ _INSTANCE_OPTIONAL_NON_COLUMN_FIELDS = ['flavor', 'old_flavor',
|
|||
# These are fields that are optional and in instance_extra
|
||||
_INSTANCE_EXTRA_FIELDS = ['numa_topology', 'pci_requests',
|
||||
'flavor', 'vcpu_model', 'migration_context',
|
||||
'keypairs', 'device_metadata', 'trusted_certs']
|
||||
'keypairs', 'device_metadata', 'trusted_certs',
|
||||
'resources']
|
||||
# These are fields that applied/drooped by migration_context
|
||||
_MIGRATION_CONTEXT_ATTRS = ['numa_topology', 'pci_requests',
|
||||
'pci_devices']
|
||||
'pci_devices', 'resources']
|
||||
|
||||
# These are fields that can be specified as expected_attrs
|
||||
INSTANCE_OPTIONAL_ATTRS = (_INSTANCE_OPTIONAL_JOINED_FIELDS +
|
||||
|
@ -114,7 +115,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
|||
# Version 2.4: Added trusted_certs
|
||||
# Version 2.5: Added hard_delete kwarg in destroy
|
||||
# Version 2.6: Added hidden
|
||||
VERSION = '2.6'
|
||||
# Version 2.7: Added resources
|
||||
VERSION = '2.7'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
|
@ -217,6 +219,7 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
|||
'keypairs': fields.ObjectField('KeyPairList'),
|
||||
'trusted_certs': fields.ObjectField('TrustedCerts', nullable=True),
|
||||
'hidden': fields.BooleanField(default=False),
|
||||
'resources': fields.ObjectField('ResourceList', nullable=True),
|
||||
}
|
||||
|
||||
obj_extra_fields = ['name']
|
||||
|
@ -224,6 +227,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
|||
def obj_make_compatible(self, primitive, target_version):
|
||||
super(Instance, self).obj_make_compatible(primitive, target_version)
|
||||
target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
if target_version < (2, 7) and 'resources' in primitive:
|
||||
del primitive['resources']
|
||||
if target_version < (2, 6) and 'hidden' in primitive:
|
||||
del primitive['hidden']
|
||||
if target_version < (2, 4) and 'trusted_certs' in primitive:
|
||||
|
@ -487,6 +492,12 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
|||
db_inst['extra'].get('trusted_certs'))
|
||||
else:
|
||||
instance.trusted_certs = None
|
||||
if 'resources' in expected_attrs:
|
||||
if have_extra:
|
||||
instance._load_resources(
|
||||
db_inst['extra'].get('resources'))
|
||||
else:
|
||||
instance.resources = None
|
||||
if any([x in expected_attrs for x in ('flavor',
|
||||
'old_flavor',
|
||||
'new_flavor')]):
|
||||
|
@ -601,6 +612,13 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
|||
trusted_certs.obj_to_primitive())
|
||||
else:
|
||||
updates['extra']['trusted_certs'] = None
|
||||
resources = updates.pop('resources', None)
|
||||
expected_attrs.append('resources')
|
||||
if resources:
|
||||
updates['extra']['resources'] = jsonutils.dumps(
|
||||
resources.obj_to_primitive())
|
||||
else:
|
||||
updates['extra']['resources'] = None
|
||||
db_inst = db.instance_create(self._context, updates)
|
||||
self._from_db_object(self._context, self, db_inst, expected_attrs)
|
||||
|
||||
|
@ -983,6 +1001,16 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
|||
self.trusted_certs = objects.TrustedCerts.obj_from_primitive(
|
||||
jsonutils.loads(db_trusted_certs))
|
||||
|
||||
def _load_resources(self, db_resources=_NO_DATA_SENTINEL):
|
||||
if db_resources is None:
|
||||
self.resources = None
|
||||
elif db_resources is _NO_DATA_SENTINEL:
|
||||
self.resources = objects.ResourceList.get_by_instance_uuid(
|
||||
self._context, self.uuid)
|
||||
else:
|
||||
self.resources = objects.ResourceList.obj_from_primitive(
|
||||
jsonutils.loads(db_resources))
|
||||
|
||||
def apply_migration_context(self):
|
||||
if self.migration_context:
|
||||
self._set_migration_context_to_instance(prefix='new_')
|
||||
|
@ -1098,6 +1126,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
|
|||
return self._load_keypairs()
|
||||
elif attrname == 'trusted_certs':
|
||||
return self._load_trusted_certs()
|
||||
elif attrname == 'resources':
|
||||
return self._load_resources()
|
||||
elif attrname == 'security_groups':
|
||||
self._load_security_groups()
|
||||
elif attrname == 'pci_devices':
|
||||
|
|
|
@ -38,7 +38,8 @@ class MigrationContext(base.NovaPersistentObject, base.NovaObject):
|
|||
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Add old/new pci_devices and pci_requests
|
||||
VERSION = '1.1'
|
||||
# Version 1.2: Add old/new resources
|
||||
VERSION = '1.2'
|
||||
|
||||
fields = {
|
||||
'instance_uuid': fields.UUIDField(),
|
||||
|
@ -55,11 +56,18 @@ class MigrationContext(base.NovaPersistentObject, base.NovaObject):
|
|||
nullable=True),
|
||||
'old_pci_requests': fields.ObjectField('InstancePCIRequests',
|
||||
nullable=True),
|
||||
'new_resources': fields.ObjectField('ResourceList',
|
||||
nullable=True),
|
||||
'old_resources': fields.ObjectField('ResourceList',
|
||||
nullable=True),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_make_compatible(cls, primitive, target_version):
|
||||
target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
if target_version < (1, 2):
|
||||
primitive.pop('old_resources', None)
|
||||
primitive.pop('new_resources', None)
|
||||
if target_version < (1, 1):
|
||||
primitive.pop('old_pci_devices', None)
|
||||
primitive.pop('new_pci_devices', None)
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# Copyright 2019 Intel Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from nova.db import api as db
|
||||
from nova.objects import base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class ResourceMetadata(base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = "1.0"
|
||||
|
||||
# This is parent object of specific resources.
|
||||
# And it's used to be a object field of Resource,
|
||||
# that is to say Resource.metadata.
|
||||
|
||||
def __eq__(self, other):
|
||||
return base.all_things_equal(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class Resource(base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = "1.0"
|
||||
|
||||
fields = {
|
||||
# UUID of resource provider
|
||||
'provider_uuid': fields.UUIDField(),
|
||||
# resource class of the Resource
|
||||
'resource_class': fields.ResourceClassField(),
|
||||
# identifier is used to identify resource, it is up to virt drivers
|
||||
# for mdev, it will be a UUID, for vpmem, it's backend namespace name
|
||||
'identifier': fields.StringField(),
|
||||
# metadata is used to contain virt driver specific resource info
|
||||
'metadata': fields.ObjectField('ResourceMetadata', subclasses=True),
|
||||
}
|
||||
|
||||
def __eq__(self, other):
|
||||
return base.all_things_equal(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def __hash__(self):
|
||||
metadata = self.metadata if 'metadata' in self else None
|
||||
return hash((self.provider_uuid, self.resource_class,
|
||||
self.identifier, metadata))
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class ResourceList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = "1.0"
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Resource'),
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_instance_uuid(cls, context, instance_uuid):
|
||||
db_extra = db.instance_extra_get_by_instance_uuid(
|
||||
context, instance_uuid, columns=['resources'])
|
||||
if not db_extra or db_extra['resources'] is None:
|
||||
return None
|
||||
|
||||
primitive = jsonutils.loads(db_extra['resources'])
|
||||
resources = cls.obj_from_primitive(primitive)
|
||||
return resources
|
|
@ -75,6 +75,7 @@ def fake_db_instance(**updates):
|
|||
'vcpu_model': None,
|
||||
'device_metadata': None,
|
||||
'trusted_certs': None,
|
||||
'resources': None,
|
||||
},
|
||||
'tags': [],
|
||||
'services': []
|
||||
|
|
|
@ -145,11 +145,13 @@ class _TestInstanceObject(object):
|
|||
exp_cols.remove('keypairs')
|
||||
exp_cols.remove('device_metadata')
|
||||
exp_cols.remove('trusted_certs')
|
||||
exp_cols.remove('resources')
|
||||
exp_cols = [exp_col for exp_col in exp_cols if 'flavor' not in exp_col]
|
||||
exp_cols.extend(['extra', 'extra.numa_topology', 'extra.pci_requests',
|
||||
'extra.flavor', 'extra.vcpu_model',
|
||||
'extra.migration_context', 'extra.keypairs',
|
||||
'extra.device_metadata', 'extra.trusted_certs'])
|
||||
'extra.device_metadata', 'extra.trusted_certs',
|
||||
'extra.resources'])
|
||||
|
||||
fake_topology = test_instance_numa.fake_db_topology['numa_topology']
|
||||
fake_requests = jsonutils.dumps(test_instance_pci_requests.
|
||||
|
@ -169,6 +171,11 @@ class _TestInstanceObject(object):
|
|||
fake_keypairlist.obj_to_primitive())
|
||||
fake_trusted_certs = jsonutils.dumps(
|
||||
objects.TrustedCerts(ids=['123foo']).obj_to_primitive())
|
||||
fake_resource = objects.Resource(
|
||||
provider_uuid=uuids.rp, resource_class='CUSTOM_FOO',
|
||||
identifier='foo')
|
||||
fake_resources = jsonutils.dumps(objects.ResourceList(
|
||||
objects=[fake_resource]).obj_to_primitive())
|
||||
fake_service = {'created_at': None, 'updated_at': None,
|
||||
'deleted_at': None, 'deleted': False, 'id': 123,
|
||||
'host': 'fake-host', 'binary': 'nova-compute',
|
||||
|
@ -188,6 +195,7 @@ class _TestInstanceObject(object):
|
|||
'migration_context': fake_mig_context,
|
||||
'keypairs': fake_keypairs,
|
||||
'trusted_certs': fake_trusted_certs,
|
||||
'resources': fake_resources,
|
||||
})
|
||||
|
||||
mock_get.return_value = fake_instance
|
||||
|
@ -203,6 +211,8 @@ class _TestInstanceObject(object):
|
|||
self.assertEqual(123, inst.services[0].id)
|
||||
self.assertEqual('foo', inst.keypairs[0].name)
|
||||
self.assertEqual(['123foo'], inst.trusted_certs.ids)
|
||||
self.assertEqual(fake_resource.identifier,
|
||||
inst.resources[0].identifier)
|
||||
|
||||
mock_get.assert_called_once_with(self.context, 'uuid',
|
||||
columns_to_join=exp_cols)
|
||||
|
@ -968,6 +978,7 @@ class _TestInstanceObject(object):
|
|||
'pci_requests': None,
|
||||
'device_metadata': None,
|
||||
'trusted_certs': None,
|
||||
'resources': None,
|
||||
}}
|
||||
fake_inst = fake_instance.fake_db_instance(**vals)
|
||||
mock_create.return_value = fake_inst
|
||||
|
@ -998,6 +1009,7 @@ class _TestInstanceObject(object):
|
|||
'pci_requests': None,
|
||||
'device_metadata': None,
|
||||
'trusted_certs': None,
|
||||
'resources': None,
|
||||
}}
|
||||
fake_inst = fake_instance.fake_db_instance(**vals)
|
||||
mock_create.return_value = fake_inst
|
||||
|
@ -1015,6 +1027,7 @@ class _TestInstanceObject(object):
|
|||
'pci_requests': None,
|
||||
'device_metadata': None,
|
||||
'trusted_certs': None,
|
||||
'resources': None,
|
||||
}
|
||||
mock_create.return_value = self.fake_instance
|
||||
inst = objects.Instance(context=self.context)
|
||||
|
@ -1052,6 +1065,9 @@ class _TestInstanceObject(object):
|
|||
spec=[])]),
|
||||
vcpu_model=test_vcpu_model.fake_vcpumodel,
|
||||
trusted_certs=objects.TrustedCerts(ids=['123foo']),
|
||||
resources=objects.ResourceList(objects=[objects.Resource(
|
||||
provider_uuid=uuids.rp, resource_class='CUSTOM_FOO',
|
||||
identifier='foo')])
|
||||
)
|
||||
inst.create()
|
||||
self.assertIsNotNone(inst.numa_topology)
|
||||
|
@ -1069,6 +1085,7 @@ class _TestInstanceObject(object):
|
|||
self.context, inst.uuid)
|
||||
self.assertEqual('fake-model', vcpu_model.model)
|
||||
self.assertEqual(['123foo'], inst.trusted_certs.ids)
|
||||
self.assertEqual('foo', inst.resources[0].identifier)
|
||||
|
||||
def test_recreate_fails(self):
|
||||
inst = objects.Instance(context=self.context,
|
||||
|
@ -1107,6 +1124,7 @@ class _TestInstanceObject(object):
|
|||
'pci_requests': None,
|
||||
'device_metadata': None,
|
||||
'trusted_certs': None,
|
||||
'resources': None,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -1348,7 +1366,8 @@ class _TestInstanceObject(object):
|
|||
inst.apply_migration_context()
|
||||
attrs_type = {'numa_topology': objects.InstanceNUMATopology,
|
||||
'pci_requests': objects.InstancePCIRequests,
|
||||
'pci_devices': objects.PciDeviceList}
|
||||
'pci_devices': objects.PciDeviceList,
|
||||
'resources': objects.ResourceList}
|
||||
|
||||
for attr_name in instance._MIGRATION_CONTEXT_ATTRS:
|
||||
value = getattr(inst, attr_name)
|
||||
|
@ -1379,14 +1398,17 @@ class _TestInstanceObject(object):
|
|||
pci_requests = objects.InstancePCIRequests(requests=[
|
||||
objects.InstancePCIRequest(count=1, spec=[])])
|
||||
pci_devices = pci_device.PciDeviceList()
|
||||
resources = objects.ResourceList()
|
||||
|
||||
inst = instance.Instance(context=self.context, uuid=uuids.instance,
|
||||
numa_topology=numa_topology,
|
||||
pci_requests=pci_requests,
|
||||
pci_devices=pci_devices)
|
||||
pci_devices=pci_devices,
|
||||
resources=resources)
|
||||
expected_objs = {'numa_topology': numa_topology,
|
||||
'pci_requests': pci_requests,
|
||||
'pci_devices': pci_devices}
|
||||
'pci_devices': pci_devices,
|
||||
'resources': resources}
|
||||
inst.migration_context = test_mig_ctxt.get_fake_migration_context_obj(
|
||||
self.context)
|
||||
with inst.mutated_migration_context():
|
||||
|
@ -1491,6 +1513,15 @@ class _TestInstanceObject(object):
|
|||
mock_get.assert_called_once_with(self.context, uuids.pci_devices)
|
||||
self.assertEqual(fake_pci_devices, pci_devices)
|
||||
|
||||
@mock.patch('nova.objects.ResourceList.get_by_instance_uuid')
|
||||
def test_load_resources(self, mock_get):
|
||||
fake_resources = objects.ResourceList()
|
||||
mock_get.return_value = fake_resources
|
||||
inst = objects.Instance(context=self.context, uuid=uuids.resources)
|
||||
resources = inst.resources
|
||||
mock_get.assert_called_once_with(self.context, uuids.resources)
|
||||
self.assertEqual(fake_resources, resources)
|
||||
|
||||
def test_get_with_extras(self):
|
||||
pci_requests = objects.InstancePCIRequests(requests=[
|
||||
objects.InstancePCIRequest(count=123, spec=[])])
|
||||
|
|
|
@ -34,6 +34,8 @@ fake_migration_context_obj.new_pci_requests = (
|
|||
objects.InstancePCIRequests(requests=[
|
||||
objects.InstancePCIRequest(count=123, spec=[])]))
|
||||
fake_migration_context_obj.old_pci_requests = None
|
||||
fake_migration_context_obj.new_resources = objects.ResourceList()
|
||||
fake_migration_context_obj.old_resources = None
|
||||
|
||||
fake_db_context = {
|
||||
'created_at': None,
|
||||
|
@ -96,6 +98,10 @@ class _TestMigrationContext(object):
|
|||
mig_context.new_pci_requests.__class__)
|
||||
self.assertIsInstance(expected_mig_context.old_pci_requests,
|
||||
mig_context.old_pci_requests.__class__)
|
||||
self.assertIsInstance(expected_mig_context. new_resources,
|
||||
mig_context.new_resources.__class__)
|
||||
self.assertIsInstance(expected_mig_context.old_resources,
|
||||
mig_context.old_resources.__class__)
|
||||
else:
|
||||
self.assertIsNone(mig_context)
|
||||
|
||||
|
|
|
@ -1070,7 +1070,7 @@ object_data = {
|
|||
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
|
||||
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
|
||||
'ImageMetaProps': '1.24-f92fa09d54185499da98f5430524964e',
|
||||
'Instance': '2.6-5fefbcb483703c85e4d328b887c8af33',
|
||||
'Instance': '2.7-d187aec68cad2e4d8b8a03a68e4739ce',
|
||||
'InstanceAction': '1.2-9a5abc87fdd3af46f45731960651efb5',
|
||||
'InstanceActionEvent': '1.3-c749e1b3589e7117c81cb2aa6ac438d5',
|
||||
'InstanceActionEventList': '1.1-13d92fb953030cdbfee56481756e02be',
|
||||
|
@ -1095,7 +1095,7 @@ object_data = {
|
|||
'LibvirtLiveMigrateData': '1.9-7082cc7dd48ca49df71fe3846521b2f3',
|
||||
'MemoryDiagnostics': '1.0-2c995ae0f2223bb0f8e523c5cc0b83da',
|
||||
'Migration': '1.7-b77066a88d08bdb0b05d7bc18780c55a',
|
||||
'MigrationContext': '1.1-9fb17b0b521370957a884636499df52d',
|
||||
'MigrationContext': '1.2-89f10a83999f852a489962ae37d8a026',
|
||||
'MigrationList': '1.4-983a9c29d4f1e747ce719dc9063b729b',
|
||||
'MonitorMetric': '1.1-53b1db7c4ae2c531db79761e7acc52ba',
|
||||
'MonitorMetricList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
|
||||
|
@ -1120,6 +1120,9 @@ object_data = {
|
|||
'QuotasNoOp': '1.3-347a039fc7cfee7b225b68b5181e0733',
|
||||
'RequestGroup': '1.3-0458d350a8ec9d0673f9be5640a990ce',
|
||||
'RequestSpec': '1.12-25010470f219af9b6163f2a457a513f5',
|
||||
'Resource': '1.0-d8a2abbb380da583b995fd118f6a8953',
|
||||
'ResourceList': '1.0-4a53826625cc280e15fae64a575e0879',
|
||||
'ResourceMetadata': '1.0-77509ea1ea0dd750d5864b9bd87d3f9d',
|
||||
'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e',
|
||||
'SCSIDeviceBus': '1.0-61c1e89a00901069ab1cf2991681533b',
|
||||
'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641',
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils.fixture import uuidsentinel as uuids
|
||||
import six
|
||||
|
||||
from nova.objects import resource
|
||||
from nova.tests.unit.objects import test_objects
|
||||
|
||||
|
||||
fake_resources = resource.ResourceList(objects=[
|
||||
resource.Resource(provider_uuid=uuids.rp, resource_class='CUSTOM_RESOURCE',
|
||||
identifier='foo'),
|
||||
resource.Resource(provider_uuid=uuids.rp, resource_class='CUSTOM_RESOURCE',
|
||||
identifier='bar')])
|
||||
|
||||
fake_instance_extras = {
|
||||
'resources': jsonutils.dumps(fake_resources.obj_to_primitive())
|
||||
}
|
||||
|
||||
|
||||
class TestResourceObject(test_objects._LocalTest):
|
||||
def _test_set_malformed_resource_class(self, rc):
|
||||
try:
|
||||
resource.Resource(provider_uuid=uuids.rp,
|
||||
resource_class=rc,
|
||||
identifier='foo')
|
||||
except ValueError as e:
|
||||
self.assertEqual('Malformed Resource Class %s' % rc,
|
||||
six.text_type(e))
|
||||
else:
|
||||
self.fail('Check malformed resource class failed.')
|
||||
|
||||
def _test_set_formed_resource_class(self, rc):
|
||||
resource.Resource(provider_uuid=uuids.rp,
|
||||
resource_class=rc,
|
||||
identifier='foo')
|
||||
|
||||
def test_set_malformed_resource_classes(self):
|
||||
malformed_resource_classes = ['!', ';', ' ']
|
||||
for rc in malformed_resource_classes:
|
||||
self._test_set_malformed_resource_class(rc)
|
||||
|
||||
def test_set_formed_resource_classes(self):
|
||||
formed_resource_classes = ['resource', 'RESOURCE', '0123']
|
||||
for rc in formed_resource_classes:
|
||||
self._test_set_formed_resource_class(rc)
|
||||
|
||||
def test_equal_without_metadata(self):
|
||||
resource_0 = resource.Resource(provider_uuid=uuids.rp,
|
||||
resource_class='bar',
|
||||
identifier='foo')
|
||||
resource_1 = resource.Resource(provider_uuid=uuids.rp,
|
||||
resource_class='bar',
|
||||
identifier='foo')
|
||||
self.assertEqual(resource_0, resource_1)
|
||||
|
||||
def test_not_equal_without_matadata(self):
|
||||
self.assertNotEqual(fake_resources[0], fake_resources[1])
|
||||
|
||||
|
||||
class _TestResourceListObject(object):
|
||||
@mock.patch('nova.db.api.instance_extra_get_by_instance_uuid')
|
||||
def test_get_by_instance_uuid(self, mock_get):
|
||||
mock_get.return_value = fake_instance_extras
|
||||
resources = resource.ResourceList.get_by_instance_uuid(
|
||||
self.context, 'fake_uuid')
|
||||
for i in range(len(resources)):
|
||||
self.assertEqual(resources[i].identifier,
|
||||
fake_resources[i].identifier)
|
||||
|
||||
|
||||
class TestResourceListObject(test_objects._LocalTest,
|
||||
_TestResourceListObject):
|
||||
pass
|
||||
|
||||
|
||||
class TestRemoteResourceListObject(test_objects._RemoteTest,
|
||||
_TestResourceListObject):
|
||||
pass
|
Loading…
Reference in New Issue