Set bridge_name in OVS trunk port's vif_details
This patch follows up on the Nova work that allows Neutron to pass an OVS 'bridge_name' in a port's vif_details [1] and the os-vif work that ensures the OVS bridge passed in as 'bridge_name' (or the config default of 'br-int') is created [2]. If the port is a trunk's parent port, then bridge_name will be set to a generated trunk bridge name for vlan-aware VMs. Otherwise, the bridge_name field will remain empty. Note: until Nova integrates os-vif [3] and uses a release that contains the change in [2], the trunk bridge will not actually be created by Nova/os-vif. It is expected to be integrated for the Newton release. Until then, this Nova-equivalent patch can be used for testing purposes [4]. [1] https://review.openstack.org/#/c/260700/ [2] https://review.openstack.org/#/c/330818/ [3] https://review.openstack.org/#/c/269672/ [4] https://review.openstack.org/#/c/332474/ Partially-implements: blueprint vlan-aware-vms Change-Id: Iad72d163bc406df65866f6d962abcb78596828e7
This commit is contained in:
@@ -50,6 +50,7 @@ VIF_DETAILS_VLAN = 'vlan'
|
||||
VIF_DETAILS_MACVTAP_SOURCE = 'macvtap_source'
|
||||
VIF_DETAILS_MACVTAP_MODE = 'macvtap_mode'
|
||||
VIF_DETAILS_PHYSICAL_INTERFACE = 'physical_interface'
|
||||
VIF_DETAILS_BRIDGE_NAME = 'bridge_name'
|
||||
|
||||
# The keys below are used in the VIF_DETAILS attribute to convey
|
||||
# information related to the configuration of the vhost-user VIF driver.
|
||||
|
||||
@@ -142,3 +142,6 @@ OPENFLOW14 = "OpenFlow14"
|
||||
|
||||
# A placeholder for dead vlans.
|
||||
DEAD_VLAN_TAG = p_const.MAX_VLAN_TAG + 1
|
||||
|
||||
# callback resource for setting 'bridge_name' in the 'binding:vif_details'
|
||||
OVS_BRIDGE_NAME = 'ovs_bridge_name'
|
||||
|
||||
@@ -19,6 +19,8 @@ from neutron_lib import constants
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.agent import securitygroups_rpc
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import registry
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.plugins.common import constants as p_constants
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
@@ -86,6 +88,24 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
||||
return self.vif_type
|
||||
|
||||
def get_vif_details(self, agent, context):
|
||||
vif_details = self._pre_get_vif_details(agent, context)
|
||||
self._set_bridge_name(context.current, vif_details)
|
||||
return vif_details
|
||||
|
||||
@staticmethod
|
||||
def _set_bridge_name(port, vif_details):
|
||||
# REVISIT(rawlin): add BridgeName as a nullable column to the Port
|
||||
# model and simply check here if it's set and insert it into the
|
||||
# vif_details.
|
||||
|
||||
def set_bridge_name_inner(bridge_name):
|
||||
vif_details[portbindings.VIF_DETAILS_BRIDGE_NAME] = bridge_name
|
||||
|
||||
registry.notify(
|
||||
a_const.OVS_BRIDGE_NAME, events.BEFORE_READ,
|
||||
set_bridge_name_inner, port=port)
|
||||
|
||||
def _pre_get_vif_details(self, agent, context):
|
||||
a_config = agent['configurations']
|
||||
if a_config.get('datapath_type') != a_const.OVS_DATAPATH_NETDEV:
|
||||
details = dict(self.vif_details)
|
||||
|
||||
15
neutron/services/trunk/drivers/openvswitch/constants.py
Normal file
15
neutron/services/trunk/drivers/openvswitch/constants.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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.
|
||||
|
||||
TRUNK_BR_PREFIX = 'tbr-'
|
||||
@@ -15,9 +15,14 @@ from neutron_lib import constants
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import registry
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
|
||||
constants as agent_consts)
|
||||
from neutron.services.trunk import constants as trunk_consts
|
||||
from neutron.services.trunk.drivers import base
|
||||
from neutron.services.trunk import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@@ -53,4 +58,15 @@ def register():
|
||||
"""Register the driver."""
|
||||
global DRIVER
|
||||
DRIVER = OVSDriver.create()
|
||||
# To set the bridge_name in a parent port's vif_details.
|
||||
registry.subscribe(vif_details_bridge_name_handler,
|
||||
agent_consts.OVS_BRIDGE_NAME,
|
||||
events.BEFORE_READ)
|
||||
LOG.debug('Open vSwitch trunk driver registered')
|
||||
|
||||
|
||||
def vif_details_bridge_name_handler(resource, event, set_br_name, **kwargs):
|
||||
"""If port is a trunk port, generate a bridge_name for its vif_details."""
|
||||
port = kwargs['port']
|
||||
if 'trunk_details' in port:
|
||||
set_br_name(utils.gen_trunk_br_name(port['trunk_details']['trunk_id']))
|
||||
|
||||
22
neutron/services/trunk/utils.py
Normal file
22
neutron/services/trunk/utils.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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.
|
||||
|
||||
from neutron_lib import constants
|
||||
|
||||
from neutron.services.trunk.drivers.openvswitch import constants as ovs_const
|
||||
|
||||
|
||||
def gen_trunk_br_name(trunk_id):
|
||||
return ((ovs_const.TRUNK_BR_PREFIX + trunk_id)
|
||||
[:constants.DEVICE_NAME_MAX_LEN - 1])
|
||||
0
neutron/tests/functional/services/trunk/__init__.py
Normal file
0
neutron/tests/functional/services/trunk/__init__.py
Normal file
51
neutron/tests/functional/services/trunk/test_plugin.py
Normal file
51
neutron/tests/functional/services/trunk/test_plugin.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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.
|
||||
|
||||
from neutron.extensions import portbindings as pb
|
||||
from neutron.services.trunk import plugin as trunk_plugin
|
||||
from neutron.services.trunk import utils as trunk_utils
|
||||
from neutron.tests.common import helpers
|
||||
from neutron.tests.unit.plugins.ml2 import base as ml2_test_base
|
||||
|
||||
|
||||
class TestTrunkServicePlugin(ml2_test_base.ML2TestFramework):
|
||||
|
||||
def setUp(self):
|
||||
super(TestTrunkServicePlugin, self).setUp()
|
||||
self.trunk_plugin = trunk_plugin.TrunkPlugin()
|
||||
|
||||
def test_ovs_bridge_name_set_when_trunk_bound(self):
|
||||
helpers.register_ovs_agent(host=helpers.HOST)
|
||||
with self.port() as port:
|
||||
trunk_port_id = port['port']['id']
|
||||
trunk_req = {'port_id': trunk_port_id,
|
||||
'tenant_id': 'test_tenant',
|
||||
'sub_ports': []}
|
||||
trunk_res = self.trunk_plugin.create_trunk(self.context,
|
||||
{'trunk': trunk_req})
|
||||
port['port'][pb.HOST_ID] = helpers.HOST
|
||||
bound_port = self.core_plugin.update_port(self.context,
|
||||
trunk_port_id, port)
|
||||
self.assertEqual(
|
||||
trunk_utils.gen_trunk_br_name(trunk_res['id']),
|
||||
bound_port[pb.VIF_DETAILS][pb.VIF_DETAILS_BRIDGE_NAME])
|
||||
|
||||
def test_ovs_bridge_name_not_set_when_not_trunk(self):
|
||||
helpers.register_ovs_agent(host=helpers.HOST)
|
||||
with self.port() as port:
|
||||
port['port'][pb.HOST_ID] = helpers.HOST
|
||||
bound_port = self.core_plugin.update_port(self.context,
|
||||
port['port']['id'], port)
|
||||
self.assertIsNone(
|
||||
bound_port[pb.VIF_DETAILS].get(pb.VIF_DETAILS_BRIDGE_NAME))
|
||||
@@ -16,10 +16,14 @@
|
||||
from neutron_lib import constants
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import registry
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers.openvswitch.mech_driver \
|
||||
import mech_openvswitch
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
|
||||
constants as a_const)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.mech_driver import (
|
||||
mech_openvswitch)
|
||||
from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base
|
||||
|
||||
|
||||
@@ -59,6 +63,19 @@ class OpenvswitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
||||
self.driver = mech_openvswitch.OpenvswitchMechanismDriver()
|
||||
self.driver.initialize()
|
||||
|
||||
def test__set_bridge_name_notify(self):
|
||||
|
||||
def fake_callback(resource, event, trigger, **kwargs):
|
||||
trigger('fake-br-name')
|
||||
|
||||
registry.subscribe(fake_callback, a_const.OVS_BRIDGE_NAME,
|
||||
events.BEFORE_READ)
|
||||
fake_vif_details = {}
|
||||
self.driver._set_bridge_name('foo', fake_vif_details)
|
||||
self.assertEqual(
|
||||
'fake-br-name',
|
||||
fake_vif_details.get(portbindings.VIF_DETAILS_BRIDGE_NAME, ''))
|
||||
|
||||
|
||||
class OpenvswitchMechanismSGDisabledBaseTestCase(
|
||||
OpenvswitchMechanismBaseTestCase):
|
||||
|
||||
@@ -11,9 +11,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from neutron_lib import constants
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import registry
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
|
||||
constants as agent_consts)
|
||||
from neutron.services.trunk.drivers.openvswitch import driver
|
||||
from neutron.tests import base
|
||||
|
||||
@@ -35,3 +41,14 @@ class OVSDriverTestCase(base.BaseTestCase):
|
||||
'openvswitch', group='ml2')
|
||||
ovs_driver = driver.OVSDriver.create()
|
||||
self.assertTrue(ovs_driver.is_loaded)
|
||||
|
||||
@mock.patch('neutron.services.trunk.utils.gen_trunk_br_name')
|
||||
def test_vif_details_bridge_name_handler_registration(self,
|
||||
mock_gen_br_name):
|
||||
driver.register()
|
||||
mock_gen_br_name.return_value = 'fake-trunk-br-name'
|
||||
test_trigger = mock.Mock()
|
||||
registry.notify(agent_consts.OVS_BRIDGE_NAME, events.BEFORE_READ,
|
||||
test_trigger, **{'port': {'trunk_details':
|
||||
{'trunk_id': 'foo'}}})
|
||||
test_trigger.assert_called_once_with('fake-trunk-br-name')
|
||||
|
||||
Reference in New Issue
Block a user