From e948aebda73f3a97582afb02cb1269beadeac2e3 Mon Sep 17 00:00:00 2001 From: yunhong jiang Date: Thu, 15 Jan 2015 14:34:18 -0800 Subject: [PATCH] Add VirtCPUModel nova objects The VirtCPUModel is to present the guest cpu modelling. As this is a quite new object, no from/to_dict function is needed. The VirtCPUModel object has limitation to the arch and mode, but not for model or features, so that no update needed when new CPU model or features are released. Co-authored: Daniel P. Berrange Change-Id: Ied869f587981804fa23f6b705b5da98a509790e2 Implements: blueprint resource-objects --- nova/compute/cpumodel.py | 45 +++++++++ nova/objects/__init__.py | 1 + nova/objects/vcpu_model.py | 75 +++++++++++++++ nova/tests/unit/objects/test_objects.py | 5 +- nova/tests/unit/objects/test_vcpu_model.py | 102 +++++++++++++++++++++ 5 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 nova/compute/cpumodel.py create mode 100644 nova/objects/vcpu_model.py create mode 100644 nova/tests/unit/objects/test_vcpu_model.py diff --git a/nova/compute/cpumodel.py b/nova/compute/cpumodel.py new file mode 100644 index 000000000000..5f531fbe6363 --- /dev/null +++ b/nova/compute/cpumodel.py @@ -0,0 +1,45 @@ +# 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. + +POLICY_FORCE = 'force' +POLICY_REQUIRE = 'require' +POLICY_OPTIONAL = 'optional' +POLICY_DISABLE = 'disable' +POLICY_FORBID = 'forbid' + +ALL_POLICIES = [ + POLICY_FORCE, + POLICY_REQUIRE, + POLICY_OPTIONAL, + POLICY_DISABLE, + POLICY_FORBID, +] + +MODE_CUSTOM = 'custom' +MODE_HOST_MODEL = 'host-model' +MODE_HOST_PASSTHROUGH = 'host-passthrough' + +ALL_CPUMODES = [ + MODE_CUSTOM, + MODE_HOST_MODEL, + MODE_HOST_PASSTHROUGH, +] + +MATCH_MINIMUM = 'minimum' +MATCH_EXACT = 'exact' +MATCH_STRICT = 'strict' + +ALL_MATCHES = [ + MATCH_MINIMUM, + MATCH_EXACT, + MATCH_STRICT, +] diff --git a/nova/objects/__init__.py b/nova/objects/__init__.py index 6ab4ee0fad6a..27c6ea20e71a 100644 --- a/nova/objects/__init__.py +++ b/nova/objects/__init__.py @@ -55,5 +55,6 @@ def register_all(): __import__('nova.objects.security_group') __import__('nova.objects.security_group_rule') __import__('nova.objects.service') + __import__('nova.objects.vcpu_model') __import__('nova.objects.virt_cpu_topology') __import__('nova.objects.virtual_interface') diff --git a/nova/objects/vcpu_model.py b/nova/objects/vcpu_model.py new file mode 100644 index 000000000000..fbdf6eebc428 --- /dev/null +++ b/nova/objects/vcpu_model.py @@ -0,0 +1,75 @@ +# 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.compute import arch +from nova.compute import cpumodel +from nova import db +from nova.objects import base +from nova.objects import fields + + +class VirtCPUModel(base.NovaObject): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'arch': fields.EnumField(nullable=True, + valid_values=arch.ALL), + 'vendor': fields.StringField(nullable=True), + 'topology': fields.ObjectField('VirtCPUTopology', + nullable=True), + 'features': fields.ListOfObjectsField("VirtCPUFeature", + default=[]), + 'mode': fields.EnumField(nullable=True, + valid_values=cpumodel.ALL_CPUMODES), + 'model': fields.StringField(nullable=True), + 'match': fields.EnumField(nullable=True, + valid_values=cpumodel.ALL_MATCHES), + } + + obj_relationships = { + 'topology': [('1.0', '1.0')], + 'features': [('1.0', '1.0')], + } + + def obj_load_attr(self, attrname): + setattr(self, attrname, None) + + def to_json(self): + return jsonutils.dumps(self.obj_to_primitive()) + + @classmethod + def from_json(cls, jsonstr): + return cls.obj_from_primitive(jsonutils.loads(jsonstr)) + + @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=['vcpu_model']) + if not db_extra or not db_extra['vcpu_model']: + return None + return cls.obj_from_primitive(jsonutils.loads(db_extra['vcpu_model'])) + + +class VirtCPUFeature(base.NovaObject): + VERSION = VirtCPUModel.VERSION + + fields = { + 'policy': fields.EnumField(nullable=True, + valid_values=cpumodel.ALL_POLICIES), + 'name': fields.StringField(nullable=False), + } + + def obj_load_attr(self, attrname): + setattr(self, attrname, None) diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index 9f31d89c7aa7..12d34fe23423 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1182,6 +1182,8 @@ object_data = { 'TestSubclassedObject': '1.6-87177ccbefd7a740a9e261f958e15b00', 'VirtualInterface': '1.0-10fdac4c704102b6d57d6936d6d790d2', 'VirtualInterfaceList': '1.0-accbf02628a8063c1d885077a2bf49b6', + 'VirtCPUFeature': '1.0-3cac8c77d84a632ba79da01a4b87afb9', + 'VirtCPUModel': '1.0-ae051080026849eddf7179e353673756', 'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563', } @@ -1205,7 +1207,8 @@ object_relationships = { 'MyObj': {'MyOwnedObject': '1.0'}, 'SecurityGroupRule': {'SecurityGroup': '1.1'}, 'Service': {'ComputeNode': '1.10'}, - 'TestSubclassedObject': {'MyOwnedObject': '1.0'} + 'TestSubclassedObject': {'MyOwnedObject': '1.0'}, + 'VirtCPUModel': {'VirtCPUTopology': '1.0'}, } diff --git a/nova/tests/unit/objects/test_vcpu_model.py b/nova/tests/unit/objects/test_vcpu_model.py new file mode 100644 index 000000000000..0dde852fc597 --- /dev/null +++ b/nova/tests/unit/objects/test_vcpu_model.py @@ -0,0 +1,102 @@ +# 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 nova.compute import arch +from nova.compute import cpumodel +from nova import objects +from nova.tests.unit.objects import test_objects + +fake_cpu_model_feature = { + 'policy': cpumodel.POLICY_REQUIRE, + 'name': 'sse2', +} + +fake_cpu_model_feature_obj = objects.VirtCPUFeature( + **fake_cpu_model_feature) + +fake_vcpumodel_dict = { + 'arch': arch.I686, + 'vendor': 'fake-vendor', + 'match': cpumodel.MATCH_EXACT, + 'topology': objects.VirtCPUTopology(sockets=1, cores=1, threads=1), + 'features': [fake_cpu_model_feature_obj], + 'mode': cpumodel.MODE_HOST_MODEL, + 'model': 'fake-model', +} +fake_vcpumodel = objects.VirtCPUModel(**fake_vcpumodel_dict) + + +class _TestVirtCPUFeatureObj(object): + def test_policy_limitation(self): + obj = objects.VirtCPUFeature() + self.assertRaises(ValueError, setattr, obj, 'policy', 'foo') + + +class TestVirtCPUFeatureObj(test_objects._LocalTest, + _TestVirtCPUFeatureObj): + pass + + +class TestRemoteVirtCPUFeatureObj(test_objects._LocalTest, + _TestVirtCPUFeatureObj): + pass + + +class _TestVirtCPUModel(object): + def test_create(self): + model = objects.VirtCPUModel(**fake_vcpumodel_dict) + self.assertEqual(fake_vcpumodel_dict['model'], model.model) + self.assertEqual(fake_vcpumodel_dict['topology']['sockets'], + model.topology.sockets) + feature = model.features[0] + self.assertEqual(fake_cpu_model_feature['policy'], + feature.policy) + + def test_defaults(self): + model = objects.VirtCPUModel() + self.assertIsNone(model.mode) + self.assertIsNone(model.model) + self.assertIsNone(model.vendor) + self.assertIsNone(model.arch) + self.assertIsNone(model.match) + self.assertEqual([], model.features) + self.assertIsNone(model.topology) + + def test_arch_field(self): + model = objects.VirtCPUModel(**fake_vcpumodel_dict) + self.assertRaises(ValueError, setattr, model, 'arch', 'foo') + + def test_serialize(self): + modelin = objects.VirtCPUModel(**fake_vcpumodel_dict) + modelout = objects.VirtCPUModel.from_json(modelin.to_json()) + + self.assertEqual(modelin.mode, modelout.mode) + self.assertEqual(modelin.model, modelout.model) + self.assertEqual(modelin.vendor, modelout.vendor) + self.assertEqual(modelin.arch, modelout.arch) + self.assertEqual(modelin.match, modelout.match) + self.assertEqual(modelin.features[0].policy, + modelout.features[0].policy) + self.assertEqual(modelin.features[0].name, modelout.features[0].name) + self.assertEqual(modelin.topology.sockets, modelout.topology.sockets) + self.assertEqual(modelin.topology.cores, modelout.topology.cores) + self.assertEqual(modelin.topology.threads, modelout.topology.threads) + + +class TestVirtCPUModel(test_objects._LocalTest, + _TestVirtCPUModel): + pass + + +class TestRemoteVirtCPUModel(test_objects._LocalTest, + _TestVirtCPUModel): + pass