Read port resource request from Neutron
This patch collects the resource requests from each neutron port involved in a server create request. Converts each request to a RequestGroup object. blueprint bandwidth-resource-provider Change-Id: I4473cb192447b5bfa9d1dfcc0dd5216c536caf73
This commit is contained in:
parent
a5d6833d77
commit
9ba910bb53
|
@ -809,8 +809,9 @@ class API(base.Base):
|
|||
# InstancePCIRequests object
|
||||
pci_request_info = pci_request.get_pci_requests_from_flavor(
|
||||
instance_type)
|
||||
network_metadata = self.network_api.create_resource_requests(
|
||||
result = self.network_api.create_resource_requests(
|
||||
context, requested_networks, pci_request_info)
|
||||
network_metadata, port_resource_requests = result
|
||||
|
||||
base_options = {
|
||||
'reservation_id': reservation_id,
|
||||
|
|
|
@ -390,10 +390,22 @@ class API(base_api.NetworkAPI):
|
|||
pci_requests=None):
|
||||
"""Retrieve all information for the networks passed at the time of
|
||||
creating the server.
|
||||
|
||||
:param context: The request context.
|
||||
:param requested_networks: The networks requested for the server.
|
||||
:type requested_networks: nova.objects.NetworkRequestList
|
||||
:param pci_requests: The list of PCI requests to which additional PCI
|
||||
requests created here will be added.
|
||||
:type pci_requests: nova.objects.InstancePCIRequests
|
||||
|
||||
:returns: A tuple with an instance of ``objects.NetworkMetadata`` for
|
||||
use by the scheduler or None and a list of RequestGroup
|
||||
objects representing the resource needs of each requested
|
||||
port
|
||||
"""
|
||||
# This is NOOP for Nova network since it doesn't support SR-IOV or
|
||||
# NUMA-aware vSwitch functionality.
|
||||
pass
|
||||
return None, []
|
||||
|
||||
def get_dns_domains(self, context):
|
||||
"""Returns a list of available dns domains.
|
||||
|
|
|
@ -273,19 +273,21 @@ class NetworkAPI(base.Base):
|
|||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_resource_requests(self, context, requested_networks):
|
||||
def create_resource_requests(self, context, requested_networks,
|
||||
pci_requests=None):
|
||||
"""Retrieve all information for the networks passed at the time of
|
||||
creating the server.
|
||||
|
||||
:param context: The request context.
|
||||
:param requested_networks: The networks requested for the server.
|
||||
:type requested_networks: nova.objects.RequestedNetworkList
|
||||
:type requested_networks: nova.objects.NetworkRequestList
|
||||
:param pci_requests: The list of PCI requests to which additional PCI
|
||||
requests created here will be added.
|
||||
requests created here will be added.
|
||||
:type pci_requests: nova.objects.InstancePCIRequests
|
||||
|
||||
:returns: An instance of ``objects.NetworkMetadata`` for use by the
|
||||
scheduler or None.
|
||||
:returns: A tuple with an instance of ``objects.NetworkMetadata`` for
|
||||
use by the scheduler or None and a list of RequestGroup
|
||||
objects representing the resource needs of each request ports
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
|
|
@ -1876,13 +1876,13 @@ class API(base_api.NetworkAPI):
|
|||
:param neutron: The Neutron client
|
||||
:param port_id: The id of port to be queried
|
||||
|
||||
:return: A tuple of vNIC type, trusted status and network ID. Trusted
|
||||
status only affects SR-IOV ports and will always be None for other
|
||||
port types.
|
||||
:return: A tuple of vNIC type, trusted status, network ID and resource
|
||||
request of the port if any. Trusted status only affects SR-IOV
|
||||
ports and will always be None for other port types.
|
||||
"""
|
||||
port = self._show_port(context, port_id, neutron_client=neutron,
|
||||
fields=['binding:vnic_type', BINDING_PROFILE,
|
||||
'network_id'])
|
||||
'network_id', 'resource_request'])
|
||||
network_id = port.get('network_id')
|
||||
trusted = None
|
||||
vnic_type = port.get('binding:vnic_type',
|
||||
|
@ -1890,7 +1890,12 @@ class API(base_api.NetworkAPI):
|
|||
if vnic_type in network_model.VNIC_TYPES_SRIOV:
|
||||
trusted = self._get_trusted_mode_from_port(port)
|
||||
|
||||
return vnic_type, trusted, network_id
|
||||
# NOTE(gibi): Get the port resource_request which may or may not be
|
||||
# set depending on neutron configuration, e.g. if QoS rules are
|
||||
# applied to the port/network and the resource_request API extension is
|
||||
# enabled.
|
||||
resource_request = port.get('resource_request', None)
|
||||
return vnic_type, trusted, network_id, resource_request
|
||||
|
||||
def create_resource_requests(self, context, requested_networks,
|
||||
pci_requests=None):
|
||||
|
@ -1899,21 +1904,25 @@ class API(base_api.NetworkAPI):
|
|||
|
||||
:param context: The request context.
|
||||
:param requested_networks: The networks requested for the server.
|
||||
:type requested_networks: nova.objects.RequestedNetworkList
|
||||
:type requested_networks: nova.objects.NetworkRequestList
|
||||
:param pci_requests: The list of PCI requests to which additional PCI
|
||||
requests created here will be added.
|
||||
:type pci_requests: nova.objects.InstancePCIRequests
|
||||
|
||||
:returns: An instance of ``objects.NetworkMetadata`` for use by the
|
||||
scheduler or None.
|
||||
:returns: A tuple with an instance of ``objects.NetworkMetadata`` for
|
||||
use by the scheduler or None and a list of RequestGroup
|
||||
objects representing the resource needs of each requested
|
||||
port
|
||||
"""
|
||||
if not requested_networks or requested_networks.no_allocate:
|
||||
return None
|
||||
return None, []
|
||||
|
||||
physnets = set()
|
||||
tunneled = False
|
||||
|
||||
neutron = get_client(context, admin=True)
|
||||
resource_requests = []
|
||||
|
||||
for request_net in requested_networks:
|
||||
physnet = None
|
||||
trusted = None
|
||||
|
@ -1922,10 +1931,20 @@ class API(base_api.NetworkAPI):
|
|||
pci_request_id = None
|
||||
|
||||
if request_net.port_id:
|
||||
vnic_type, trusted, network_id = self._get_port_vnic_info(
|
||||
result = self._get_port_vnic_info(
|
||||
context, neutron, request_net.port_id)
|
||||
vnic_type, trusted, network_id, resource_request = result
|
||||
physnet, tunneled_ = self._get_physnet_tunneled_info(
|
||||
context, neutron, network_id)
|
||||
|
||||
if resource_request:
|
||||
# NOTE(gibi): explicitly orphan the RequestGroup as we
|
||||
# never intended to save it to the DB.
|
||||
resource_requests.append(
|
||||
objects.RequestGroup.from_port_request(
|
||||
context=None,
|
||||
port_resource_request=resource_request))
|
||||
|
||||
elif request_net.network_id and not request_net.auto_allocate:
|
||||
network_id = request_net.network_id
|
||||
physnet, tunneled_ = self._get_physnet_tunneled_info(
|
||||
|
@ -1969,7 +1988,8 @@ class API(base_api.NetworkAPI):
|
|||
# Add pci_request_id into the requested network
|
||||
request_net.pci_request_id = pci_request_id
|
||||
|
||||
return objects.NetworkMetadata(physnets=physnets, tunneled=tunneled)
|
||||
return (objects.NetworkMetadata(physnets=physnets, tunneled=tunneled),
|
||||
resource_requests)
|
||||
|
||||
def _can_auto_allocate_network(self, context, neutron):
|
||||
"""Helper method to determine if we can auto-allocate networks
|
||||
|
|
|
@ -203,7 +203,7 @@ def stub_out_nw_api(test, cls=None, private=None, publics=None):
|
|||
|
||||
def create_resource_requests(self, context, requested_networks,
|
||||
pci_requests):
|
||||
pass
|
||||
return None, []
|
||||
|
||||
if cls is None:
|
||||
cls = Fake
|
||||
|
|
|
@ -219,7 +219,8 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||
port_id=port)])
|
||||
|
||||
with mock.patch.object(self.compute_api.network_api,
|
||||
'create_resource_requests'):
|
||||
'create_resource_requests',
|
||||
return_value=(None, [])):
|
||||
self.compute_api.create(self.context, instance_type, 'image_id',
|
||||
requested_networks=requested_networks,
|
||||
max_count=None)
|
||||
|
|
|
@ -3015,7 +3015,8 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
|
||||
def _test_get_port_vnic_info(self, mock_get_client,
|
||||
binding_vnic_type,
|
||||
expected_vnic_type):
|
||||
expected_vnic_type,
|
||||
port_resource_request=None):
|
||||
api = neutronapi.API()
|
||||
self.mox.ResetAll()
|
||||
test_port = {
|
||||
|
@ -3026,19 +3027,24 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
|
||||
if binding_vnic_type:
|
||||
test_port['port']['binding:vnic_type'] = binding_vnic_type
|
||||
if port_resource_request:
|
||||
test_port['port']['resource_request'] = port_resource_request
|
||||
|
||||
mock_get_client.reset_mock()
|
||||
mock_client = mock_get_client()
|
||||
mock_client.show_port.return_value = test_port
|
||||
|
||||
vnic_type, trusted, network_id = api._get_port_vnic_info(
|
||||
result = api._get_port_vnic_info(
|
||||
self.context, mock_client, test_port['port']['id'])
|
||||
vnic_type, trusted, network_id, resource_request = result
|
||||
|
||||
mock_client.show_port.assert_called_once_with(test_port['port']['id'],
|
||||
fields=['binding:vnic_type', 'binding:profile', 'network_id'])
|
||||
fields=['binding:vnic_type', 'binding:profile', 'network_id',
|
||||
'resource_request'])
|
||||
self.assertEqual(expected_vnic_type, vnic_type)
|
||||
self.assertEqual('net-id', network_id)
|
||||
self.assertIsNone(trusted)
|
||||
self.assertEqual(port_resource_request, resource_request)
|
||||
|
||||
@mock.patch.object(neutronapi, 'get_client', return_value=mock.MagicMock())
|
||||
def test_get_port_vnic_info_1(self, mock_get_client):
|
||||
|
@ -3055,6 +3061,22 @@ class TestNeutronv2(TestNeutronv2Base):
|
|||
self._test_get_port_vnic_info(mock_get_client, None,
|
||||
model.VNIC_TYPE_NORMAL)
|
||||
|
||||
@mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
|
||||
def test_get_port_vnic_info_requested_resources(self, mock_get_client):
|
||||
self._test_get_port_vnic_info(
|
||||
mock_get_client, None, model.VNIC_TYPE_NORMAL,
|
||||
port_resource_request={
|
||||
"resources": {
|
||||
"NET_BW_EGR_KILOBIT_PER_SEC": 6000,
|
||||
"NET_BW_IGR_KILOBIT_PER_SEC": 6000,
|
||||
},
|
||||
"required": [
|
||||
"CUSTOM_PHYSNET_PHYSNET0",
|
||||
"CUSTOM_VNIC_TYPE_NORMAL"
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class TestNeutronv2WithMock(_TestNeutronv2Common):
|
||||
"""Used to test Neutron V2 API with mock."""
|
||||
|
@ -3401,14 +3423,17 @@ class TestNeutronv2WithMock(_TestNeutronv2Common):
|
|||
mock_client = mock_get_client()
|
||||
mock_client.show_port.return_value = test_port
|
||||
mock_client.list_extensions.return_value = test_ext_list
|
||||
vnic_type, trusted, network_id = self.api._get_port_vnic_info(
|
||||
result = self.api._get_port_vnic_info(
|
||||
self.context, mock_client, test_port['port']['id'])
|
||||
vnic_type, trusted, network_id, resource_requests = result
|
||||
|
||||
mock_client.show_port.assert_called_once_with(test_port['port']['id'],
|
||||
fields=['binding:vnic_type', 'binding:profile', 'network_id'])
|
||||
fields=['binding:vnic_type', 'binding:profile', 'network_id',
|
||||
'resource_request'])
|
||||
self.assertEqual(model.VNIC_TYPE_DIRECT, vnic_type)
|
||||
self.assertEqual('net-id', network_id)
|
||||
self.assertTrue(trusted)
|
||||
self.assertIsNone(resource_requests)
|
||||
|
||||
@mock.patch('nova.network.neutronv2.api.API._show_port')
|
||||
def test_deferred_ip_port_immediate_allocation(self, mock_show):
|
||||
|
@ -5154,11 +5179,13 @@ class TestNeutronv2WithMock(_TestNeutronv2Common):
|
|||
pci_requests = objects.InstancePCIRequests()
|
||||
api = neutronapi.API()
|
||||
|
||||
network_metadata = api.create_resource_requests(
|
||||
result = api.create_resource_requests(
|
||||
self.context, requested_networks, pci_requests)
|
||||
network_metadata, port_resource_requests = result
|
||||
|
||||
self.assertFalse(mock_get_client.called)
|
||||
self.assertIsNone(network_metadata)
|
||||
self.assertEqual([], port_resource_requests)
|
||||
|
||||
@mock.patch.object(neutronapi.API, '_get_physnet_tunneled_info')
|
||||
@mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
|
||||
|
@ -5174,18 +5201,22 @@ class TestNeutronv2WithMock(_TestNeutronv2Common):
|
|||
pci_requests = objects.InstancePCIRequests()
|
||||
api = neutronapi.API()
|
||||
|
||||
network_metadata = api.create_resource_requests(
|
||||
result = api.create_resource_requests(
|
||||
self.context, requested_networks, pci_requests)
|
||||
network_metadata, port_resource_requests = result
|
||||
|
||||
mock_get_physnet_tunneled_info.assert_not_called()
|
||||
self.assertEqual(set(), network_metadata.physnets)
|
||||
self.assertFalse(network_metadata.tunneled)
|
||||
self.assertEqual([], port_resource_requests)
|
||||
|
||||
@mock.patch('nova.objects.request_spec.RequestGroup.from_port_request')
|
||||
@mock.patch.object(neutronapi.API, '_get_physnet_tunneled_info')
|
||||
@mock.patch.object(neutronapi.API, "_get_port_vnic_info")
|
||||
@mock.patch.object(neutronapi, 'get_client')
|
||||
def test_create_resource_requests(self, getclient,
|
||||
mock_get_port_vnic_info, mock_get_physnet_tunneled_info):
|
||||
mock_get_port_vnic_info, mock_get_physnet_tunneled_info,
|
||||
mock_request_spec):
|
||||
requested_networks = objects.NetworkRequestList(
|
||||
objects = [
|
||||
objects.NetworkRequest(port_id=uuids.portid_1),
|
||||
|
@ -5199,12 +5230,14 @@ class TestNeutronv2WithMock(_TestNeutronv2Common):
|
|||
# _get_port_vnic_info should be called for every NetworkRequest with a
|
||||
# port_id attribute (so six times)
|
||||
mock_get_port_vnic_info.side_effect = [
|
||||
(model.VNIC_TYPE_DIRECT, None, 'netN'),
|
||||
(model.VNIC_TYPE_NORMAL, None, 'netN'),
|
||||
(model.VNIC_TYPE_MACVTAP, None, 'netN'),
|
||||
(model.VNIC_TYPE_MACVTAP, None, 'netN'),
|
||||
(model.VNIC_TYPE_DIRECT_PHYSICAL, None, 'netN'),
|
||||
(model.VNIC_TYPE_DIRECT, True, 'netN'),
|
||||
(model.VNIC_TYPE_DIRECT, None, 'netN', None),
|
||||
(model.VNIC_TYPE_NORMAL, None, 'netN',
|
||||
mock.sentinel.resource_request1),
|
||||
(model.VNIC_TYPE_MACVTAP, None, 'netN', None),
|
||||
(model.VNIC_TYPE_MACVTAP, None, 'netN', None),
|
||||
(model.VNIC_TYPE_DIRECT_PHYSICAL, None, 'netN', None),
|
||||
(model.VNIC_TYPE_DIRECT, True, 'netN',
|
||||
mock.sentinel.resource_request2),
|
||||
]
|
||||
# _get_physnet_tunneled_info should be called for every NetworkRequest
|
||||
# (so seven times)
|
||||
|
@ -5215,9 +5248,19 @@ class TestNeutronv2WithMock(_TestNeutronv2Common):
|
|||
]
|
||||
api = neutronapi.API()
|
||||
|
||||
network_metadata = api.create_resource_requests(
|
||||
self.context, requested_networks, pci_requests)
|
||||
mock_request_spec.side_effect = [
|
||||
mock.sentinel.request_group1,
|
||||
mock.sentinel.request_group2,
|
||||
]
|
||||
|
||||
result = api.create_resource_requests(
|
||||
self.context, requested_networks, pci_requests)
|
||||
network_metadata, port_resource_requests = result
|
||||
|
||||
self.assertEqual([
|
||||
mock.sentinel.request_group1,
|
||||
mock.sentinel.request_group2],
|
||||
port_resource_requests)
|
||||
self.assertEqual(5, len(pci_requests.requests))
|
||||
has_pci_request_id = [net.pci_request_id is not None for net in
|
||||
requested_networks.objects]
|
||||
|
@ -5239,6 +5282,14 @@ class TestNeutronv2WithMock(_TestNeutronv2Common):
|
|||
['physnet1', 'physnet2', 'physnet3', 'physnet4'],
|
||||
network_metadata.physnets)
|
||||
self.assertTrue(network_metadata.tunneled)
|
||||
mock_request_spec.assert_has_calls([
|
||||
mock.call(
|
||||
context=None,
|
||||
port_resource_request=mock.sentinel.resource_request1),
|
||||
mock.call(
|
||||
context=None,
|
||||
port_resource_request=mock.sentinel.resource_request2),
|
||||
])
|
||||
|
||||
@mock.patch.object(neutronapi, 'get_client')
|
||||
def test_associate_floating_ip_conflict(self, mock_get_client):
|
||||
|
|
Loading…
Reference in New Issue