From dda508188315e687d84d0faeb9d5d4b6b2f654eb Mon Sep 17 00:00:00 2001 From: Bob Kukura Date: Mon, 3 Feb 2014 23:18:44 -0500 Subject: [PATCH] Replace binding:capabilities with binding:vif_details In addition to binding:vif_type, the neutron core plugin needs to supply various information to nova's VIF driver, such as VIF security details and PCI details when SR-IOV is being used. This information is read-only, requires admin privileges, and is not intended for normal users. Rather than add separate mechanisms throughout the stack for each such requirement, the binding:capabilities port attibute, which is a dictionary and is not currently not used by nova, is renamed to binding:vif_details to serve as a general-purpose mechanism for supplying binding-specific details to the VIF driver. This patch does not remove or replace the CAP_PORT_FILTER boolean previously used in binding:capabilities. A separate patch should implement the specific key/value pairs carried by binding:vif_details to implement VIF security. Another patch will implement the key/value pairs needed for SR-IOV. The ML2 plugin now allows the bound mechanism driver to supply the binding:vif_details dictionary content, instead of just the CAP_PORT_FILTER boolean previously carried by the binding:capabilities attribute. DocImpact: Need to update portbinding extension API, but no impact on user or administrator documentation. Implements: blueprint vif-details Related-Bug: 1112912 Change-Id: I34be746fcfa73c70f72b4f9add8eff3ac88c723f --- etc/policy.json | 2 +- .../50d5ba354c23_ml2_binding_vif_details.py | 59 +++++++++++++ neutron/extensions/portbindings.py | 25 ++++-- neutron/plugins/bigswitch/plugin.py | 3 +- neutron/plugins/brocade/NeutronPlugin.py | 3 +- .../plugins/linuxbridge/lb_neutron_plugin.py | 3 +- neutron/plugins/midonet/plugin.py | 3 +- neutron/plugins/ml2/db.py | 3 +- neutron/plugins/ml2/driver_api.py | 4 +- neutron/plugins/ml2/driver_context.py | 9 +- neutron/plugins/ml2/drivers/mech_agent.py | 85 +++++++++++++++---- neutron/plugins/ml2/drivers/mech_hyperv.py | 4 +- .../plugins/ml2/drivers/mech_linuxbridge.py | 4 +- .../plugins/ml2/drivers/mech_openvswitch.py | 4 +- neutron/plugins/ml2/managers.py | 8 +- neutron/plugins/ml2/models.py | 2 +- neutron/plugins/ml2/plugin.py | 15 +++- neutron/plugins/mlnx/mlnx_plugin.py | 3 +- neutron/plugins/nec/nec_plugin.py | 3 +- neutron/plugins/nicira/NeutronPlugin.py | 3 +- .../plugins/openvswitch/ovs_neutron_plugin.py | 3 +- .../plumgrid_plugin/plumgrid_plugin.py | 3 +- neutron/plugins/ryu/ryu_neutron_plugin.py | 3 +- neutron/policy.py | 2 +- .../unit/_test_extension_portbindings.py | 14 +-- neutron/tests/unit/ml2/_test_mech_agent.py | 14 +-- .../tests/unit/ml2/drivers/mechanism_test.py | 6 +- neutron/tests/unit/ml2/test_port_binding.py | 20 +++-- 28 files changed, 231 insertions(+), 79 deletions(-) create mode 100644 neutron/db/migration/alembic_migrations/versions/50d5ba354c23_ml2_binding_vif_details.py diff --git a/etc/policy.json b/etc/policy.json index bd0bc9274c..f2dfa0f4b4 100644 --- a/etc/policy.json +++ b/etc/policy.json @@ -52,7 +52,7 @@ "get_port": "rule:admin_or_owner", "get_port:queue_id": "rule:admin_only", "get_port:binding:vif_type": "rule:admin_only", - "get_port:binding:capabilities": "rule:admin_only", + "get_port:binding:vif_details": "rule:admin_only", "get_port:binding:host_id": "rule:admin_only", "get_port:binding:profile": "rule:admin_only", "get_port:binding:vnic_type": "rule:admin_or_owner", diff --git a/neutron/db/migration/alembic_migrations/versions/50d5ba354c23_ml2_binding_vif_details.py b/neutron/db/migration/alembic_migrations/versions/50d5ba354c23_ml2_binding_vif_details.py new file mode 100644 index 0000000000..fc50807dac --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/50d5ba354c23_ml2_binding_vif_details.py @@ -0,0 +1,59 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# 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 binding:vif_details + +Revision ID: 50d5ba354c23 +Revises: 27cc183af192 +Create Date: 2014-02-11 23:21:59.577972 + +""" + +# revision identifiers, used by Alembic. +revision = '50d5ba354c23' +down_revision = '27cc183af192' + +# 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 + + +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('vif_details', sa.String(length=4095), + nullable=False, server_default='')) + op.drop_column('ml2_port_bindings', 'cap_port_filter') + + +def downgrade(active_plugins=None, options=None): + if not migration.should_run(active_plugins, migration_for_plugins): + return + + op.add_column('ml2_port_bindings', + sa.Column('cap_port_filter', sa.Boolean(), + nullable=False, server_default=False)) + op.drop_column('ml2_port_bindings', 'vif_details') diff --git a/neutron/extensions/portbindings.py b/neutron/extensions/portbindings.py index 4ed38fc239..6c1ed7ed40 100644 --- a/neutron/extensions/portbindings.py +++ b/neutron/extensions/portbindings.py @@ -22,6 +22,10 @@ from neutron.api.v2 import attributes VNIC_TYPE = 'binding:vnic_type' # The service will return the vif type for the specific port. VIF_TYPE = 'binding:vif_type' +# The service may return a dictionary containing additional +# information needed by the interface driver. The set of items +# returned may depend on the value of VIF_TYPE. +VIF_DETAILS = 'binding:vif_details' # In some cases different implementations may be run on different hosts. # The host on which the port will be allocated. HOST_ID = 'binding:host_id' @@ -29,11 +33,16 @@ HOST_ID = 'binding:host_id' # on the specific host to pass and receive vif port specific information to # the plugin. PROFILE = 'binding:profile' -# The capabilities will be a dictionary that enables pass information about -# functionalies neutron provides. The following value should be provided. + +# The keys below are used in the VIF_DETAILS attribute to convey +# information to the VIF driver. + +# TODO(rkukura): Replace CAP_PORT_FILTER, which nova no longer +# understands, with the new set of VIF security details to be used in +# the VIF_DETAILS attribute. +# # - port_filter : Boolean value indicating Neutron provides port filtering # features such as security group and anti MAC/IP spoofing -CAPABILITIES = 'binding:capabilities' CAP_PORT_FILTER = 'port_filter' VIF_TYPE_UNBOUND = 'unbound' @@ -63,6 +72,10 @@ EXTENDED_ATTRIBUTES_2_0 = { 'default': attributes.ATTR_NOT_SPECIFIED, 'enforce_policy': True, 'is_visible': True}, + VIF_DETAILS: {'allow_post': False, 'allow_put': False, + 'default': attributes.ATTR_NOT_SPECIFIED, + 'enforce_policy': True, + 'is_visible': True}, VNIC_TYPE: {'allow_post': True, 'allow_put': True, 'default': VNIC_NORMAL, 'is_visible': True, @@ -77,10 +90,6 @@ EXTENDED_ATTRIBUTES_2_0 = { 'enforce_policy': True, 'validate': {'type:dict_or_none': None}, 'is_visible': True}, - CAPABILITIES: {'allow_post': False, 'allow_put': False, - 'default': attributes.ATTR_NOT_SPECIFIED, - 'enforce_policy': True, - 'is_visible': True}, } } @@ -112,7 +121,7 @@ class Portbindings(extensions.ExtensionDescriptor): @classmethod def get_updated(cls): - return "2012-11-14T10:00:00-00:00" + return "2014-02-03T10:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": diff --git a/neutron/plugins/bigswitch/plugin.py b/neutron/plugins/bigswitch/plugin.py index a80385d4f8..fb4421a2a3 100644 --- a/neutron/plugins/bigswitch/plugin.py +++ b/neutron/plugins/bigswitch/plugin.py @@ -300,7 +300,8 @@ class NeutronRestProxyV2Base(db_base_plugin_v2.NeutronDbPluginV2, cfg_vif_type = override port[portbindings.VIF_TYPE] = cfg_vif_type - port[portbindings.CAPABILITIES] = { + port[portbindings.VIF_DETAILS] = { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases} return port diff --git a/neutron/plugins/brocade/NeutronPlugin.py b/neutron/plugins/brocade/NeutronPlugin.py index f5a04ad205..ed27f2881a 100644 --- a/neutron/plugins/brocade/NeutronPlugin.py +++ b/neutron/plugins/brocade/NeutronPlugin.py @@ -478,7 +478,8 @@ class BrocadePluginV2(db_base_plugin_v2.NeutronDbPluginV2, def _get_base_binding_dict(self): binding = { portbindings.VIF_TYPE: portbindings.VIF_TYPE_BRIDGE, - portbindings.CAPABILITIES: { + portbindings.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} return binding diff --git a/neutron/plugins/linuxbridge/lb_neutron_plugin.py b/neutron/plugins/linuxbridge/lb_neutron_plugin.py index c06ce6a76e..4cd1f8155e 100644 --- a/neutron/plugins/linuxbridge/lb_neutron_plugin.py +++ b/neutron/plugins/linuxbridge/lb_neutron_plugin.py @@ -255,7 +255,8 @@ class LinuxBridgePluginV2(db_base_plugin_v2.NeutronDbPluginV2, super(LinuxBridgePluginV2, self).__init__() self.base_binding_dict = { portbindings.VIF_TYPE: portbindings.VIF_TYPE_BRIDGE, - portbindings.CAPABILITIES: { + portbindings.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} self._parse_network_vlan_ranges() diff --git a/neutron/plugins/midonet/plugin.py b/neutron/plugins/midonet/plugin.py index 984736dcbc..2977b9b3dd 100644 --- a/neutron/plugins/midonet/plugin.py +++ b/neutron/plugins/midonet/plugin.py @@ -234,7 +234,8 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2, self.base_binding_dict = { portbindings.VIF_TYPE: portbindings.VIF_TYPE_MIDONET, - portbindings.CAPABILITIES: { + portbindings.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py index 0a2d981a5f..00475070dc 100644 --- a/neutron/plugins/ml2/db.py +++ b/neutron/plugins/ml2/db.py @@ -66,8 +66,7 @@ def ensure_port_binding(session, port_id): record = models.PortBinding( port_id=port_id, host='', - vif_type=portbindings.VIF_TYPE_UNBOUND, - cap_port_filter=False) + vif_type=portbindings.VIF_TYPE_UNBOUND) session.add(record) return record diff --git a/neutron/plugins/ml2/driver_api.py b/neutron/plugins/ml2/driver_api.py index d7663042b9..f6167e761c 100644 --- a/neutron/plugins/ml2/driver_api.py +++ b/neutron/plugins/ml2/driver_api.py @@ -246,12 +246,12 @@ class PortContext(object): pass @abstractmethod - def set_binding(self, segment_id, vif_type, cap_port_filter): + def set_binding(self, segment_id, vif_type, vif_details): """Set the binding for the port. :param segment_id: Network segment bound for the port. :param vif_type: The VIF type for the bound port. - :param cap_port_filter: True if the bound port filters. + :param vif_details: Dictionary with details for VIF driver. Called by MechanismDriver.bind_port to indicate success and specify binding details to use for port. The segment_id must diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py index 7468192f74..89f4e61ac7 100644 --- a/neutron/plugins/ml2/driver_context.py +++ b/neutron/plugins/ml2/driver_context.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.openstack.common import jsonutils from neutron.plugins.ml2 import db from neutron.plugins.ml2 import driver_api as api @@ -103,12 +104,8 @@ class PortContext(MechanismDriverContext, api.PortContext): filters={'agent_type': [agent_type], 'host': [self._binding.host]}) - def set_binding(self, segment_id, vif_type, cap_port_filter): - # REVISIT(rkukura): Pass extensible list of capabilities? Move - # vif_type and capabilities to methods on the bound mechanism - # driver? - + def set_binding(self, segment_id, vif_type, vif_details): # TODO(rkukura) Verify binding allowed, segment in network self._binding.segment = segment_id self._binding.vif_type = vif_type - self._binding.cap_port_filter = cap_port_filter + self._binding.vif_details = jsonutils.dumps(vif_details) diff --git a/neutron/plugins/ml2/drivers/mech_agent.py b/neutron/plugins/ml2/drivers/mech_agent.py index 2081057dad..319055a891 100644 --- a/neutron/plugins/ml2/drivers/mech_agent.py +++ b/neutron/plugins/ml2/drivers/mech_agent.py @@ -34,21 +34,19 @@ class AgentMechanismDriverBase(api.MechanismDriver): running on the port's host, and that agent to have connectivity to at least one segment of the port's network. - MechanismDrivers using this base class must pass the agent type - and VIF type constants to __init__(), and must implement + MechanismDrivers using this base class must pass the agent type to + __init__(), and must implement try_to_bind_segment_for_agent() and check_segment_for_agent(). """ - def __init__(self, agent_type, vif_type, cap_port_filter, + def __init__(self, agent_type, supported_vnic_types=[portbindings.VNIC_NORMAL]): """Initialize base class for specific L2 agent type. :param agent_type: Constant identifying agent type in agents_db - :param vif_type: Value for binding:vif_type to when bound + :param supported_vnic_types: The binding:vnic_type values we can bind """ self.agent_type = agent_type - self.vif_type = vif_type - self.cap_port_filter = cap_port_filter self.supported_vnic_types = supported_vnic_types def initialize(self): @@ -69,10 +67,8 @@ class AgentMechanismDriverBase(api.MechanismDriver): LOG.debug(_("Checking agent: %s"), agent) if agent['alive']: for segment in context.network.network_segments: - if self.check_segment_for_agent(segment, agent): - context.set_binding(segment[api.ID], - self.vif_type, - self.cap_port_filter) + if self.try_to_bind_segment_for_agent(context, segment, + agent): LOG.debug(_("Bound using segment: %s"), segment) return else: @@ -99,6 +95,25 @@ class AgentMechanismDriverBase(api.MechanismDriver): {'port': context.current['id'], 'network': context.network.current['id']}) + @abstractmethod + def try_to_bind_segment_for_agent(self, context, segment, agent): + """Try to bind with segment for agent. + + :param context: PortContext instance describing the port + :param segment: segment dictionary describing segment to bind + :param agent: agents_db entry describing agent to bind + :returns: True iff segment has been bound for agent + + Called inside transaction during bind_port() so that derived + MechanismDrivers can use agent_db data along with built-in + knowledge of the corresponding agent's capabilities to attempt + to bind to the specified network segment for the agent. + + If the segment can be bound for the agent, this function must + call context.set_binding() with appropriate values and then + return True. Otherwise, it must return False. + """ + @abstractmethod def check_segment_for_agent(self, segment, agent): """Check if segment can be bound for agent. @@ -107,9 +122,49 @@ class AgentMechanismDriverBase(api.MechanismDriver): :param agent: agents_db entry describing agent to bind :returns: True iff segment can be bound for agent - Called inside transaction during bind_port() and - validate_port_binding() so that derived MechanismDrivers can - use agent_db data along with built-in knowledge of the - corresponding agent's capabilities to determine whether or not - the specified network segment can be bound for the agent. + Called inside transaction during validate_port_binding() so + that derived MechanismDrivers can use agent_db data along with + built-in knowledge of the corresponding agent's capabilities + to determine whether or not the specified network segment can + be bound for the agent. """ + + +@six.add_metaclass(ABCMeta) +class SimpleAgentMechanismDriverBase(AgentMechanismDriverBase): + """Base class for simple drivers using an L2 agent. + + The SimpleAgentMechanismDriverBase provides common code for + mechanism drivers that integrate the ml2 plugin with L2 agents, + where the binding:vif_type and binding:vif_details values are the + same for all bindings. Port binding with this driver requires the + driver's associated agent to be running on the port's host, and + that agent to have connectivity to at least one segment of the + port's network. + + MechanismDrivers using this base class must pass the agent type + and the values for binding:vif_type and binding:vif_details to + __init__(). They must implement check_segment_for_agent() as + defined in AgentMechanismDriverBase, which will be called during + both binding establishment and validation. + """ + + def __init__(self, agent_type, vif_type, vif_details, + supported_vnic_types=[portbindings.VNIC_NORMAL]): + """Initialize base class for specific L2 agent type. + + :param agent_type: Constant identifying agent type in agents_db + :param vif_type: Value for binding:vif_type when bound + :param vif_details: Dictionary with details for VIF driver when bound + :param supported_vnic_types: The binding:vnic_type values we can bind + """ + super(SimpleAgentMechanismDriverBase, self).__init__( + agent_type, supported_vnic_types) + self.vif_type = vif_type + self.vif_details = vif_details + + def try_to_bind_segment_for_agent(self, context, segment, agent): + if self.check_segment_for_agent(segment, agent): + context.set_binding(segment[api.ID], + self.vif_type, + self.vif_details) diff --git a/neutron/plugins/ml2/drivers/mech_hyperv.py b/neutron/plugins/ml2/drivers/mech_hyperv.py index 87cf6c22c4..b384d3425e 100644 --- a/neutron/plugins/ml2/drivers/mech_hyperv.py +++ b/neutron/plugins/ml2/drivers/mech_hyperv.py @@ -24,7 +24,7 @@ from neutron.plugins.ml2.drivers import mech_agent LOG = log.getLogger(__name__) -class HypervMechanismDriver(mech_agent.AgentMechanismDriverBase): +class HypervMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Attach to networks using hyperv L2 agent. The HypervMechanismDriver integrates the ml2 plugin with the @@ -37,7 +37,7 @@ class HypervMechanismDriver(mech_agent.AgentMechanismDriverBase): super(HypervMechanismDriver, self).__init__( constants.AGENT_TYPE_HYPERV, portbindings.VIF_TYPE_HYPERV, - False) + {portbindings.CAP_PORT_FILTER: False}) def check_segment_for_agent(self, segment, agent): mappings = agent['configurations'].get('vswitch_mappings', {}) diff --git a/neutron/plugins/ml2/drivers/mech_linuxbridge.py b/neutron/plugins/ml2/drivers/mech_linuxbridge.py index 233737f46a..b304ad4bab 100644 --- a/neutron/plugins/ml2/drivers/mech_linuxbridge.py +++ b/neutron/plugins/ml2/drivers/mech_linuxbridge.py @@ -22,7 +22,7 @@ from neutron.plugins.ml2.drivers import mech_agent LOG = log.getLogger(__name__) -class LinuxbridgeMechanismDriver(mech_agent.AgentMechanismDriverBase): +class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Attach to networks using linuxbridge L2 agent. The LinuxbridgeMechanismDriver integrates the ml2 plugin with the @@ -36,7 +36,7 @@ class LinuxbridgeMechanismDriver(mech_agent.AgentMechanismDriverBase): super(LinuxbridgeMechanismDriver, self).__init__( constants.AGENT_TYPE_LINUXBRIDGE, portbindings.VIF_TYPE_BRIDGE, - True) + {portbindings.CAP_PORT_FILTER: True}) def check_segment_for_agent(self, segment, agent): mappings = agent['configurations'].get('interface_mappings', {}) diff --git a/neutron/plugins/ml2/drivers/mech_openvswitch.py b/neutron/plugins/ml2/drivers/mech_openvswitch.py index f98ddf2886..b070bb9e7c 100644 --- a/neutron/plugins/ml2/drivers/mech_openvswitch.py +++ b/neutron/plugins/ml2/drivers/mech_openvswitch.py @@ -22,7 +22,7 @@ from neutron.plugins.ml2.drivers import mech_agent LOG = log.getLogger(__name__) -class OpenvswitchMechanismDriver(mech_agent.AgentMechanismDriverBase): +class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Attach to networks using openvswitch L2 agent. The OpenvswitchMechanismDriver integrates the ml2 plugin with the @@ -36,7 +36,7 @@ class OpenvswitchMechanismDriver(mech_agent.AgentMechanismDriverBase): super(OpenvswitchMechanismDriver, self).__init__( constants.AGENT_TYPE_OVS, portbindings.VIF_TYPE_OVS, - True) + {portbindings.CAP_PORT_FILTER: True}) def check_segment_for_agent(self, segment, agent): mappings = agent['configurations'].get('bridge_mappings', {}) diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index 424f9f5d09..516beddb44 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -450,14 +450,14 @@ class MechanismManager(stevedore.named.NamedExtensionManager): LOG.debug(_("Bound port: %(port)s, host: %(host)s, " "vnic_type: %(vnic_type)s, " "driver: %(driver)s, vif_type: %(vif_type)s, " - "cap_port_filter: %(cap_port_filter)s, " + "vif_details: %(vif_details)s, " "segment: %(segment)s"), {'port': context._port['id'], 'host': binding.host, + 'vnic_type': binding.vnic_type, 'driver': binding.driver, 'vif_type': binding.vif_type, - 'vnic_type': binding.vnic_type, - 'cap_port_filter': binding.cap_port_filter, + 'vif_details': binding.vif_details, 'segment': binding.segment}) return except Exception: @@ -509,6 +509,6 @@ class MechanismManager(stevedore.named.NamedExtensionManager): "unbind_port"), driver.name) binding.vif_type = portbindings.VIF_TYPE_UNBOUND - binding.cap_port_filter = False + binding.vif_details = '' binding.driver = None binding.segment = None diff --git a/neutron/plugins/ml2/models.py b/neutron/plugins/ml2/models.py index f17fa1cdb5..26aa11cff2 100644 --- a/neutron/plugins/ml2/models.py +++ b/neutron/plugins/ml2/models.py @@ -57,7 +57,7 @@ class PortBinding(model_base.BASEV2): vnic_type = sa.Column(sa.String(64), nullable=False, default=portbindings.VNIC_NORMAL) vif_type = sa.Column(sa.String(64), nullable=False) - cap_port_filter = sa.Column(sa.Boolean, nullable=False) + vif_details = sa.Column(sa.String(4095), nullable=False, default='') driver = sa.Column(sa.String(64)) segment = sa.Column(sa.String(36), sa.ForeignKey('ml2_network_segments.id', diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 67d788548b..ddb5d62943 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -40,6 +40,7 @@ from neutron import manager from neutron.openstack.common import db as os_db from neutron.openstack.common import excutils from neutron.openstack.common import importutils +from neutron.openstack.common import jsonutils from neutron.openstack.common import log from neutron.openstack.common import rpc as c_rpc from neutron.plugins.common import constants as service_constants @@ -242,8 +243,18 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, port[portbindings.HOST_ID] = binding.host port[portbindings.VNIC_TYPE] = binding.vnic_type port[portbindings.VIF_TYPE] = binding.vif_type - port[portbindings.CAPABILITIES] = { - portbindings.CAP_PORT_FILTER: binding.cap_port_filter} + port[portbindings.VIF_DETAILS] = self._get_vif_details(binding) + + def _get_vif_details(self, binding): + if binding.vif_details: + try: + return jsonutils.loads(binding.vif_details) + except Exception: + LOG.error(_("Serialized vif_details DB value '%(value)s' " + "for port %(port)s is invalid"), + {'value': binding.vif_details, + 'port': binding.port_id}) + return {} def _delete_port_binding(self, mech_context): binding = mech_context._binding diff --git a/neutron/plugins/mlnx/mlnx_plugin.py b/neutron/plugins/mlnx/mlnx_plugin.py index cee2e4b3d8..28ceac7f28 100644 --- a/neutron/plugins/mlnx/mlnx_plugin.py +++ b/neutron/plugins/mlnx/mlnx_plugin.py @@ -102,7 +102,8 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2, self.vnic_type = cfg.CONF.ESWITCH.vnic_type self.base_binding_dict = { portbindings.VIF_TYPE: self.vnic_type, - portbindings.CAPABILITIES: { + portbindings.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} self._setup_rpc() diff --git a/neutron/plugins/nec/nec_plugin.py b/neutron/plugins/nec/nec_plugin.py index 1b04af5307..4f8dcad184 100644 --- a/neutron/plugins/nec/nec_plugin.py +++ b/neutron/plugins/nec/nec_plugin.py @@ -381,7 +381,8 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2, def _get_base_binding_dict(self): binding = { portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, - portbindings.CAPABILITIES: { + portbindings.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} return binding diff --git a/neutron/plugins/nicira/NeutronPlugin.py b/neutron/plugins/nicira/NeutronPlugin.py index a6f145909f..f047624919 100644 --- a/neutron/plugins/nicira/NeutronPlugin.py +++ b/neutron/plugins/nicira/NeutronPlugin.py @@ -183,7 +183,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, self.base_binding_dict = { pbin.VIF_TYPE: pbin.VIF_TYPE_OVS, - pbin.CAPABILITIES: { + pbin.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details pbin.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} diff --git a/neutron/plugins/openvswitch/ovs_neutron_plugin.py b/neutron/plugins/openvswitch/ovs_neutron_plugin.py index fde00edeb1..55b7827b9f 100644 --- a/neutron/plugins/openvswitch/ovs_neutron_plugin.py +++ b/neutron/plugins/openvswitch/ovs_neutron_plugin.py @@ -297,7 +297,8 @@ class OVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, super(OVSNeutronPluginV2, self).__init__() self.base_binding_dict = { portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, - portbindings.CAPABILITIES: { + portbindings.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} self._parse_network_vlan_ranges() diff --git a/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py b/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py index 1f8f99c0d9..664ca8003b 100644 --- a/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py +++ b/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py @@ -545,7 +545,8 @@ class NeutronPluginPLUMgridV2(db_base_plugin_v2.NeutronDbPluginV2, def _port_viftype_binding(self, context, port): port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_IOVISOR - port[portbindings.CAPABILITIES] = { + port[portbindings.VIF_DETAILS] = { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases} return port diff --git a/neutron/plugins/ryu/ryu_neutron_plugin.py b/neutron/plugins/ryu/ryu_neutron_plugin.py index c81b2069f0..01ca69996a 100644 --- a/neutron/plugins/ryu/ryu_neutron_plugin.py +++ b/neutron/plugins/ryu/ryu_neutron_plugin.py @@ -112,7 +112,8 @@ class RyuNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, super(RyuNeutronPluginV2, self).__init__() self.base_binding_dict = { portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS, - portbindings.CAPABILITIES: { + portbindings.VIF_DETAILS: { + # TODO(rkukura): Replace with new VIF security details portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases}} portbindings_base.register_port_dict_function() diff --git a/neutron/policy.py b/neutron/policy.py index 6906373c98..fbd43fd0f6 100644 --- a/neutron/policy.py +++ b/neutron/policy.py @@ -46,7 +46,7 @@ DEPRECATED_POLICY_MAP = { 'extension:router': ['network:router:external'], 'extension:port_binding': - ['port:binding:vif_type', 'port:binding:capabilities', + ['port:binding:vif_type', 'port:binding:vif_details', 'port:binding:profile', 'port:binding:host_id'] } DEPRECATED_ACTION_MAP = { diff --git a/neutron/tests/unit/_test_extension_portbindings.py b/neutron/tests/unit/_test_extension_portbindings.py index 5e52629a57..4852d56530 100644 --- a/neutron/tests/unit/_test_extension_portbindings.py +++ b/neutron/tests/unit/_test_extension_portbindings.py @@ -39,15 +39,19 @@ class PortBindingsTestCase(test_db_plugin.NeutronDbPluginV2TestCase): HAS_PORT_FILTER = False def _check_response_portbindings(self, port): - self.assertEqual(port['binding:vif_type'], self.VIF_TYPE) - port_cap = port[portbindings.CAPABILITIES] - self.assertEqual(port_cap[portbindings.CAP_PORT_FILTER], - self.HAS_PORT_FILTER) + self.assertEqual(port[portbindings.VIF_TYPE], self.VIF_TYPE) + vif_details = port[portbindings.VIF_DETAILS] + # REVISIT(rkukura): Consider reworking tests to enable ML2 to bind + if self.VIF_TYPE not in [portbindings.VIF_TYPE_UNBOUND, + portbindings.VIF_TYPE_BINDING_FAILED]: + # TODO(rkukura): Replace with new VIF security details + self.assertEqual(vif_details[portbindings.CAP_PORT_FILTER], + self.HAS_PORT_FILTER) def _check_response_no_portbindings(self, port): self.assertIn('status', port) self.assertNotIn(portbindings.VIF_TYPE, port) - self.assertNotIn(portbindings.CAPABILITIES, port) + self.assertNotIn(portbindings.VIF_DETAILS, port) def _get_non_admin_context(self): return context.Context(user_id=None, diff --git a/neutron/tests/unit/ml2/_test_mech_agent.py b/neutron/tests/unit/ml2/_test_mech_agent.py index 876cb1d754..0714b58907 100644 --- a/neutron/tests/unit/ml2/_test_mech_agent.py +++ b/neutron/tests/unit/ml2/_test_mech_agent.py @@ -46,8 +46,7 @@ class FakePortContext(api.PortContext): self._network_context = FakeNetworkContext(segments) self._bound_segment_id = None self._bound_vif_type = None - self._bound_vnic_type = portbindings.VNIC_NORMAL - self._bound_cap_port_filter = None + self._bound_vif_details = None @property def current(self): @@ -74,10 +73,10 @@ class FakePortContext(api.PortContext): else: return [] - def set_binding(self, segment_id, vif_type, cap_port_filter): + def set_binding(self, segment_id, vif_type, vif_details): self._bound_segment_id = segment_id self._bound_vif_type = vif_type - self._bound_cap_port_filter = cap_port_filter + self._bound_vif_details = vif_details class AgentMechanismBaseTestCase(base.BaseTestCase): @@ -93,12 +92,15 @@ class AgentMechanismBaseTestCase(base.BaseTestCase): def _check_unbound(self, context): self.assertIsNone(context._bound_segment_id) self.assertIsNone(context._bound_vif_type) - self.assertIsNone(context._bound_cap_port_filter) + self.assertIsNone(context._bound_vif_details) def _check_bound(self, context, segment): self.assertEqual(context._bound_segment_id, segment[api.ID]) self.assertEqual(context._bound_vif_type, self.VIF_TYPE) - self.assertEqual(context._bound_cap_port_filter, self.CAP_PORT_FILTER) + vif_details = context._bound_vif_details + self.assertIsNotNone(vif_details) + self.assertEqual(vif_details[portbindings.CAP_PORT_FILTER], + self.CAP_PORT_FILTER) class AgentMechanismGenericTestCase(AgentMechanismBaseTestCase): diff --git a/neutron/tests/unit/ml2/drivers/mechanism_test.py b/neutron/tests/unit/ml2/drivers/mechanism_test.py index 24a4055045..0b3d98af71 100644 --- a/neutron/tests/unit/ml2/drivers/mechanism_test.py +++ b/neutron/tests/unit/ml2/drivers/mechanism_test.py @@ -116,9 +116,11 @@ class TestMechanismDriver(api.MechanismDriver): host = context.current.get(portbindings.HOST_ID, None) segment = context.network.network_segments[0][api.ID] if host == "host-ovs-no_filter": - context.set_binding(segment, portbindings.VIF_TYPE_OVS, False) + context.set_binding(segment, portbindings.VIF_TYPE_OVS, + {portbindings.CAP_PORT_FILTER: False}) elif host == "host-bridge-filter": - context.set_binding(segment, portbindings.VIF_TYPE_BRIDGE, True) + context.set_binding(segment, portbindings.VIF_TYPE_BRIDGE, + {portbindings.CAP_PORT_FILTER: True}) def validate_port_binding(self, context): self._check_port_context(context, False) diff --git a/neutron/tests/unit/ml2/test_port_binding.py b/neutron/tests/unit/ml2/test_port_binding.py index c9f784f26c..86e066892d 100644 --- a/neutron/tests/unit/ml2/test_port_binding.py +++ b/neutron/tests/unit/ml2/test_port_binding.py @@ -41,17 +41,20 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase): self.port_create_status = 'DOWN' self.plugin = manager.NeutronManager.get_plugin() - def _check_response(self, port, vif_type, has_port_filter): - self.assertEqual(port['binding:vif_type'], vif_type) - port_cap = port[portbindings.CAPABILITIES] - self.assertEqual(port_cap[portbindings.CAP_PORT_FILTER], - has_port_filter) + def _check_response(self, port, vif_type, has_port_filter, bound): + self.assertEqual(port[portbindings.VIF_TYPE], vif_type) + vif_details = port[portbindings.VIF_DETAILS] + if bound: + # TODO(rkukura): Replace with new VIF security details + self.assertEqual(vif_details[portbindings.CAP_PORT_FILTER], + has_port_filter) def _test_port_binding(self, host, vif_type, has_port_filter, bound): host_arg = {portbindings.HOST_ID: host} with self.port(name='name', arg_list=(portbindings.HOST_ID,), **host_arg) as port: - self._check_response(port['port'], vif_type, has_port_filter) + self._check_response(port['port'], vif_type, has_port_filter, + bound) port_id = port['port']['id'] details = self.plugin.callbacks.get_device_details( None, agent_id="theAgentId", device=port_id) @@ -95,9 +98,10 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase): neutron_context=neutron_context) port_data = updated_port['port'] if new_host is not None: - self.assertEqual(port_data['binding:host_id'], new_host) + self.assertEqual(port_data[portbindings.HOST_ID], + new_host) else: - self.assertEqual(port_data['binding:host_id'], host) + self.assertEqual(port_data[portbindings.HOST_ID], host) if new_host is not None and new_host != host: notify_mock.assert_called_once_with(mock.ANY) else: