Merge "ML2 binding:profile port attribute"
This commit is contained in:
commit
ee0788b9b3
@ -0,0 +1,55 @@
|
|||||||
|
# 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:profile
|
||||||
|
|
||||||
|
Revision ID: 157a5d299379
|
||||||
|
Revises: 50d5ba354c23
|
||||||
|
Create Date: 2014-02-13 23:48:25.147279
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '157a5d299379'
|
||||||
|
down_revision = '50d5ba354c23'
|
||||||
|
|
||||||
|
# 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('profile', sa.String(length=4095),
|
||||||
|
nullable=False, server_default=''))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(active_plugins=None, options=None):
|
||||||
|
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||||
|
return
|
||||||
|
|
||||||
|
op.drop_column('ml2_port_bindings', 'profile')
|
@ -65,7 +65,6 @@ def ensure_port_binding(session, port_id):
|
|||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
record = models.PortBinding(
|
record = models.PortBinding(
|
||||||
port_id=port_id,
|
port_id=port_id,
|
||||||
host='',
|
|
||||||
vif_type=portbindings.VIF_TYPE_UNBOUND)
|
vif_type=portbindings.VIF_TYPE_UNBOUND)
|
||||||
session.add(record)
|
session.add(record)
|
||||||
return record
|
return record
|
||||||
|
@ -438,10 +438,11 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
|
|||||||
"""
|
"""
|
||||||
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"),
|
"for vnic_type %(vnic_type)s with profile %(profile)s"),
|
||||||
{'port': context._port['id'],
|
{'port': context._port['id'],
|
||||||
'host': binding.host,
|
'host': binding.host,
|
||||||
'vnic_type': binding.vnic_type})
|
'vnic_type': binding.vnic_type,
|
||||||
|
'profile': binding.profile})
|
||||||
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)
|
||||||
@ -449,12 +450,14 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
|
|||||||
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, "
|
"vnic_type: %(vnic_type)s, "
|
||||||
|
"profile: %(profile)s"
|
||||||
"driver: %(driver)s, vif_type: %(vif_type)s, "
|
"driver: %(driver)s, vif_type: %(vif_type)s, "
|
||||||
"vif_details: %(vif_details)s, "
|
"vif_details: %(vif_details)s, "
|
||||||
"segment: %(segment)s"),
|
"segment: %(segment)s"),
|
||||||
{'port': context._port['id'],
|
{'port': context._port['id'],
|
||||||
'host': binding.host,
|
'host': binding.host,
|
||||||
'vnic_type': binding.vnic_type,
|
'vnic_type': binding.vnic_type,
|
||||||
|
'profile': binding.profile,
|
||||||
'driver': binding.driver,
|
'driver': binding.driver,
|
||||||
'vif_type': binding.vif_type,
|
'vif_type': binding.vif_type,
|
||||||
'vif_details': binding.vif_details,
|
'vif_details': binding.vif_details,
|
||||||
|
@ -20,6 +20,8 @@ from neutron.db import model_base
|
|||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
from neutron.extensions import portbindings
|
from neutron.extensions import portbindings
|
||||||
|
|
||||||
|
BINDING_PROFILE_LEN = 4095
|
||||||
|
|
||||||
|
|
||||||
class NetworkSegment(model_base.BASEV2, models_v2.HasId):
|
class NetworkSegment(model_base.BASEV2, models_v2.HasId):
|
||||||
"""Represent persistent state of a network segment.
|
"""Represent persistent state of a network segment.
|
||||||
@ -53,9 +55,11 @@ class PortBinding(model_base.BASEV2):
|
|||||||
port_id = sa.Column(sa.String(36),
|
port_id = sa.Column(sa.String(36),
|
||||||
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, default='')
|
||||||
vnic_type = sa.Column(sa.String(64), nullable=False,
|
vnic_type = sa.Column(sa.String(64), nullable=False,
|
||||||
default=portbindings.VNIC_NORMAL)
|
default=portbindings.VNIC_NORMAL)
|
||||||
|
profile = sa.Column(sa.String(BINDING_PROFILE_LEN), nullable=False,
|
||||||
|
default='')
|
||||||
vif_type = sa.Column(sa.String(64), nullable=False)
|
vif_type = sa.Column(sa.String(64), nullable=False)
|
||||||
vif_details = sa.Column(sa.String(4095), nullable=False, default='')
|
vif_details = sa.Column(sa.String(4095), nullable=False, default='')
|
||||||
driver = sa.Column(sa.String(64))
|
driver = sa.Column(sa.String(64))
|
||||||
|
@ -207,24 +207,36 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
binding = mech_context._binding
|
binding = mech_context._binding
|
||||||
port = mech_context.current
|
port = mech_context.current
|
||||||
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 = attrs and attrs.get(portbindings.VNIC_TYPE)
|
||||||
vnic_type_set = attributes.is_attr_set(vnic_type)
|
vnic_type_set = attributes.is_attr_set(vnic_type)
|
||||||
|
|
||||||
|
# CLI can't send {}, so treat None as {}
|
||||||
|
profile = attrs and attrs.get(portbindings.PROFILE)
|
||||||
|
profile_set = profile is not attributes.ATTR_NOT_SPECIFIED
|
||||||
|
if profile_set and not profile:
|
||||||
|
profile = {}
|
||||||
|
|
||||||
if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
|
if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
|
||||||
if (not host_set and not vnic_type_set and binding.segment and
|
if (not host_set and not vnic_type_set and not profile_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 or vnic_type was specified that
|
# This will happen if a new host, vnic_type, or profile was specified
|
||||||
# differs from the current one. Note that host_set is True
|
# that 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) or
|
ret_value = ((host_set and binding.get('host') != host) or
|
||||||
(vnic_type_set and binding.get('vnic_type') != vnic_type))
|
(vnic_type_set and
|
||||||
|
binding.get('vnic_type') != vnic_type) or
|
||||||
|
(profile_set and self._get_profile(binding) != profile))
|
||||||
|
|
||||||
if host_set:
|
if host_set:
|
||||||
binding.host = host
|
binding.host = host
|
||||||
port[portbindings.HOST_ID] = host
|
port[portbindings.HOST_ID] = host
|
||||||
@ -233,6 +245,14 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
binding.vnic_type = vnic_type
|
binding.vnic_type = vnic_type
|
||||||
port[portbindings.VNIC_TYPE] = vnic_type
|
port[portbindings.VNIC_TYPE] = vnic_type
|
||||||
|
|
||||||
|
if profile_set:
|
||||||
|
binding.profile = jsonutils.dumps(profile)
|
||||||
|
if len(binding.profile) > models.BINDING_PROFILE_LEN:
|
||||||
|
msg = _("binding:profile value too large")
|
||||||
|
raise exc.InvalidInput(error_message=msg)
|
||||||
|
port[portbindings.PROFILE] = profile
|
||||||
|
|
||||||
|
# To try to [re]bind if host is non-empty.
|
||||||
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)
|
||||||
@ -242,6 +262,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.VNIC_TYPE] = binding.vnic_type
|
||||||
|
port[portbindings.PROFILE] = self._get_profile(binding)
|
||||||
port[portbindings.VIF_TYPE] = binding.vif_type
|
port[portbindings.VIF_TYPE] = binding.vif_type
|
||||||
port[portbindings.VIF_DETAILS] = self._get_vif_details(binding)
|
port[portbindings.VIF_DETAILS] = self._get_vif_details(binding)
|
||||||
|
|
||||||
@ -256,6 +277,17 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
'port': binding.port_id})
|
'port': binding.port_id})
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def _get_profile(self, binding):
|
||||||
|
if binding.profile:
|
||||||
|
try:
|
||||||
|
return jsonutils.loads(binding.profile)
|
||||||
|
except Exception:
|
||||||
|
LOG.error(_("Serialized profile DB value '%(value)s' for "
|
||||||
|
"port %(port)s is invalid"),
|
||||||
|
{'value': binding.profile,
|
||||||
|
'port': binding.port_id})
|
||||||
|
return {}
|
||||||
|
|
||||||
def _delete_port_binding(self, mech_context):
|
def _delete_port_binding(self, mech_context):
|
||||||
binding = mech_context._binding
|
binding = mech_context._binding
|
||||||
port = mech_context.current
|
port = mech_context.current
|
||||||
|
@ -90,7 +90,7 @@ class PortBindingsTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
for non_admin_port in ports:
|
for non_admin_port in ports:
|
||||||
self._check_response_no_portbindings(non_admin_port)
|
self._check_response_no_portbindings(non_admin_port)
|
||||||
|
|
||||||
def _check_default_port_binding_profile(self, port):
|
def _check_port_binding_profile(self, port, profile=None):
|
||||||
# For plugins which does not use binding:profile attr
|
# For plugins which does not use binding:profile attr
|
||||||
# we just check an operation for the port succeed.
|
# we just check an operation for the port succeed.
|
||||||
self.assertIn('id', port)
|
self.assertIn('id', port)
|
||||||
@ -99,7 +99,10 @@ class PortBindingsTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
profile_arg = {portbindings.PROFILE: profile}
|
profile_arg = {portbindings.PROFILE: profile}
|
||||||
with self.port(arg_list=(portbindings.PROFILE,),
|
with self.port(arg_list=(portbindings.PROFILE,),
|
||||||
**profile_arg) as port:
|
**profile_arg) as port:
|
||||||
self._check_default_port_binding_profile(port['port'])
|
port_id = port['port']['id']
|
||||||
|
self._check_port_binding_profile(port['port'], profile)
|
||||||
|
port = self._show('ports', port_id)
|
||||||
|
self._check_port_binding_profile(port['port'], profile)
|
||||||
|
|
||||||
def test_create_port_binding_profile_none(self):
|
def test_create_port_binding_profile_none(self):
|
||||||
self._test_create_port_binding_profile(None)
|
self._test_create_port_binding_profile(None)
|
||||||
@ -111,12 +114,14 @@ class PortBindingsTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
profile_arg = {portbindings.PROFILE: profile}
|
profile_arg = {portbindings.PROFILE: profile}
|
||||||
with self.port() as port:
|
with self.port() as port:
|
||||||
# print "(1) %s" % port
|
# print "(1) %s" % port
|
||||||
self._check_default_port_binding_profile(port['port'])
|
self._check_port_binding_profile(port['port'])
|
||||||
port_id = port['port']['id']
|
port_id = port['port']['id']
|
||||||
ctx = context.get_admin_context()
|
ctx = context.get_admin_context()
|
||||||
port = self._update('ports', port_id, {'port': profile_arg},
|
port = self._update('ports', port_id, {'port': profile_arg},
|
||||||
neutron_context=ctx)['port']
|
neutron_context=ctx)['port']
|
||||||
self._check_default_port_binding_profile(port)
|
self._check_port_binding_profile(port, profile)
|
||||||
|
port = self._show('ports', port_id)['port']
|
||||||
|
self._check_port_binding_profile(port, profile)
|
||||||
|
|
||||||
def test_update_port_binding_profile_none(self):
|
def test_update_port_binding_profile_none(self):
|
||||||
self._test_update_port_binding_profile(None)
|
self._test_update_port_binding_profile(None)
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
import testtools
|
import testtools
|
||||||
|
import webob
|
||||||
|
|
||||||
from neutron.common import exceptions as exc
|
from neutron.common import exceptions as exc
|
||||||
from neutron import context
|
from neutron import context
|
||||||
@ -130,6 +131,43 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
|
|||||||
test_sg_rpc.set_firewall_driver(self.FIREWALL_DRIVER)
|
test_sg_rpc.set_firewall_driver(self.FIREWALL_DRIVER)
|
||||||
super(TestMl2PortBinding, self).setUp()
|
super(TestMl2PortBinding, self).setUp()
|
||||||
|
|
||||||
|
def _check_port_binding_profile(self, port, profile=None):
|
||||||
|
self.assertIn('id', port)
|
||||||
|
self.assertIn(portbindings.PROFILE, port)
|
||||||
|
value = port[portbindings.PROFILE]
|
||||||
|
self.assertEqual(profile or {}, value)
|
||||||
|
|
||||||
|
def test_create_port_binding_profile(self):
|
||||||
|
self._test_create_port_binding_profile({'a': 1, 'b': 2})
|
||||||
|
|
||||||
|
def test_update_port_binding_profile(self):
|
||||||
|
self._test_update_port_binding_profile({'c': 3})
|
||||||
|
|
||||||
|
def test_create_port_binding_profile_too_big(self):
|
||||||
|
s = 'x' * 5000
|
||||||
|
profile_arg = {portbindings.PROFILE: {'d': s}}
|
||||||
|
try:
|
||||||
|
with self.port(expected_res_status=400,
|
||||||
|
arg_list=(portbindings.PROFILE,),
|
||||||
|
**profile_arg):
|
||||||
|
pass
|
||||||
|
except webob.exc.HTTPClientError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_remove_port_binding_profile(self):
|
||||||
|
profile = {'e': 5}
|
||||||
|
profile_arg = {portbindings.PROFILE: profile}
|
||||||
|
with self.port(arg_list=(portbindings.PROFILE,),
|
||||||
|
**profile_arg) as port:
|
||||||
|
self._check_port_binding_profile(port['port'], profile)
|
||||||
|
port_id = port['port']['id']
|
||||||
|
profile_arg = {portbindings.PROFILE: None}
|
||||||
|
port = self._update('ports', port_id,
|
||||||
|
{'port': profile_arg})['port']
|
||||||
|
self._check_port_binding_profile(port)
|
||||||
|
port = self._show('ports', port_id)['port']
|
||||||
|
self._check_port_binding_profile(port)
|
||||||
|
|
||||||
|
|
||||||
class TestMl2PortBindingNoSG(TestMl2PortBinding):
|
class TestMl2PortBindingNoSG(TestMl2PortBinding):
|
||||||
HAS_PORT_FILTER = False
|
HAS_PORT_FILTER = False
|
||||||
|
Loading…
Reference in New Issue
Block a user