diff --git a/nova/objects/request_spec.py b/nova/objects/request_spec.py index 29998ebe4d53..e6e4c939488f 100644 --- a/nova/objects/request_spec.py +++ b/nova/objects/request_spec.py @@ -816,3 +816,35 @@ class RequestGroup(base.NovaObject): def __init__(self, context=None, **kwargs): super(RequestGroup, self).__init__(context=context, **kwargs) self.obj_set_defaults() + + @classmethod + def from_port_request(cls, context, port_resource_request): + """Init the group from the resource request of a neutron port + + :param context: the request context + :param port_resource_request: the resource_request attribute of the + neutron port + For example: + + port_resource_request = { + "resources": { + "NET_BW_IGR_KILOBIT_PER_SEC": 1000, + "NET_BW_EGR_KILOBIT_PER_SEC": 1000}, + "required": ["CUSTOM_PHYSNET_2", + "CUSTOM_VNIC_TYPE_NORMAL"] + } + """ + + # NOTE(gibi): Assumptions: + # * a port requests resource from a single provider. + # * a port only specifies resources and required traits + # NOTE(gibi): Placement rejects allocation candidates where a request + # group has traits but no resources specified. This is why resources + # are handled as mandatory below but not traits. + obj = cls(context=context, + use_same_provider=True, + resources=port_resource_request['resources'], + required_traits=set(port_resource_request.get( + 'required', []))) + obj.obj_set_defaults() + return obj diff --git a/nova/tests/unit/objects/test_request_spec.py b/nova/tests/unit/objects/test_request_spec.py index 582174868488..8ce182a9fd09 100644 --- a/nova/tests/unit/objects/test_request_spec.py +++ b/nova/tests/unit/objects/test_request_spec.py @@ -854,3 +854,43 @@ class TestRequestGroupObject(test.TestCase): self.assertEqual(set(), rg.required_traits) self.assertEqual(set(), rg.forbidden_traits) self.assertEqual([], rg.aggregates) + + def test_from_port_request(self): + port_resource_request = { + "resources": { + "NET_BW_IGR_KILOBIT_PER_SEC": 1000, + "NET_BW_EGR_KILOBIT_PER_SEC": 1000}, + "required": ["CUSTOM_PHYSNET_2", + "CUSTOM_VNIC_TYPE_NORMAL"] + } + rg = request_spec.RequestGroup.from_port_request( + self.context, 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({"CUSTOM_PHYSNET_2", "CUSTOM_VNIC_TYPE_NORMAL"}, + rg.required_traits) + # and the rest is defaulted + self.assertEqual(set(), rg.forbidden_traits) + self.assertEqual([], rg.aggregates) + + def test_from_port_request_without_traits(self): + port_resource_request = { + "resources": { + "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.assertTrue(rg.use_same_provider) + self.assertEqual( + {"NET_BW_IGR_KILOBIT_PER_SEC": 1000, + "NET_BW_EGR_KILOBIT_PER_SEC": 1000}, + rg.resources) + # and the rest is defaulted + self.assertEqual(set(), rg.required_traits) + self.assertEqual(set(), rg.forbidden_traits) + self.assertEqual([], rg.aggregates)