diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 9fc8f0fb5d32..16250a71d2e7 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 8b6c8e85d53e..439ce4b599d0 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 e6e4c939488f..6669b1e98b51 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 87f934fa09a2..cea34a500081 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 b03ddf8b3a50..4d9a599146b2 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 8ce182a9fd09..8a4de4f0926d 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)