Support SR-IOV networking in nova compute api and nova neutronv2

Add another field into the NetworkRequest object for the request_id.
For a SR-IOV port, Create a PCI request and add the pci-request-id into
the tuple. For a non SR-IOV port, the pci-request-id is None.
Add new fields 'profile' and 'vnic_type' in VIF.
Enhance nova neutronv2 api module to:
  -- retrieve a port's vnic-type and the physical network it is attached to
  -- populate binding:profile before updating a neutron port
  -- populate VIF with newly added fields.
Partially Implements: blueprint pci-passthrough-sriov
Change-Id: I4aab1cd43960d5b1b54a67a05c191f295dfc46fb
This commit is contained in:
Robert Li 2014-09-10 16:55:51 -04:00 committed by Dan Smith
parent 5a8ae0f1d0
commit a8a5d44c8a
18 changed files with 383 additions and 85 deletions

View File

@ -784,8 +784,16 @@ class API(base.Base):
system_metadata = flavors.save_flavor_info(
dict(), instance_type)
# PCI requests come from two sources: instance flavor and
# requested_networks. The first call in below returns an
# InstancePCIRequests object which is a list of InstancePCIRequest
# objects. The second call in below creates an InstancePCIRequest
# object for each SR-IOV port, and append it to the list in the
# InstancePCIRequests object
pci_request_info = pci_request.get_pci_requests_from_flavor(
instance_type)
self.network_api.create_pci_requests_for_sriov_ports(context,
pci_request_info, requested_networks)
base_options = {
'reservation_id': reservation_id,
@ -827,13 +835,15 @@ class API(base.Base):
return base_options, max_network_count
def _build_filter_properties(self, context, scheduler_hints, forced_host,
forced_node, instance_type):
forced_node, instance_type, pci_request_info):
filter_properties = dict(scheduler_hints=scheduler_hints)
filter_properties['instance_type'] = instance_type
if forced_host:
filter_properties['force_hosts'] = [forced_host]
if forced_node:
filter_properties['force_nodes'] = [forced_node]
if pci_request_info and pci_request_info.requests:
filter_properties['pci_requests'] = pci_request_info
return filter_properties
def _provision_instances(self, context, instance_type, min_count,
@ -1036,7 +1046,9 @@ class API(base.Base):
block_device_mapping, shutdown_terminate)
filter_properties = self._build_filter_properties(context,
scheduler_hints, forced_host, forced_node, instance_type)
scheduler_hints, forced_host,
forced_node, instance_type,
base_options.get('pci_request_info'))
self._update_instance_group(context, instances, scheduler_hints)

View File

@ -406,6 +406,17 @@ class API(base_api.NetworkAPI):
# the requested number in this case.
return num_instances
def create_pci_requests_for_sriov_ports(self, context,
pci_requests,
requested_networks):
"""Check requested networks for any SR-IOV port request.
Create a PCI request object for each SR-IOV port, and add it to the
pci_requests object that contains a list of PCI request object.
"""
# This is NOOP for Nova network since it doesn't support SR-IOV.
pass
@wrap_check_policy
def get_instance_uuids_by_ip_filter(self, context, filters):
"""Returns a list of dicts in the form of

View File

@ -237,6 +237,16 @@ class NetworkAPI(base.Base):
"""Returns all network info related to an instance."""
raise NotImplementedError()
def create_pci_requests_for_sriov_ports(self, context,
pci_requests,
requested_networks):
"""Check requested networks for any SR-IOV port request.
Create a PCI request object for each SR-IOV port, and add it to the
pci_requests object that contains a list of PCI request object.
"""
raise NotImplementedError()
def validate_networks(self, context, requested_networks, num_instances):
"""validate the networks passed at the time of creating
the server.

View File

