From 51248a4c01ec388c31b633cd95e90207d4b831d2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 7 Jun 2018 15:15:48 +0100 Subject: [PATCH] objects: Add RequestSpec.network_metadata We need a way to pass information about networks from the conductor to the scheduler. These are not really part of the network requests but rather metadata about these networks, meaning the 'NetworkRequest' object doesn't really make sense. Instead, we use the recently added 'NetworkMetadata' object like the good citizens we are. Part of blueprint numa-aware-vswitches Change-Id: Icb295bbd8c83e2e340a7ac3ecc1f159e0db7c7b1 --- nova/objects/request_spec.py | 19 ++++++++++++-- nova/tests/unit/objects/test_objects.py | 2 +- nova/tests/unit/objects/test_request_spec.py | 26 +++++++++++++++++--- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/nova/objects/request_spec.py b/nova/objects/request_spec.py index 5db3d26a89b7..d420d4565944 100644 --- a/nova/objects/request_spec.py +++ b/nova/objects/request_spec.py @@ -27,7 +27,8 @@ from nova.scheduler import utils as scheduler_utils from nova.virt import hardware REQUEST_SPEC_OPTIONAL_ATTRS = ['requested_destination', - 'security_groups'] + 'security_groups', + 'network_metadata'] @base.NovaObjectRegistry.register @@ -42,7 +43,8 @@ class RequestSpec(base.NovaObject): # Version 1.7: Added destroy() # Version 1.8: Added security_groups # Version 1.9: Added user_id - VERSION = '1.9' + # Version 1.10: Added network_metadata + VERSION = '1.10' fields = { 'id': fields.IntegerField(), @@ -79,11 +81,15 @@ class RequestSpec(base.NovaObject): 'scheduler_hints': fields.DictOfListOfStringsField(nullable=True), 'instance_uuid': fields.UUIDField(), 'security_groups': fields.ObjectField('SecurityGroupList'), + 'network_metadata': fields.ObjectField('NetworkMetadata'), } def obj_make_compatible(self, primitive, target_version): super(RequestSpec, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 10): + if 'network_metadata' in primitive: + del primitive['network_metadata'] if target_version < (1, 9): if 'user_id' in primitive: del primitive['user_id'] @@ -104,6 +110,11 @@ class RequestSpec(base.NovaObject): self.security_groups = objects.SecurityGroupList(objects=[]) return + if attrname == 'network_metadata': + self.network_metadata = objects.NetworkMetadata( + physnets=set(), tunneled=False) + return + # NOTE(sbauza): In case the primitive was not providing that field # because of a previous RequestSpec version, we want to default # that field in order to have the same behaviour. @@ -525,6 +536,10 @@ class RequestSpec(base.NovaObject): # NOTE(mriedem): Don't persist retries since those are per-request if 'retry' in spec and spec.retry: spec.retry = None + # NOTE(stephenfin): Don't persist network metadata since we have + # no need for it after scheduling + if 'network_metadata' in spec and spec.network_metadata: + del spec.network_metadata db_updates = {'spec': jsonutils.dumps(spec.obj_to_primitive())} if 'instance_uuid' in updates: diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index a58ab042c7e9..cf59ab9b97d0 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1142,7 +1142,7 @@ object_data = { 'PowerVMLiveMigrateData': '1.3-79c635ecf61d1d70b5b9fa04bf778a91', 'Quotas': '1.3-40fcefe522111dddd3e5e6155702cf4e', 'QuotasNoOp': '1.3-347a039fc7cfee7b225b68b5181e0733', - 'RequestSpec': '1.9-e506ccb22cd7807a1207c22a3f179387', + 'RequestSpec': '1.10-afe714bc445ab7cb791150a775f3b779', 'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e', 'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641', 'SchedulerRetries': '1.1-3c9c8b16143ebbb6ad7030e999d14cc0', diff --git a/nova/tests/unit/objects/test_request_spec.py b/nova/tests/unit/objects/test_request_spec.py index 2ae92adbe03a..4d4f09b24def 100644 --- a/nova/tests/unit/objects/test_request_spec.py +++ b/nova/tests/unit/objects/test_request_spec.py @@ -299,7 +299,7 @@ class _TestRequestSpecObject(object): spec = objects.RequestSpec.from_primitives(ctxt, spec_dict, filt_props) mock_limits.assert_called_once_with({}) # Make sure that all fields are set using that helper method - skip = ['id', 'security_groups'] + skip = ['id', 'security_groups', 'network_metadata'] for field in [f for f in spec.obj_fields if f not in skip]: self.assertTrue(spec.obj_attr_is_set(field), 'Field: %s is not set' % field) @@ -329,7 +329,8 @@ class _TestRequestSpecObject(object): filter_properties, instance_group, instance.availability_zone, objects.SecurityGroupList()) # Make sure that all fields are set using that helper method - for field in [f for f in spec.obj_fields if f != 'id']: + skip = ['id', 'network_metadata'] + for field in [f for f in spec.obj_fields if f not in skip]: self.assertTrue(spec.obj_attr_is_set(field), 'Field: %s is not set' % field) # just making sure that the context is set by the method @@ -542,7 +543,7 @@ class _TestRequestSpecObject(object): # object fields for field in ['image', 'numa_topology', 'pci_requests', 'flavor', - 'limits']: + 'limits', 'network_metadata']: self.assertEqual( getattr(req_obj, field).obj_to_primitive(), getattr(serialized_obj, field).obj_to_primitive()) @@ -641,6 +642,18 @@ class _TestRequestSpecObject(object): self.assertNotIn('user_id', primitive) self.assertIn('project_id', primitive) + def test_compat_network_metadata(self): + network_metadata = objects.NetworkMetadata(physnets=set(), + tunneled=False) + req_obj = objects.RequestSpec(network_metadata=network_metadata, + user_id=fakes.FAKE_USER_ID) + versions = ovo_base.obj_tree_get_versions('RequestSpec') + primitive = req_obj.obj_to_primitive(target_version='1.9', + version_manifest=versions) + primitive = primitive['nova_object.data'] + self.assertNotIn('network_metadata', primitive) + self.assertIn('user_id', primitive) + def test_default_requested_destination(self): req_obj = objects.RequestSpec() self.assertIsNone(req_obj.requested_destination) @@ -652,6 +665,13 @@ class _TestRequestSpecObject(object): objects.SecurityGroupList) self.assertIn('security_groups', req_obj) + def test_network_requests_load(self): + req_obj = objects.RequestSpec() + self.assertNotIn('network_metadata', req_obj) + self.assertIsInstance(req_obj.network_metadata, + objects.NetworkMetadata) + self.assertIn('network_metadata', req_obj) + def test_destination_aggregates_default(self): destination = objects.Destination() self.assertIsNone(destination.aggregates)