Merge "Add support to request vnic type on port"
This commit is contained in:
commit
8dc930a798
@ -47,6 +47,7 @@
|
|||||||
"create_port:port_security_enabled": "rule:admin_or_network_owner",
|
"create_port:port_security_enabled": "rule:admin_or_network_owner",
|
||||||
"create_port:binding:host_id": "rule:admin_only",
|
"create_port:binding:host_id": "rule:admin_only",
|
||||||
"create_port:binding:profile": "rule:admin_only",
|
"create_port:binding:profile": "rule:admin_only",
|
||||||
|
"create_port:binding:vnic_type": "rule:admin_or_owner",
|
||||||
"create_port:mac_learning_enabled": "rule:admin_or_network_owner",
|
"create_port:mac_learning_enabled": "rule:admin_or_network_owner",
|
||||||
"get_port": "rule:admin_or_owner",
|
"get_port": "rule:admin_or_owner",
|
||||||
"get_port:queue_id": "rule:admin_only",
|
"get_port:queue_id": "rule:admin_only",
|
||||||
@ -54,11 +55,13 @@
|
|||||||
"get_port:binding:capabilities": "rule:admin_only",
|
"get_port:binding:capabilities": "rule:admin_only",
|
||||||
"get_port:binding:host_id": "rule:admin_only",
|
"get_port:binding:host_id": "rule:admin_only",
|
||||||
"get_port:binding:profile": "rule:admin_only",
|
"get_port:binding:profile": "rule:admin_only",
|
||||||
|
"get_port:binding:vnic_type": "rule:admin_or_owner",
|
||||||
"update_port": "rule:admin_or_owner",
|
"update_port": "rule:admin_or_owner",
|
||||||
"update_port:fixed_ips": "rule:admin_or_network_owner",
|
"update_port:fixed_ips": "rule:admin_or_network_owner",
|
||||||
"update_port:port_security_enabled": "rule:admin_or_network_owner",
|
"update_port:port_security_enabled": "rule:admin_or_network_owner",
|
||||||
"update_port:binding:host_id": "rule:admin_only",
|
"update_port:binding:host_id": "rule:admin_only",
|
||||||
"update_port:binding:profile": "rule:admin_only",
|
"update_port:binding:profile": "rule:admin_only",
|
||||||
|
"update_port:binding:vnic_type": "rule:admin_or_owner",
|
||||||
"update_port:mac_learning_enabled": "rule:admin_or_network_owner",
|
"update_port:mac_learning_enabled": "rule:admin_or_network_owner",
|
||||||
"delete_port": "rule:admin_or_owner",
|
"delete_port": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
# Copyright 2014 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""ml2_vnic_type
|
||||||
|
|
||||||
|
Revision ID: 27cc183af192
|
||||||
|
Revises: 4ca36cfc898c
|
||||||
|
Create Date: 2014-02-09 12:19:21.362967
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '27cc183af192'
|
||||||
|
down_revision = '4ca36cfc898c'
|
||||||
|
|
||||||
|
# Change to ['*'] if this migration applies to all plugins
|
||||||
|
|
||||||
|
migration_for_plugins = [
|
||||||
|
'neutron.plugins.ml2.plugin.Ml2Plugin'
|
||||||
|
]
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from neutron.db import migration
|
||||||
|
from neutron.extensions import portbindings
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(active_plugins=None, options=None):
|
||||||
|
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||||
|
return
|
||||||
|
|
||||||
|
op.add_column('ml2_port_bindings',
|
||||||
|
sa.Column('vnic_type', sa.String(length=64),
|
||||||
|
nullable=False,
|
||||||
|
server_default=portbindings.VNIC_NORMAL))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(active_plugins=None, options=None):
|
||||||
|
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||||
|
return
|
||||||
|
|
||||||
|
op.drop_column('ml2_port_bindings', 'vnic_type')
|
@ -71,6 +71,16 @@ class PortBindingMixin(portbindings_base.PortBindingBaseMixin):
|
|||||||
binding_profile_set = attributes.is_attr_set(binding_profile)
|
binding_profile_set = attributes.is_attr_set(binding_profile)
|
||||||
if not binding_profile_set and binding_profile is not None:
|
if not binding_profile_set and binding_profile is not None:
|
||||||
del port[portbindings.PROFILE]
|
del port[portbindings.PROFILE]
|
||||||
|
|
||||||
|
binding_vnic = port.get(portbindings.VNIC_TYPE)
|
||||||
|
binding_vnic_set = attributes.is_attr_set(binding_vnic)
|
||||||
|
if not binding_vnic_set and binding_vnic is not None:
|
||||||
|
del port[portbindings.VNIC_TYPE]
|
||||||
|
# REVISIT(irenab) Add support for vnic_type for plugins that
|
||||||
|
# can handle more than one type.
|
||||||
|
# Currently implemented for ML2 plugin that does not use
|
||||||
|
# PortBindingMixin.
|
||||||
|
|
||||||
host = port_data.get(portbindings.HOST_ID)
|
host = port_data.get(portbindings.HOST_ID)
|
||||||
host_set = attributes.is_attr_set(host)
|
host_set = attributes.is_attr_set(host)
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
from neutron.api import extensions
|
from neutron.api import extensions
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
|
|
||||||
|
# The type of vnic that this port should be attached to
|
||||||
|
VNIC_TYPE = 'binding:vnic_type'
|
||||||
# The service will return the vif type for the specific port.
|
# The service will return the vif type for the specific port.
|
||||||
VIF_TYPE = 'binding:vif_type'
|
VIF_TYPE = 'binding:vif_type'
|
||||||
# In some cases different implementations may be run on different hosts.
|
# In some cases different implementations may be run on different hosts.
|
||||||
@ -51,6 +52,10 @@ VIF_TYPES = [VIF_TYPE_UNBOUND, VIF_TYPE_BINDING_FAILED, VIF_TYPE_OVS,
|
|||||||
VIF_TYPE_802_QBH, VIF_TYPE_HYPERV, VIF_TYPE_MIDONET,
|
VIF_TYPE_802_QBH, VIF_TYPE_HYPERV, VIF_TYPE_MIDONET,
|
||||||
VIF_TYPE_OTHER]
|
VIF_TYPE_OTHER]
|
||||||
|
|
||||||
|
VNIC_NORMAL = 'normal'
|
||||||
|
VNIC_DIRECT = 'direct'
|
||||||
|
VNIC_MACVTAP = 'macvtap'
|
||||||
|
VNIC_TYPES = [VNIC_NORMAL, VNIC_DIRECT, VNIC_MACVTAP]
|
||||||
|
|
||||||
EXTENDED_ATTRIBUTES_2_0 = {
|
EXTENDED_ATTRIBUTES_2_0 = {
|
||||||
'ports': {
|
'ports': {
|
||||||
@ -58,6 +63,11 @@ EXTENDED_ATTRIBUTES_2_0 = {
|
|||||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
'enforce_policy': True,
|
'enforce_policy': True,
|
||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
|
VNIC_TYPE: {'allow_post': True, 'allow_put': True,
|
||||||
|
'default': VNIC_NORMAL,
|
||||||
|
'is_visible': True,
|
||||||
|
'validate': {'type:values': VNIC_TYPES},
|
||||||
|
'enforce_policy': True},
|
||||||
HOST_ID: {'allow_post': True, 'allow_put': True,
|
HOST_ID: {'allow_post': True, 'allow_put': True,
|
||||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
'is_visible': True,
|
'is_visible': True,
|
||||||
|
@ -17,6 +17,7 @@ from abc import ABCMeta, abstractmethod
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from neutron.extensions import portbindings
|
||||||
from neutron.openstack.common import log
|
from neutron.openstack.common import log
|
||||||
from neutron.plugins.ml2 import driver_api as api
|
from neutron.plugins.ml2 import driver_api as api
|
||||||
|
|
||||||
@ -38,7 +39,8 @@ class AgentMechanismDriverBase(api.MechanismDriver):
|
|||||||
check_segment_for_agent().
|
check_segment_for_agent().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, agent_type, vif_type, cap_port_filter):
|
def __init__(self, agent_type, vif_type, cap_port_filter,
|
||||||
|
supported_vnic_types=[portbindings.VNIC_NORMAL]):
|
||||||
"""Initialize base class for specific L2 agent type.
|
"""Initialize base class for specific L2 agent type.
|
||||||
|
|
||||||
:param agent_type: Constant identifying agent type in agents_db
|
:param agent_type: Constant identifying agent type in agents_db
|
||||||
@ -47,6 +49,7 @@ class AgentMechanismDriverBase(api.MechanismDriver):
|
|||||||
self.agent_type = agent_type
|
self.agent_type = agent_type
|
||||||
self.vif_type = vif_type
|
self.vif_type = vif_type
|
||||||
self.cap_port_filter = cap_port_filter
|
self.cap_port_filter = cap_port_filter
|
||||||
|
self.supported_vnic_types = supported_vnic_types
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
pass
|
pass
|
||||||
@ -56,6 +59,12 @@ class AgentMechanismDriverBase(api.MechanismDriver):
|
|||||||
"network %(network)s"),
|
"network %(network)s"),
|
||||||
{'port': context.current['id'],
|
{'port': context.current['id'],
|
||||||
'network': context.network.current['id']})
|
'network': context.network.current['id']})
|
||||||
|
vnic_type = context.current.get(portbindings.VNIC_TYPE,
|
||||||
|
portbindings.VNIC_NORMAL)
|
||||||
|
if vnic_type not in self.supported_vnic_types:
|
||||||
|
LOG.debug(_("Refusing to bind due to unsupported vnic_type: %s"),
|
||||||
|
vnic_type)
|
||||||
|
return
|
||||||
for agent in context.host_agents(self.agent_type):
|
for agent in context.host_agents(self.agent_type):
|
||||||
LOG.debug(_("Checking agent: %s"), agent)
|
LOG.debug(_("Checking agent: %s"), agent)
|
||||||
if agent['alive']:
|
if agent['alive']:
|
||||||
|
@ -437,15 +437,18 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
|
|||||||
attempt to establish a port binding.
|
attempt to establish a port binding.
|
||||||
"""
|
"""
|
||||||
binding = context._binding
|
binding = context._binding
|
||||||
LOG.debug(_("Attempting to bind port %(port)s on host %(host)s"),
|
LOG.debug(_("Attempting to bind port %(port)s on host %(host)s "
|
||||||
|
"for vnic_type %(vnic_type)s"),
|
||||||
{'port': context._port['id'],
|
{'port': context._port['id'],
|
||||||
'host': binding.host})
|
'host': binding.host,
|
||||||
|
'vnic_type': binding.vnic_type})
|
||||||
for driver in self.ordered_mech_drivers:
|
for driver in self.ordered_mech_drivers:
|
||||||
try:
|
try:
|
||||||
driver.obj.bind_port(context)
|
driver.obj.bind_port(context)
|
||||||
if binding.segment:
|
if binding.segment:
|
||||||
binding.driver = driver.name
|
binding.driver = driver.name
|
||||||
LOG.debug(_("Bound port: %(port)s, host: %(host)s, "
|
LOG.debug(_("Bound port: %(port)s, host: %(host)s, "
|
||||||
|
"vnic_type: %(vnic_type)s, "
|
||||||
"driver: %(driver)s, vif_type: %(vif_type)s, "
|
"driver: %(driver)s, vif_type: %(vif_type)s, "
|
||||||
"cap_port_filter: %(cap_port_filter)s, "
|
"cap_port_filter: %(cap_port_filter)s, "
|
||||||
"segment: %(segment)s"),
|
"segment: %(segment)s"),
|
||||||
@ -453,6 +456,7 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
|
|||||||
'host': binding.host,
|
'host': binding.host,
|
||||||
'driver': binding.driver,
|
'driver': binding.driver,
|
||||||
'vif_type': binding.vif_type,
|
'vif_type': binding.vif_type,
|
||||||
|
'vnic_type': binding.vnic_type,
|
||||||
'cap_port_filter': binding.cap_port_filter,
|
'cap_port_filter': binding.cap_port_filter,
|
||||||
'segment': binding.segment})
|
'segment': binding.segment})
|
||||||
return
|
return
|
||||||
|
@ -18,6 +18,7 @@ from sqlalchemy import orm
|
|||||||
|
|
||||||
from neutron.db import model_base
|
from neutron.db import model_base
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
|
from neutron.extensions import portbindings
|
||||||
|
|
||||||
|
|
||||||
class NetworkSegment(model_base.BASEV2, models_v2.HasId):
|
class NetworkSegment(model_base.BASEV2, models_v2.HasId):
|
||||||
@ -53,6 +54,8 @@ class PortBinding(model_base.BASEV2):
|
|||||||
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
||||||
primary_key=True)
|
primary_key=True)
|
||||||
host = sa.Column(sa.String(255), nullable=False)
|
host = sa.Column(sa.String(255), nullable=False)
|
||||||
|
vnic_type = sa.Column(sa.String(64), nullable=False,
|
||||||
|
default=portbindings.VNIC_NORMAL)
|
||||||
vif_type = sa.Column(sa.String(64), nullable=False)
|
vif_type = sa.Column(sa.String(64), nullable=False)
|
||||||
cap_port_filter = sa.Column(sa.Boolean, nullable=False)
|
cap_port_filter = sa.Column(sa.Boolean, nullable=False)
|
||||||
driver = sa.Column(sa.String(64))
|
driver = sa.Column(sa.String(64))
|
||||||
|
@ -208,23 +208,30 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
self._update_port_dict_binding(port, binding)
|
self._update_port_dict_binding(port, binding)
|
||||||
host = attrs and attrs.get(portbindings.HOST_ID)
|
host = attrs and attrs.get(portbindings.HOST_ID)
|
||||||
host_set = attributes.is_attr_set(host)
|
host_set = attributes.is_attr_set(host)
|
||||||
|
vnic_type = attrs and attrs.get(portbindings.VNIC_TYPE)
|
||||||
|
vnic_type_set = attributes.is_attr_set(vnic_type)
|
||||||
|
|
||||||
if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
|
if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
|
||||||
if (not host_set and binding.segment and
|
if (not host_set and not vnic_type_set and binding.segment and
|
||||||
self.mechanism_manager.validate_port_binding(mech_context)):
|
self.mechanism_manager.validate_port_binding(mech_context)):
|
||||||
return False
|
return False
|
||||||
self.mechanism_manager.unbind_port(mech_context)
|
self.mechanism_manager.unbind_port(mech_context)
|
||||||
self._update_port_dict_binding(port, binding)
|
self._update_port_dict_binding(port, binding)
|
||||||
|
|
||||||
# Return True only if an agent notification is needed.
|
# Return True only if an agent notification is needed.
|
||||||
# This will happen if a new host was specified and that host
|
# This will happen if a new host or vnic_type was specified that
|
||||||
# differs from the current one. Note that host_set is True
|
# differs from the current one. Note that host_set is True
|
||||||
# even if the host is an empty string
|
# even if the host is an empty string
|
||||||
ret_value = host_set and binding.get('host') != host
|
ret_value = ((host_set and binding.get('host') != host) or
|
||||||
|
(vnic_type_set and binding.get('vnic_type') != vnic_type))
|
||||||
if host_set:
|
if host_set:
|
||||||
binding.host = host
|
binding.host = host
|
||||||
port[portbindings.HOST_ID] = host
|
port[portbindings.HOST_ID] = host
|
||||||
|
|
||||||
|
if vnic_type_set:
|
||||||
|
binding.vnic_type = vnic_type
|
||||||
|
port[portbindings.VNIC_TYPE] = vnic_type
|
||||||
|
|
||||||
if binding.host:
|
if binding.host:
|
||||||
self.mechanism_manager.bind_port(mech_context)
|
self.mechanism_manager.bind_port(mech_context)
|
||||||
self._update_port_dict_binding(port, binding)
|
self._update_port_dict_binding(port, binding)
|
||||||
@ -233,6 +240,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
def _update_port_dict_binding(self, port, binding):
|
def _update_port_dict_binding(self, port, binding):
|
||||||
port[portbindings.HOST_ID] = binding.host
|
port[portbindings.HOST_ID] = binding.host
|
||||||
|
port[portbindings.VNIC_TYPE] = binding.vnic_type
|
||||||
port[portbindings.VIF_TYPE] = binding.vif_type
|
port[portbindings.VIF_TYPE] = binding.vif_type
|
||||||
port[portbindings.CAPABILITIES] = {
|
port[portbindings.CAPABILITIES] = {
|
||||||
portbindings.CAP_PORT_FILTER: binding.cap_port_filter}
|
portbindings.CAP_PORT_FILTER: binding.cap_port_filter}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import httplib
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from webob import exc
|
from webob import exc
|
||||||
@ -279,3 +280,89 @@ class PortBindingsHostTestCaseMixin(object):
|
|||||||
self._test_list_resources(
|
self._test_list_resources(
|
||||||
'port', (port1, port3),
|
'port', (port1, port3),
|
||||||
query_params='%s=%s' % (portbindings.HOST_ID, self.hostname))
|
query_params='%s=%s' % (portbindings.HOST_ID, self.hostname))
|
||||||
|
|
||||||
|
|
||||||
|
class PortBindingsVnicTestCaseMixin(object):
|
||||||
|
fmt = 'json'
|
||||||
|
vnic_type = portbindings.VNIC_NORMAL
|
||||||
|
|
||||||
|
def _check_response_portbindings_vnic_type(self, port):
|
||||||
|
self.assertIn('status', port)
|
||||||
|
self.assertEqual(port[portbindings.VNIC_TYPE], self.vnic_type)
|
||||||
|
|
||||||
|
def test_port_vnic_type_non_admin(self):
|
||||||
|
with self.network(set_context=True,
|
||||||
|
tenant_id='test') as net1:
|
||||||
|
with self.subnet(network=net1) as subnet1:
|
||||||
|
vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type}
|
||||||
|
with self.port(subnet=subnet1,
|
||||||
|
expected_res_status=httplib.CREATED,
|
||||||
|
arg_list=(portbindings.VNIC_TYPE,),
|
||||||
|
set_context=True,
|
||||||
|
tenant_id='test',
|
||||||
|
**vnic_arg) as port:
|
||||||
|
# Check a response of create_port
|
||||||
|
self._check_response_portbindings_vnic_type(port['port'])
|
||||||
|
|
||||||
|
def test_port_vnic_type(self):
|
||||||
|
vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type}
|
||||||
|
with self.port(name='name', arg_list=(portbindings.VNIC_TYPE,),
|
||||||
|
**vnic_arg) as port:
|
||||||
|
port_id = port['port']['id']
|
||||||
|
# Check a response of create_port
|
||||||
|
self._check_response_portbindings_vnic_type(port['port'])
|
||||||
|
# Check a response of get_port
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
port = self._show('ports', port_id, neutron_context=ctx)['port']
|
||||||
|
self._check_response_portbindings_vnic_type(port)
|
||||||
|
# By default user is admin - now test non admin user
|
||||||
|
ctx = context.Context(user_id=None,
|
||||||
|
tenant_id=self._tenant_id,
|
||||||
|
is_admin=False,
|
||||||
|
read_deleted="no")
|
||||||
|
non_admin_port = self._show(
|
||||||
|
'ports', port_id, neutron_context=ctx)['port']
|
||||||
|
self._check_response_portbindings_vnic_type(non_admin_port)
|
||||||
|
|
||||||
|
def test_ports_vnic_type(self):
|
||||||
|
cfg.CONF.set_default('allow_overlapping_ips', True)
|
||||||
|
vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type}
|
||||||
|
with contextlib.nested(
|
||||||
|
self.port(name='name1',
|
||||||
|
arg_list=(portbindings.VNIC_TYPE,),
|
||||||
|
**vnic_arg),
|
||||||
|
self.port(name='name2')):
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
ports = self._list('ports', neutron_context=ctx)['ports']
|
||||||
|
self.assertEqual(2, len(ports))
|
||||||
|
for port in ports:
|
||||||
|
if port['name'] == 'name1':
|
||||||
|
self._check_response_portbindings_vnic_type(port)
|
||||||
|
else:
|
||||||
|
self.assertEqual(portbindings.VNIC_NORMAL,
|
||||||
|
port[portbindings.VNIC_TYPE])
|
||||||
|
# By default user is admin - now test non admin user
|
||||||
|
ctx = context.Context(user_id=None,
|
||||||
|
tenant_id=self._tenant_id,
|
||||||
|
is_admin=False,
|
||||||
|
read_deleted="no")
|
||||||
|
ports = self._list('ports', neutron_context=ctx)['ports']
|
||||||
|
self.assertEqual(2, len(ports))
|
||||||
|
for non_admin_port in ports:
|
||||||
|
self._check_response_portbindings_vnic_type(non_admin_port)
|
||||||
|
|
||||||
|
def test_ports_vnic_type_list(self):
|
||||||
|
cfg.CONF.set_default('allow_overlapping_ips', True)
|
||||||
|
vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type}
|
||||||
|
with contextlib.nested(
|
||||||
|
self.port(name='name1',
|
||||||
|
arg_list=(portbindings.VNIC_TYPE,),
|
||||||
|
**vnic_arg),
|
||||||
|
self.port(name='name2'),
|
||||||
|
self.port(name='name3',
|
||||||
|
arg_list=(portbindings.VNIC_TYPE,),
|
||||||
|
**vnic_arg),) as (port1, port2, port3):
|
||||||
|
self._test_list_resources(
|
||||||
|
'port', (port1, port2, port3),
|
||||||
|
query_params='%s=%s' % (portbindings.VNIC_TYPE,
|
||||||
|
self.vnic_type))
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
from neutron.extensions import portbindings
|
||||||
from neutron.plugins.ml2 import driver_api as api
|
from neutron.plugins.ml2 import driver_api as api
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ class FakePortContext(api.PortContext):
|
|||||||
self._network_context = FakeNetworkContext(segments)
|
self._network_context = FakeNetworkContext(segments)
|
||||||
self._bound_segment_id = None
|
self._bound_segment_id = None
|
||||||
self._bound_vif_type = None
|
self._bound_vif_type = None
|
||||||
|
self._bound_vnic_type = portbindings.VNIC_NORMAL
|
||||||
self._bound_cap_port_filter = None
|
self._bound_cap_port_filter = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -137,6 +137,11 @@ class TestMl2PortBindingHost(Ml2PluginV2TestCase,
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestMl2PortBindingVnicType(Ml2PluginV2TestCase,
|
||||||
|
test_bindings.PortBindingsVnicTestCaseMixin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestMultiSegmentNetworks(Ml2PluginV2TestCase):
|
class TestMultiSegmentNetworks(Ml2PluginV2TestCase):
|
||||||
|
|
||||||
def setUp(self, plugin=None):
|
def setUp(self, plugin=None):
|
||||||
|
Loading…
Reference in New Issue
Block a user