@ -36,16 +36,29 @@ VIF_TYPE_IOVISOR = 'iovisor'
VIF_TYPE_BRIDGE = 'bridge'
VIF_TYPE_802_QBG = '802.1qbg'
VIF_TYPE_802_QBH = '802.1qbh'
VIF_TYPE_HW_VEB = 'hw_veb'
VIF_TYPE_MLNX_DIRECT = 'mlnx_direct'
VIF_TYPE_MIDONET = 'midonet'
VIF_TYPE_OTHER = 'other'
# Constants for dictionary keys in the 'vif_details' field in the VIF
# class
VIF_DETAIL_PORT_FILTER = 'port_filter'
VIF_DETAIL_OVS_HYBRID_PLUG = 'ovs_hybrid_plug'
VIF_DETAILS_PORT_FILTER = 'port_filter'
VIF_DETAILS_OVS_HYBRID_PLUG = 'ovs_hybrid_plug'
VIF_DETAILS_PHYSICAL_NETWORK = 'physical_network'
# The following two constants define the SR-IOV related fields in the
# 'vif_details'. 'profileid' should be used for VIF_TYPE_802_QBH,
# 'vlan' for VIF_TYPE_HW_VEB
VIF_DETAILS_PROFILEID = 'profileid'
VIF_DETAILS_VLAN = 'vlan'
# Define supported virtual NIC types. VNIC_TYPE_DIRECT and VNIC_TYPE_MACVTAP
# are used for SR-IOV ports
VNIC_TYPE_NORMAL = 'normal'
VNIC_TYPE_DIRECT = 'direct'
VNIC_TYPE_MACVTAP = 'macvtap'
# Constants for the 'vif_model' values
VIF_MODEL_VIRTIO = 'virtio'
VIF_MODEL_NE2K_PCI = 'ne2k_pci'
@ -273,6 +286,7 @@ class VIF(Model):
def __init__(self, id=None, address=None, network=None, type=None,
details=None, devname=None, ovs_interfaceid=None,
qbh_params=None, qbg_params=None, active=False,
vnic_type=VNIC_TYPE_NORMAL, profile=None,
**kwargs):
super(VIF, self).__init__()
@ -287,11 +301,14 @@ class VIF(Model):
self['qbh_params'] = qbh_params
self['qbg_params'] = qbg_params
self['active'] = active
self['vnic_type'] = vnic_type
self['profile'] = profile
self._set_meta(kwargs)
def __eq__(self, other):
keys = ['id', 'address', 'network', 'type', 'details', 'devname',
keys = ['id', 'address', 'network', 'vnic_type',
'type', 'profile', 'details', 'devname',
'ovs_interfaceid', 'qbh_params', 'qbg_params',
'active']
return all(self[k] == other[k] for k in keys)
@ -342,10 +359,10 @@ class VIF(Model):
return []
def is_hybrid_plug_enabled(self):
return self['details'].get(VIF_DETAIL_OVS_HYBRID_PLUG, False)
return self['details'].get(VIF_DETAILS_OVS_HYBRID_PLUG, False)
def is_neutron_filtering_enabled(self):
return self['details'].get(VIF_DETAIL_PORT_FILTER, False)
return self['details'].get(VIF_DETAILS_PORT_FILTER, False)
def get_physical_network(self):
phy_network = self['network']['meta'].get('physical_network')

View File

@ -16,6 +16,7 @@
#
import time
import uuid
from neutronclient.common import exceptions as neutron_client_exc
from oslo.config import cfg
@ -35,6 +36,9 @@ from nova import objects
from nova.openstack.common import excutils
from nova.openstack.common import log as logging
from nova.openstack.common import uuidutils
from nova.pci import pci_manager
from nova.pci import pci_request
from nova.pci import pci_whitelist
neutron_opts = [
cfg.StrOpt('url',
@ -310,7 +314,8 @@ class API(base_api.NetworkAPI):
# if this function is directly called without a requested_network param
# or if it is indirectly called through allocate_port_for_instance()
# with None params=(network_id=None, requested_ip=None, port_id=None):
# with None params=(network_id=None, requested_ip=None, port_id=None,
# pci_request_id=None):
if (not requested_networks
or requested_networks.is_single_unspecified):
# bug/1267723 - if no network is requested and more
@ -390,7 +395,9 @@ class API(base_api.NetworkAPI):
port_req_body = {'port': {'device_id': instance.uuid,
'device_owner': zone}}
try:
self._populate_neutron_extension_values(context, instance,
self._populate_neutron_extension_values(context,
instance,
request.pci_request_id,
port_req_body)
# Requires admin creds to set port bindings
port_client = (neutron if not
@ -461,8 +468,27 @@ class API(base_api.NetworkAPI):
self._refresh_neutron_extensions_cache(context)
return constants.PORTBINDING_EXT in self.extensions
@staticmethod
def _populate_neutron_binding_profile(instance, pci_request_id,
port_req_body):
"""Populate neutron binding:profile.
Populate it with SR-IOV related information
"""
if pci_request_id:
pci_dev = pci_manager.get_instance_pci_devs(
instance, pci_request_id).pop()
devspec = pci_whitelist.get_pci_device_devspec(pci_dev)
profile = {'pci_vendor_info': "%s:%s" % (pci_dev.vendor_id,
pci_dev.product_id),
'pci_slot': pci_dev.address,
'physical_network':
devspec.get_tags().get('physical_network')
}
port_req_body['port']['binding:profile'] = profile
def _populate_neutron_extension_values(self, context, instance,
port_req_body):
pci_request_id, port_req_body):
"""Populate neutron extension values for the instance.
If the extensions loaded contain QOS_QUEUE then pass the rxtx_factor.
@ -474,6 +500,9 @@ class API(base_api.NetworkAPI):
port_req_body['port']['rxtx_factor'] = rxtx_factor
if self._has_port_binding_extension(context):
port_req_body['port']['binding:host_id'] = instance.get('host')
self._populate_neutron_binding_profile(instance,
pci_request_id,
port_req_body)
def deallocate_for_instance(self, context, instance, **kwargs):
"""Deallocate all network resources related to the instance."""
@ -487,7 +516,8 @@ class API(base_api.NetworkAPI):
# NOTE(danms): Temporary and transitional
if isinstance(requested_networks, objects.NetworkRequestList):
requested_networks = requested_networks.as_tuples()
ports_to_skip = [port_id for nets, fips, port_id in requested_networks]
ports_to_skip = [port_id for nets, fips, port_id, pci_request_id
in requested_networks]
ports = set(ports) - set(ports_to_skip)
# Reset device_id and device_owner for the ports that are skipped
for port in ports_to_skip:
@ -523,7 +553,8 @@ class API(base_api.NetworkAPI):
requested_networks = objects.NetworkRequestList(
objects=[objects.NetworkRequest(network_id=network_id,
address=requested_ip,
port_id=port_id)])
port_id=port_id,
pci_request_id=None)])
return self.allocate_for_instance(context, instance,
requested_networks=requested_networks)
@ -670,6 +701,54 @@ class API(base_api.NetworkAPI):
raise exception.FixedIpNotFoundForSpecificInstance(
instance_uuid=instance['uuid'], ip=address)
def _get_port_vnic_info(self, context, neutron, port_id):
"""Retrieve port vnic info
Invoked with a valid port_id.
Return vnic type and the attached physical network name.
"""
phynet_name = None
vnic_type = None
port = neutron.show_port(port_id,
fields=['binding:vnic_type', 'network_id']).get('port')
vnic_type = port['binding:vnic_type']
if vnic_type != network_model.VNIC_TYPE_NORMAL:
net_id = port['network_id']
net = neutron.show_network(net_id,
fields='provider:physical_network').get('network')
phynet_name = net.get('provider:physical_network')
return vnic_type, phynet_name
def create_pci_requests_for_sriov_ports(self, context, pci_requests,
requested_networks):
"""Check requested networks for any SR-IOV port request.
Create a PCI request object for each SR-IOV port, and add it to the
pci_requests object that contains a list of PCI request object.
"""
if not requested_networks:
return
neutron = neutronv2.get_client(context, admin=True)
for request_net in requested_networks:
phynet_name = None
vnic_type = network_model.VNIC_TYPE_NORMAL
if request_net.port_id:
vnic_type, phynet_name = self._get_port_vnic_info(
context, neutron, request_net.port_id)
pci_request_id = None
if vnic_type != network_model.VNIC_TYPE_NORMAL:
request = objects.InstancePCIRequest(
count=1,
spec=[{pci_request.PCI_NET_TAG: phynet_name}],
request_id=str(uuid.uuid4()))
pci_requests.requests.append(request)
pci_request_id = request.request_id
# Add pci_request_id into the requested network
request_net.pci_request_id = pci_request_id
def validate_networks(self, context, requested_networks, num_instances):
"""Validate that the tenant can use the requested networks.
@ -1297,7 +1376,9 @@ class API(base_api.NetworkAPI):
id=current_neutron_port['id'],
address=current_neutron_port['mac_address'],
network=network,
vnic_type=current_neutron_port['binding:vnic_type'],
type=current_neutron_port.get('binding:vif_type'),
profile=current_neutron_port.get('binding:profile'),
details=current_neutron_port.get('binding:vif_details'),
ovs_interfaceid=ovs_interfaceid,
devname=devname,

View File

@ -19,11 +19,13 @@ from nova import utils
class NetworkRequest(obj_base.NovaObject):
# Version 1.0: Initial version
# Version 1.1: Added pci_request_id
VERSION = '1.0'
fields = {
'network_id': fields.StringField(nullable=True),
'address': fields.IPAddressField(nullable=True),
'port_id': fields.UUIDField(nullable=True),
'pci_request_id': fields.UUIDField(nullable=True),
}
def obj_load_attr(self, attr):
@ -32,15 +34,16 @@ class NetworkRequest(obj_base.NovaObject):
def to_tuple(self):
address = str(self.address) if self.address is not None else None
if utils.is_neutron():
return self.network_id, address, self.port_id
return self.network_id, address, self.port_id, self.pci_request_id
else:
return self.network_id, address
@classmethod
def from_tuple(cls, net_tuple):
if len(net_tuple) == 3:
network_id, address, port_id = net_tuple
return cls(network_id=network_id, address=address, port_id=port_id)
if len(net_tuple) == 4:
network_id, address, port_id, pci_request_id = net_tuple
return cls(network_id=network_id, address=address,
port_id=port_id, pci_request_id=pci_request_id)
else:
network_id, address = net_tuple
return cls(network_id=network_id, address=address)
@ -53,8 +56,9 @@ class NetworkRequestList(obj_base.ObjectListBase, obj_base.NovaObject):
child_versions = {
'1.0': '1.0',
'1.1': '1.1',
}
VERSION = '1.0'
VERSION = '1.1'
def as_tuples(self):
return [x.to_tuple() for x in self.objects]

View File

@ -66,6 +66,8 @@ pci_alias_opts = [
)
]
PCI_NET_TAG = 'physical_network'
CONF = cfg.CONF
CONF.register_opts(pci_alias_opts)

View File

@ -28,7 +28,6 @@ from nova import exception
from nova.i18n import _
from nova import objects
from nova.openstack.common import log as logging
from nova.pci import pci_request
from nova import rpc
from nova.scheduler import driver
from nova.scheduler import scheduler_options
@ -207,10 +206,6 @@ class FilterScheduler(driver.Scheduler):
os_type = request_spec['instance_properties']['os_type']
filter_properties['project_id'] = project_id
filter_properties['os_type'] = os_type
pci_requests = pci_request.get_pci_requests_from_flavor(
request_spec.get('instance_type') or {})
if pci_requests:
filter_properties['pci_requests'] = pci_requests
def _setup_instance_group(self, context, filter_properties):
update_group_hosts = False

View File

@ -27,6 +27,7 @@ from nova import compute
from nova import context
import nova.db
from nova import exception
from nova.network import model
from nova.network import neutronv2
from nova.network.neutronv2 import api as neutron_api
from nova.network.security_group import neutron_driver
@ -69,7 +70,7 @@ class TestNeutronSecurityGroups(
return net
def _create_port(self, **kwargs):
body = {'port': {}}
body = {'port': {'binding:vnic_type': model.VNIC_TYPE_NORMAL}}
fields = ['security_groups', 'device_id', 'network_id',
'port_security_enabled']
for field in fields:
@ -711,7 +712,9 @@ class MockClient(object):
'device_id': p.get('device_id', str(uuid.uuid4())),
'admin_state_up': p.get('admin_state_up', True),
'security_groups': p.get('security_groups', []),
'network_id': p.get('network_id')}
'network_id': p.get('network_id'),
'binding:vnic_type':
p.get('binding:vnic_type') or model.VNIC_TYPE_NORMAL}
network = self._fake_networks[p['network_id']]
if 'port_security_enabled' in p:

View File

@ -210,14 +210,14 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_requested_networks_neutronv2_enabled_with_network(self):
self.flags(network_api_class='nova.network.neutronv2.api.API')
network = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
requested_networks = [{'uuid': network}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(network, None, None)], res.as_tuples())
self.assertEqual([(network, None, None, None)], res.as_tuples())
def test_requested_networks_neutronv2_enabled_with_network_and_port(self):
self.flags(network_api_class='nova.network.neutronv2.api.API')
@ -225,7 +225,7 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'uuid': network, 'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_requested_networks_neutronv2_enabled_conflict_on_fixed_ip(self):
self.flags(network_api_class='nova.network.neutronv2.api.API')
@ -254,7 +254,7 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'uuid': network, 'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_requested_networks_neutronv2_subclass_with_port(self):
cls = 'nova.tests.api.openstack.compute.test_servers.NeutronV2Subclass'
@ -262,7 +262,7 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_get_server_by_uuid(self):
req = fakes.HTTPRequestV3.blank('/servers/%s' % FAKE_UUID)
@ -2167,7 +2167,7 @@ class ServersControllerCreateTest(test.TestCase):
def create(*args, **kwargs):
result = [('76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', None,
None)]
None, None)]
self.assertEqual(result, kwargs['requested_networks'].as_tuples())
return old_create(*args, **kwargs)

