diff --git a/nova/objects/request_spec.py b/nova/objects/request_spec.py index 7d57903078..a3efbd0bf5 100644 --- a/nova/objects/request_spec.py +++ b/nova/objects/request_spec.py @@ -1293,16 +1293,55 @@ class RequestLevelParams(base.NovaObject): RequestGroup). """ # Version 1.0: Initial version - VERSION = '1.0' + # Version 1.1: Add same_subtree field + VERSION = '1.1' fields = { # Traits required on the root provider 'root_required': fields.SetOfStringsField(default=set()), # Traits forbidden on the root provider 'root_forbidden': fields.SetOfStringsField(default=set()), + # Lists of request group suffixes that needs to be allocated from the + # same provider subtree + 'same_subtree': fields.ListOfListsOfStringsField(default=list()), # NOTE(efried): group_policy would be appropriate to include here, once # we have a use case for it. } def obj_load_attr(self, attrname): self.obj_set_defaults(attrname) + + def obj_make_compatible(self, primitive, target_version): + super().obj_make_compatible(primitive, target_version) + + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + if 'same_subtree' in primitive: + del primitive['same_subtree'] + + @classmethod + def from_port_request(cls, port_resource_request): + """Extracts request level global parameters from the resource_request + of a neutron port. + + Neutron only uses same_subtree at the moment. + """ + same_subtree = port_resource_request.get('same_subtree') + if same_subtree: + # NOTE(gibi): A single port only has a single list of groups + # requesting same subtree, but the RequestLevelParams maintains a + # list of such subtree requests + return cls(same_subtree=[same_subtree]) + + return cls() + + def extend_with(self, other_req_lvl_params): + """Extends the existing object with parameter values from another + RequestLevelParams object. That new set of requirements are connected + with the existing ones with a logical AND operation. + """ + self.root_required = self.root_required.union( + other_req_lvl_params.root_required) + self.root_forbidden = self.root_forbidden.union( + other_req_lvl_params.root_forbidden) + self.same_subtree.extend(other_req_lvl_params.same_subtree) diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index bb7e879224..4e16baeaa1 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1123,7 +1123,7 @@ object_data = { 'Quotas': '1.3-3b2b91371f60e788035778fc5f87797d', 'QuotasNoOp': '1.3-d1593cf969c81846bc8192255ea95cce', 'RequestGroup': '1.3-0458d350a8ec9d0673f9be5640a990ce', - 'RequestLevelParams': '1.0-1e5c8c18bd44cd233c8b32509c99d06f', + 'RequestLevelParams': '1.1-3a718a0ae0bfdec669e7108631b3f313', 'RequestSpec': '1.14-2cdbda368ca07e10905dc5fe96908a58', 'Resource': '1.0-d8a2abbb380da583b995fd118f6a8953', 'ResourceList': '1.0-4a53826625cc280e15fae64a575e0879', diff --git a/nova/tests/unit/objects/test_request_spec.py b/nova/tests/unit/objects/test_request_spec.py index 7edc84bf62..2963909c32 100644 --- a/nova/tests/unit/objects/test_request_spec.py +++ b/nova/tests/unit/objects/test_request_spec.py @@ -1692,3 +1692,54 @@ class TestMappingRequestGroupsToProviders(test.NoDBTestCase): allocations, provider_traits) self.assertIn('allocations leftover', mock_debug.mock_calls[3][1][0]) + + +class TestRequestLevelParams(test.NoDBTestCase): + def setUp(self): + super().setUp() + self.user_id = uuids.user_id + self.project_id = uuids.project_id + self.context = context.RequestContext(uuids.user_id, uuids.project_id) + + def test_obj_make_compatible(self): + obj = request_spec.RequestLevelParams( + self.context, + root_required={"CUSTOM_FOO", "CUSTOM_BAR"}, + root_forbidden={"CUSTOM_BAZ"}, + same_subtree=[["group1", "group2"], ["group3", "group4"]]) + + manifest = ovo_base.obj_tree_get_versions(obj.obj_name()) + + obj_primitive = obj.obj_to_primitive( + target_version='1.1', + version_manifest=manifest)['nova_object.data'] + self.assertIn('root_required', obj_primitive) + self.assertIn('root_forbidden', obj_primitive) + self.assertIn('same_subtree', obj_primitive) + + obj_primitive = obj.obj_to_primitive( + target_version='1.0', + version_manifest=manifest)['nova_object.data'] + self.assertIn('root_required', obj_primitive) + self.assertIn('root_forbidden', obj_primitive) + self.assertNotIn('same_subtree', obj_primitive) + + def test_extend_with(self): + obj1 = request_spec.RequestLevelParams( + self.context, + root_required={"CUSTOM_FOO"}, + root_forbidden={"CUSTOM_BAZ"}, + same_subtree=[["group1", "group2"]]) + obj2 = request_spec.RequestLevelParams( + self.context, + root_required={"CUSTOM_BAR"}, + root_forbidden={"CUSTOM_FOOBAR"}, + same_subtree=[["group3", "group4"]]) + + obj1.extend_with(obj2) + + self.assertEqual({"CUSTOM_FOO", "CUSTOM_BAR"}, obj1.root_required) + self.assertEqual({"CUSTOM_BAZ", "CUSTOM_FOOBAR"}, obj1.root_forbidden) + self.assertEqual( + [["group1", "group2"], ["group3", "group4"]], + obj1.same_subtree)