From b2a995f19867bb2eedcb615d7f7431bdbef73c8e Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Tue, 20 Nov 2018 12:54:28 +0100 Subject: [PATCH] Extend RequestGroup object for mapping This patch adds two new fields to the RequestGroup ovo, requester_id and provider_uuids. These two fields are needed to be able to hold and communicate the mapping between the requester of the RequestGroup (e.g. Neutron port) and the resource providers that are fulfilling the request (e.g. network device RPs). If the RequestGroup represents the un-numbered group then more than one RP can fulfill the request hence provider_uuids is a list. These new fields later in the series will be populated based on some logic in the nova-conductor. However in the long run we expect that these fields will be populated from the Placement allocation candidates response. blueprint bandwidth-resource-provider Change-Id: Ic4735f92542e5e0ca36b459874dc486f6b360317 --- nova/network/neutronv2/api.py | 1 + nova/objects/fields.py | 6 +++++ nova/objects/request_spec.py | 27 +++++++++++++++++--- nova/tests/unit/network/test_neutronv2.py | 2 ++ nova/tests/unit/objects/test_objects.py | 2 +- nova/tests/unit/objects/test_request_spec.py | 22 ++++++++++++++-- 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 9fc8f0fb5d..16250a71d2 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -1983,6 +1983,7 @@ class API(base_api.NetworkAPI): resource_requests.append( objects.RequestGroup.from_port_request( context=None, + port_uuid=request_net.port_id, port_resource_request=resource_request)) elif request_net.network_id and not request_net.auto_allocate: diff --git a/nova/objects/fields.py b/nova/objects/fields.py index 8b6c8e85d5..439ce4b599 100644 --- a/nova/objects/fields.py +++ b/nova/objects/fields.py @@ -1249,3 +1249,9 @@ class InstancePowerStateField(BaseEnumField): class ListOfListsOfStringsField(fields.AutoTypedField): AUTO_TYPE = List(List(fields.String())) + + +# TODO(mriedem): Replace this with the version from oslo.versiondobjects +# when https://review.openstack.org/#/c/634700/ is released. +class ListOfUUIDField(AutoTypedField): + AUTO_TYPE = List(fields.UUID()) diff --git a/nova/objects/request_spec.py b/nova/objects/request_spec.py index e6e4c93948..6669b1e98b 100644 --- a/nova/objects/request_spec.py +++ b/nova/objects/request_spec.py @@ -797,7 +797,9 @@ class RequestGroup(base.NovaObject): """Versioned object based on the unversioned nova.api.openstack.placement.lib.RequestGroup object. """ - VERSION = '1.0' + # Version 1.0: Initial version + # Version 1.1: add requester_id and provider_uuids fields + VERSION = '1.1' fields = { 'use_same_provider': fields.BooleanField(default=True), @@ -811,6 +813,13 @@ class RequestGroup(base.NovaObject): # member of the aggregate aggregate_UUID1 and member of the aggregate # aggregate_UUID2 or aggregate_UUID3 . 'aggregates': fields.ListOfListsOfStringsField(default=[]), + # The entity the request is coming from (e.g. the Neutron port uuid) + # which may not always be a UUID. + 'requester_id': fields.StringField(nullable=True, default=None), + # The resource provider UUIDs that together fulfill the request + # NOTE(gibi): this can be more than one if this is the unnumbered + # request group (i.e. use_same_provider=False) + 'provider_uuids': fields.ListOfUUIDField(default=[]), } def __init__(self, context=None, **kwargs): @@ -818,10 +827,11 @@ class RequestGroup(base.NovaObject): self.obj_set_defaults() @classmethod - def from_port_request(cls, context, port_resource_request): + def from_port_request(cls, context, port_uuid, port_resource_request): """Init the group from the resource request of a neutron port :param context: the request context + :param port_uuid: the port requesting the resources :param port_resource_request: the resource_request attribute of the neutron port For example: @@ -845,6 +855,17 @@ class RequestGroup(base.NovaObject): use_same_provider=True, resources=port_resource_request['resources'], required_traits=set(port_resource_request.get( - 'required', []))) + 'required', [])), + requester_id=port_uuid) obj.obj_set_defaults() return obj + + def obj_make_compatible(self, primitive, target_version): + super(RequestGroup, self).obj_make_compatible( + primitive, target_version) + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + if 'requester_id' in primitive: + del primitive['requester_id'] + if 'provider_uuids' in primitive: + del primitive['provider_uuids'] diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index 87f934fa09..cea34a5000 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -5325,9 +5325,11 @@ class TestNeutronv2WithMock(_TestNeutronv2Common): mock_request_spec.assert_has_calls([ mock.call( context=None, + port_uuid=uuids.portid_2, port_resource_request=mock.sentinel.resource_request1), mock.call( context=None, + port_uuid=uuids.trusted_port, port_resource_request=mock.sentinel.resource_request2), ]) diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index b03ddf8b3a..4d9a599146 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.4-a745f4eda16b45e1bc5686a0c498f27e', 'Quotas': '1.3-40fcefe522111dddd3e5e6155702cf4e', 'QuotasNoOp': '1.3-347a039fc7cfee7b225b68b5181e0733', - 'RequestGroup': '1.0-5f694d4237c00c7b01136a4e4bcacd6d', + 'RequestGroup': '1.1-5a330f65df2d91356b1da19f10540ec8', 'RequestSpec': '1.12-25010470f219af9b6163f2a457a513f5', 'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e', 'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641', diff --git a/nova/tests/unit/objects/test_request_spec.py b/nova/tests/unit/objects/test_request_spec.py index 8ce182a9fd..8a4de4f092 100644 --- a/nova/tests/unit/objects/test_request_spec.py +++ b/nova/tests/unit/objects/test_request_spec.py @@ -854,6 +854,8 @@ class TestRequestGroupObject(test.TestCase): self.assertEqual(set(), rg.required_traits) self.assertEqual(set(), rg.forbidden_traits) self.assertEqual([], rg.aggregates) + self.assertIsNone(None, rg.requester_id) + self.assertEqual([], rg.provider_uuids) def test_from_port_request(self): port_resource_request = { @@ -864,7 +866,7 @@ class TestRequestGroupObject(test.TestCase): "CUSTOM_VNIC_TYPE_NORMAL"] } rg = request_spec.RequestGroup.from_port_request( - self.context, port_resource_request) + self.context, uuids.port_id, port_resource_request) self.assertTrue(rg.use_same_provider) self.assertEqual( @@ -873,9 +875,11 @@ class TestRequestGroupObject(test.TestCase): rg.resources) self.assertEqual({"CUSTOM_PHYSNET_2", "CUSTOM_VNIC_TYPE_NORMAL"}, rg.required_traits) + self.assertEqual(uuids.port_id, rg.requester_id) # and the rest is defaulted self.assertEqual(set(), rg.forbidden_traits) self.assertEqual([], rg.aggregates) + self.assertEqual([], rg.provider_uuids) def test_from_port_request_without_traits(self): port_resource_request = { @@ -883,14 +887,28 @@ class TestRequestGroupObject(test.TestCase): "NET_BW_IGR_KILOBIT_PER_SEC": 1000, "NET_BW_EGR_KILOBIT_PER_SEC": 1000}} rg = request_spec.RequestGroup.from_port_request( - self.context, port_resource_request) + self.context, uuids.port_id, port_resource_request) self.assertTrue(rg.use_same_provider) self.assertEqual( {"NET_BW_IGR_KILOBIT_PER_SEC": 1000, "NET_BW_EGR_KILOBIT_PER_SEC": 1000}, rg.resources) + self.assertEqual(uuids.port_id, rg.requester_id) # and the rest is defaulted self.assertEqual(set(), rg.required_traits) self.assertEqual(set(), rg.forbidden_traits) self.assertEqual([], rg.aggregates) + self.assertEqual([], rg.provider_uuids) + + def test_compat_requester_and_provider(self): + req_obj = objects.RequestGroup( + requester_id=uuids.requester, provider_uuids=[uuids.rp1], + required_traits=set(['CUSTOM_PHYSNET_2'])) + versions = ovo_base.obj_tree_get_versions('RequestGroup') + primitive = req_obj.obj_to_primitive( + target_version='1.0', + version_manifest=versions)['nova_object.data'] + self.assertNotIn('requester_id', primitive) + self.assertNotIn('provider_uuids', primitive) + self.assertIn('required_traits', primitive)