View File

@ -212,14 +212,14 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_requested_networks_neutronv2_enabled_with_network(self):
self.flags(network_api_class='nova.network.neutronv2.api.API')
network = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
requested_networks = [{'uuid': network}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(network, None, None)], res.as_tuples())
self.assertEqual([(network, None, None, None)], res.as_tuples())
def test_requested_networks_neutronv2_enabled_with_network_and_port(self):
self.flags(network_api_class='nova.network.neutronv2.api.API')
@ -227,7 +227,7 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'uuid': network, 'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_requested_networks_neutronv2_disabled_with_port(self):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
@ -243,7 +243,7 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'uuid': network, 'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_requested_networks_neutronv2_subclass_with_port(self):
cls = 'nova.tests.api.openstack.compute.test_servers.NeutronV2Subclass'
@ -251,7 +251,7 @@ class ServersControllerTest(ControllerTest):
port = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
requested_networks = [{'port': port}]
res = self.controller._get_requested_networks(requested_networks)
self.assertEqual([(None, None, port)], res.as_tuples())
self.assertEqual([(None, None, port, None)], res.as_tuples())
def test_get_server_by_uuid(self):
req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID)
@ -2854,7 +2854,7 @@ class ServersControllerCreateTest(test.TestCase):
def create(*args, **kwargs):
result = [('76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', None,
None)]
None, None)]
self.assertEqual(result, kwargs['requested_networks'].as_tuples())
return old_create(*args, **kwargs)

View File

@ -263,6 +263,11 @@ def stub_out_nw_api(stubs, cls=None, private=None, publics=None):
def validate_networks(self, context, networks, max_count):
return max_count
def create_pci_requests_for_sriov_ports(self, context,
system_metadata,
requested_networks):
pass
if cls is None:
cls = Fake
stubs.Set(network_api, 'API', cls)

View File

@ -230,7 +230,7 @@ class CellsConductorAPIRPCRedirect(test.NoDBTestCase):
def test_build_instances(self, _validate, _get_image, _check_bdm,
_provision, _record_action_start):
_get_image.return_value = (None, 'fake-image')
_validate.return_value = (None, 1)
_validate.return_value = ({}, 1)
_check_bdm.return_value = 'bdms'
_provision.return_value = 'instances'

View File

@ -382,6 +382,14 @@ class VIFTests(test.NoDBTestCase):
vif2 = model.VIF(active=False)
self.assertNotEqual(vif1, vif2)
vif1 = model.VIF(vnic_type=model.VNIC_TYPE_NORMAL)
vif2 = model.VIF(vnic_type=model.VNIC_TYPE_DIRECT)
self.assertNotEqual(vif1, vif2)
vif1 = model.VIF(profile={'pci_slot': '0000:0a:00.1'})
vif2 = model.VIF(profile={'pci_slot': '0000:0a:00.2'})
self.assertNotEqual(vif1, vif2)
def test_create_vif_with_type(self):
vif_dict = dict(
id=1,

View File

@ -14,6 +14,7 @@
# under the License.
#
import collections
import contextlib
import copy
import uuid
@ -35,6 +36,8 @@ from nova.network.neutronv2 import constants
from nova import objects
from nova.openstack.common import jsonutils
from nova.openstack.common import policy as common_policy
from nova.pci import pci_manager
from nova.pci import pci_whitelist
from nova import policy
from nova import test
from nova.tests import fake_instance
@ -250,6 +253,7 @@ class TestNeutronv2Base(test.TestCase):
'device_id': self.instance2['uuid'],
'device_owner': 'compute:nova',
'id': 'my_portid1',
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
'status': 'DOWN',
'admin_state_up': True,
'fixed_ips': [{'ip_address': self.port_address,
@ -271,6 +275,7 @@ class TestNeutronv2Base(test.TestCase):
'status': 'ACTIVE',
'device_owner': 'compute:nova',
'id': 'my_portid2',
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
'fixed_ips':
[{'ip_address': self.port_address2,
'subnet_id': 'my_subid2'}],
@ -286,6 +291,7 @@ class TestNeutronv2Base(test.TestCase):
'admin_state_up': True,
'device_owner': 'compute:nova',
'id': 'my_portid3',
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
'fixed_ips': [], # no fixed ip
'mac_address': 'my_mac3', }]
self.subnet_data1 = [{'id': 'my_subid1',
@ -467,7 +473,8 @@ class TestNeutronv2Base(test.TestCase):
self.instance.get('host'))
if not has_portbinding:
api._populate_neutron_extension_values(mox.IgnoreArg(),
self.instance, mox.IgnoreArg()).AndReturn(None)
self.instance, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn(None)
else:
# since _populate_neutron_extension_values() will call
# _has_port_binding_extension()
@ -834,7 +841,7 @@ class TestNeutronv2(TestNeutronv2Base):
instance = {'system_metadata': sys_meta}
port_req_body = {'port': {}}
api._populate_neutron_extension_values(self.context, instance,
port_req_body)
None, port_req_body)
self.assertEqual(port_req_body['port']['rxtx_factor'], 1)
def test_allocate_for_instance_1(self):
@ -1017,7 +1024,7 @@ class TestNeutronv2(TestNeutronv2Base):
port = {'id': 'portid_' + network['id']}
api._populate_neutron_extension_values(self.context,
self.instance, binding_port_req_body).AndReturn(None)
self.instance, None, binding_port_req_body).AndReturn(None)
if index == 0:
self.moxed_client.create_port(
MyComparator(port_req_body)).AndReturn({'port': port})
@ -1067,7 +1074,7 @@ class TestNeutronv2(TestNeutronv2Base):
},
}
api._populate_neutron_extension_values(self.context,
self.instance, binding_port_req_body).AndReturn(None)
self.instance, None, binding_port_req_body).AndReturn(None)
self.moxed_client.create_port(
MyComparator(port_req_body)).AndRaise(
Exception("fail to create port"))
@ -1128,7 +1135,7 @@ class TestNeutronv2(TestNeutronv2Base):
# NOTE(danms): Temporary and transitional
with mock.patch('nova.utils.is_neutron', return_value=True):
requested_networks = requested_networks.as_tuples()
for net, fip, port in requested_networks:
for net, fip, port, request_id in requested_networks:
ret_data.append({'network_id': net,
'device_id': self.instance.uuid,
'device_owner': 'compute:nova',
@ -1141,7 +1148,7 @@ class TestNeutronv2(TestNeutronv2Base):
device_id=self.instance.uuid).AndReturn(
{'ports': ret_data})
if requested_networks:
for net, fip, port in requested_networks:
for net, fip, port, request_id in requested_networks:
self.moxed_client.update_port(port)
for port in reversed(port_data):
self.moxed_client.delete_port(port['id'])
@ -1263,8 +1270,8 @@ class TestNeutronv2(TestNeutronv2Base):
neutronapi.API().show_port(self.context, 'foo')
def test_validate_networks(self):
requested_networks = [('my_netid1', None, None),
('my_netid2', None, None)]
requested_networks = [('my_netid1', None, None, None),
('my_netid2', None, None, None)]
ids = ['my_netid1', 'my_netid2']
self.moxed_client.list_networks(
id=mox.SameElementsAs(ids)).AndReturn(
@ -1279,8 +1286,8 @@ class TestNeutronv2(TestNeutronv2Base):
api.validate_networks(self.context, requested_networks, 1)
def test_validate_networks_without_port_quota_on_network_side(self):
requested_networks = [('my_netid1', None, None),
('my_netid2', None, None)]
requested_networks = [('my_netid1', None, None, None),
('my_netid2', None, None, None)]
ids = ['my_netid1', 'my_netid2']
self.moxed_client.list_networks(
id=mox.SameElementsAs(ids)).AndReturn(
@ -1295,7 +1302,7 @@ class TestNeutronv2(TestNeutronv2Base):
api.validate_networks(self.context, requested_networks, 1)
def test_validate_networks_ex_1(self):
requested_networks = [('my_netid1', None, None)]
requested_networks = [('my_netid1', None, None, None)]
self.moxed_client.list_networks(
id=mox.SameElementsAs(['my_netid1'])).AndReturn(
{'networks': self.nets1})
@ -1312,9 +1319,9 @@ class TestNeutronv2(TestNeutronv2Base):
self.assertIn("my_netid2", six.text_type(ex))
def test_validate_networks_ex_2(self):
requested_networks = [('my_netid1', None, None),
('my_netid2', None, None),
('my_netid3', None, None)]
requested_networks = [('my_netid1', None, None, None),
('my_netid2', None, None, None),
('my_netid3', None, None, None)]
ids = ['my_netid1', 'my_netid2', 'my_netid3']
self.moxed_client.list_networks(
id=mox.SameElementsAs(ids)).AndReturn(
@ -1331,8 +1338,8 @@ class TestNeutronv2(TestNeutronv2Base):
network ids are passed to validate_networks, when nova config flag
allow_duplicate_networks is set to its default value: False
"""
requested_networks = [('my_netid1', None, None),
('my_netid1', None, None)]
requested_networks = [('my_netid1', None, None, None),
('my_netid1', None, None, None)]
self.mox.ReplayAll()
# Expected call from setUp.
neutronv2.get_client(None)
@ -2330,6 +2337,8 @@ class TestNeutronv2(TestNeutronv2Base):
'fixed_ips': [{'ip_address': '1.1.1.1'}],
'mac_address': 'de:ad:be:ef:00:01',
'binding:vif_type': model.VIF_TYPE_BRIDGE,
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
'binding:vif_details': {},
},
# admin_state_up=False and status='DOWN' thus vif.active=True
{'id': 'port2',
@ -2339,6 +2348,8 @@ class TestNeutronv2(TestNeutronv2Base):
'fixed_ips': [{'ip_address': '1.1.1.1'}],
'mac_address': 'de:ad:be:ef:00:02',
'binding:vif_type': model.VIF_TYPE_BRIDGE,
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
'binding:vif_details': {},
},
# admin_state_up=True and status='DOWN' thus vif.active=False
{'id': 'port0',
@ -2348,13 +2359,44 @@ class TestNeutronv2(TestNeutronv2Base):
'fixed_ips': [{'ip_address': '1.1.1.1'}],
'mac_address': 'de:ad:be:ef:00:03',
'binding:vif_type': model.VIF_TYPE_BRIDGE,
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
'binding:vif_details': {},
},
# admin_state_up=True and status='ACTIVE' thus vif.active=True
{'id': 'port3',
'network_id': 'net-id',
'admin_state_up': True,
'status': 'ACTIVE',
'fixed_ips': [{'ip_address': '1.1.1.1'}],
'mac_address': 'de:ad:be:ef:00:04',
'binding:vif_type': model.VIF_TYPE_HW_VEB,
'binding:vnic_type': model.VNIC_TYPE_DIRECT,
'binding:profile': {'pci_vendor_info': '1137:0047',
'pci_slot': '0000:0a:00.1',
'physical_network': 'phynet1'},
'binding:vif_details': {model.VIF_DETAILS_PROFILEID: 'pfid'},
},
# admin_state_up=True and status='ACTIVE' thus vif.active=True
{'id': 'port4',
'network_id': 'net-id',
'admin_state_up': True,
'status': 'ACTIVE',
'fixed_ips': [{'ip_address': '1.1.1.1'}],
'mac_address': 'de:ad:be:ef:00:05',
'binding:vif_type': model.VIF_TYPE_802_QBH,
'binding:vnic_type': model.VNIC_TYPE_MACVTAP,
'binding:profile': {'pci_vendor_info': '1137:0047',
'pci_slot': '0000:0a:00.2',
'physical_network': 'phynet1'},
'binding:vif_details': {model.VIF_DETAILS_PROFILEID: 'pfid'},
},
# This does not match the networks we provide below,
# so it should be ignored (and is here to verify that)
{'id': 'port3',
{'id': 'port5',
'network_id': 'other-net-id',
'admin_state_up': True,
'status': 'DOWN',
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
},
]
fake_subnets = [model.Subnet(cidr='1.0.0.0/8')]
@ -2372,7 +2414,8 @@ class TestNeutronv2(TestNeutronv2Base):
self.mox.StubOutWithMock(api, '_get_floating_ips_by_fixed_and_port')
self.mox.StubOutWithMock(api, '_get_subnets_from_port')
requested_ports = [fake_ports[2], fake_ports[0], fake_ports[1]]
requested_ports = [fake_ports[2], fake_ports[0], fake_ports[1],
fake_ports[3], fake_ports[4]]
for requested_port in requested_ports:
api._get_floating_ips_by_fixed_and_port(
self.moxed_client, '1.1.1.1', requested_port['id']).AndReturn(
@ -2387,25 +2430,39 @@ class TestNeutronv2(TestNeutronv2Base):
fake_nets,
[fake_ports[2]['id'],
fake_ports[0]['id'],
fake_ports[1]['id']])
self.assertEqual(len(nw_infos), 3)
fake_ports[1]['id'],
fake_ports[3]['id'],
fake_ports[4]['id']])
self.assertEqual(len(nw_infos), 5)
index = 0
for nw_info in nw_infos:
self.assertEqual(nw_info['address'],
requested_ports[index]['mac_address'])
self.assertEqual(nw_info['devname'], 'tapport' + str(index))
self.assertIsNone(nw_info['ovs_interfaceid'])
self.assertEqual(nw_info['type'], model.VIF_TYPE_BRIDGE)
self.assertEqual(nw_info['network']['bridge'], 'brqnet-id')
self.assertEqual(nw_info['type'],
requested_ports[index]['binding:vif_type'])
if nw_info['type'] == model.VIF_TYPE_BRIDGE:
self.assertEqual(nw_info['network']['bridge'], 'brqnet-id')
self.assertEqual(nw_info['vnic_type'],
requested_ports[index]['binding:vnic_type'])
self.assertEqual(nw_info.get('details'),
requested_ports[index].get('binding:vif_details'))
self.assertEqual(nw_info.get('profile'),
requested_ports[index].get('binding:profile'))
index += 1
self.assertEqual(nw_infos[0]['active'], False)
self.assertEqual(nw_infos[1]['active'], True)
self.assertEqual(nw_infos[2]['active'], True)
self.assertEqual(nw_infos[3]['active'], True)
self.assertEqual(nw_infos[4]['active'], True)
self.assertEqual(nw_infos[0]['id'], 'port0')
self.assertEqual(nw_infos[1]['id'], 'port1')
self.assertEqual(nw_infos[2]['id'], 'port2')
self.assertEqual(nw_infos[3]['id'], 'port3')
self.assertEqual(nw_infos[4]['id'], 'port4')
def test_get_subnets_from_port(self):
api = neutronapi.API()
@ -2450,6 +2507,81 @@ class TestNeutronv2(TestNeutronv2Base):
api.get_floating_ips_by_fixed_address,
self.context, fake_fixed)
@mock.patch.object(neutronv2, 'get_client', return_value=mock.Mock())
def test_get_port_vnic_info_1(self, mock_get_client):
api = neutronapi.API()
self.mox.ResetAll()
test_port = {
'port': {'id': 'my_port_id1',
'network_id': 'net-id',
'binding:vnic_type': model.VNIC_TYPE_DIRECT,
},
}
test_net = {'network': {'provider:physical_network': 'phynet1'}}
mock_client = mock_get_client()
mock_client.show_port.return_value = test_port
mock_client.show_network.return_value = test_net
vnic_type, phynet_name = api._get_port_vnic_info(
self.context, mock_client, test_port['port']['id'])
mock_client.show_port.assert_called_once_with(test_port['port']['id'],
fields=['binding:vnic_type', 'network_id'])
mock_client.show_network.assert_called_once_with(
test_port['port']['network_id'],
fields='provider:physical_network')
self.assertEqual(model.VNIC_TYPE_DIRECT, vnic_type)
self.assertEqual(phynet_name, 'phynet1')
@mock.patch.object(neutronv2, 'get_client', return_value=mock.Mock())
def test_get_port_vnic_info_2(self, mock_get_client):
api = neutronapi.API()
self.mox.ResetAll()
test_port = {
'port': {'id': 'my_port_id2',
'network_id': 'net-id',
'binding:vnic_type': model.VNIC_TYPE_NORMAL,
},
}
mock_client = mock_get_client()
mock_client.show_port.return_value = test_port
vnic_type, phynet_name = api._get_port_vnic_info(
self.context, mock_client, test_port['port']['id'])
mock_client.show_port.assert_called_once_with(test_port['port']['id'],
fields=['binding:vnic_type', 'network_id'])
self.assertEqual(model.VNIC_TYPE_NORMAL, vnic_type)
self.assertFalse(phynet_name)
@mock.patch.object(neutronapi.API, "_get_port_vnic_info")
@mock.patch.object(neutronv2, 'get_client', return_value=mock.Mock())
def test_create_pci_requests_for_sriov_ports(self, mock_get_client,
mock_get_port_vnic_info):
api = neutronapi.API()
self.mox.ResetAll()
requested_networks = objects.NetworkRequestList(
objects = [
objects.NetworkRequest(port_id='my_portid1'),
objects.NetworkRequest(network_id='net1'),
objects.NetworkRequest(port_id='my_portid2'),
objects.NetworkRequest(port_id='my_portid3'),
objects.NetworkRequest(port_id='my_portid4')])
pci_requests = objects.InstancePCIRequests(requests=[])
mock_get_port_vnic_info.side_effect = [
(model.VNIC_TYPE_DIRECT, 'phynet1'),
(model.VNIC_TYPE_NORMAL, ''),
(model.VNIC_TYPE_MACVTAP, 'phynet1'),
(model.VNIC_TYPE_MACVTAP, 'phynet2')
]
api.create_pci_requests_for_sriov_ports(
None, pci_requests, requested_networks)
self.assertEqual(3, len(pci_requests.requests))
has_pci_request_id = [net.pci_request_id is not None for net in
requested_networks.objects]
expected_results = [True, False, False, True, True]
self.assertEqual(expected_results, has_pci_request_id)
class TestNeutronv2WithMock(test.TestCase):
"""Used to test Neutron V2 API with mock."""
@ -2495,7 +2627,7 @@ class TestNeutronv2WithMock(test.TestCase):
'subnets': ['mysubnid1'],
'tenant_id': 'fake-project'}]
requested_networks = [('my_netid1', '10.0.1.2', None)]
requested_networks = [('my_netid1', '10.0.1.2', None, None)]
ids = ['my_netid1']
list_port_values = [({'network_id': 'my_netid1',
'fixed_ips': 'ip_address=10.0.1.2',
@ -2519,8 +2651,8 @@ class TestNeutronv2WithMock(test.TestCase):
'subnets': ['mysubnid2'],
'tenant_id': 'fake-project'}]
requested_networks = [('my_netid1', '10.0.1.2', None),
('my_netid2', '10.0.1.3', None)]
requested_networks = [('my_netid1', '10.0.1.2', None, None),
('my_netid2', '10.0.1.3', None, None)]
ids = ['my_netid1', 'my_netid2']
list_port_values = [({'network_id': 'my_netid1',
'fixed_ips': 'ip_address=10.0.1.2',
@ -2541,7 +2673,7 @@ class TestNeutronv2WithMock(test.TestCase):
# Test validation for a request for a network with a
# fixed ip that is already in use
requested_networks = [('my_netid1', '10.0.1.2', None)]
requested_networks = [('my_netid1', '10.0.1.2', None, None)]
list_port_mock_params = {'network_id': 'my_netid1',
'fixed_ips': 'ip_address=10.0.1.2',
'fields': 'device_id'}
@ -2691,8 +2823,40 @@ class TestNeutronv2Portbinding(TestNeutronv2Base):
instance = {'host': host_id}
port_req_body = {'port': {}}
api._populate_neutron_extension_values(self.context, instance,
port_req_body)
None, port_req_body)
self.assertEqual(port_req_body['port']['binding:host_id'], host_id)
self.assertFalse(port_req_body['port'].get('binding:profile'))
@mock.patch.object(pci_whitelist, 'get_pci_device_devspec')
@mock.patch.object(pci_manager, 'get_instance_pci_devs')
def test_populate_neutron_extension_values_binding_sriov(self,
mock_get_instance_pci_devs,
mock_get_pci_device_devspec):
api = neutronapi.API()
host_id = 'my_host_id'
instance = {'host': host_id}
port_req_body = {'port': {}}
pci_req_id = 'my_req_id'
pci_dev = {'vendor_id': '1377',
'product_id': '0047',
'address': '0000:0a:00.1',
}
PciDevice = collections.namedtuple('PciDevice',
['vendor_id', 'product_id', 'address'])
mydev = PciDevice(**pci_dev)
profile = {'pci_vendor_info': '1377:0047',
'pci_slot': '0000:0a:00.1',
'physical_network': 'phynet1',
}
mock_get_instance_pci_devs.return_value = [mydev]
devspec = mock.Mock()
devspec.get_tags.return_value = {'physical_network': 'phynet1'}
mock_get_pci_device_devspec.return_value = devspec
api._populate_neutron_binding_profile(instance,
pci_req_id, port_req_body)
self.assertEqual(port_req_body['port']['binding:profile'], profile)
def test_migrate_instance_finish_binding_false(self):
api = neutronapi.API()

View File

@ -38,7 +38,7 @@ class _TestNetworkRequestObject(object):
port_id=FAKE_UUID,
)
with mock.patch('nova.utils.is_neutron', return_value=True):
self.assertEqual(('123', '1.2.3.4', FAKE_UUID),
self.assertEqual(('123', '1.2.3.4', FAKE_UUID, None),
request.to_tuple())
def test_to_tuple_nova(self):
@ -51,7 +51,7 @@ class _TestNetworkRequestObject(object):
def test_from_tuple_neutron(self):
request = objects.NetworkRequest.from_tuple(
('123', '1.2.3.4', FAKE_UUID))
('123', '1.2.3.4', FAKE_UUID, None))
self.assertEqual('123', request.network_id)
self.assertEqual('1.2.3.4', str(request.address))
self.assertEqual(FAKE_UUID, request.port_id)
@ -68,8 +68,9 @@ class _TestNetworkRequestObject(object):
requests = objects.NetworkRequestList(
objects=[objects.NetworkRequest(network_id='123'),
objects.NetworkRequest(network_id='456')])
self.assertEqual([('123', None, None), ('456', None, None)],
requests.as_tuples())
self.assertEqual(
[('123', None, None, None), ('456', None, None, None)],
requests.as_tuples())
def test_is_single_unspecified(self):
requests = objects.NetworkRequestList(

View File

@ -106,6 +106,8 @@ class PciDevTrackerTestCase(test.TestCase):
super(PciDevTrackerTestCase, self).setUp()
self.stubs.Set(db, 'pci_device_get_all_by_node',
self._fake_get_pci_devices)
# The fake_pci_whitelist must be called before creating the fake
# devices
patcher = pci_fakes.fake_pci_whitelist()
self.addCleanup(patcher.stop)
self._create_fake_instance()

View File

@ -28,7 +28,6 @@ from nova import context
from nova import db
from nova import exception
from nova import objects
from nova.pci import pci_request
from nova.scheduler import driver
from nova.scheduler import filter_scheduler
from nova.scheduler import host_manager
@ -676,19 +675,3 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
sched._provision_resource(fake_context, weighted_host,
request_spec, filter_properties,
None, None, None, None)
def test_pci_request_in_filter_properties(self):
instance_type = {}
request_spec = {'instance_type': instance_type,
'instance_properties': {'project_id': 1,
'os_type': 'Linux'}}
filter_properties = {}
requests = [{'count': 1, 'spec': [{'vendor_id': '8086'}]}]
self.mox.StubOutWithMock(pci_request, 'get_pci_requests_from_flavor')
pci_request.get_pci_requests_from_flavor(
instance_type).AndReturn(requests)
self.mox.ReplayAll()
self.driver.populate_filter_properties(
request_spec, filter_properties)
self.assertEqual(filter_properties.get('pci_requests'),
requests)