diff --git a/neutron/extensions/portbindings.py b/neutron/extensions/portbindings.py index ca1db137ef7..3fa4a2b1be5 100644 --- a/neutron/extensions/portbindings.py +++ b/neutron/extensions/portbindings.py @@ -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. diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py b/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py index 96903d35fac..b172ea9e4c9 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py @@ -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' diff --git a/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py b/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py index 88740debf78..494cbb54bb4 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py +++ b/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py @@ -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) diff --git a/neutron/services/trunk/drivers/openvswitch/constants.py b/neutron/services/trunk/drivers/openvswitch/constants.py new file mode 100644 index 00000000000..3620f68774c --- /dev/null +++ b/neutron/services/trunk/drivers/openvswitch/constants.py @@ -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-' diff --git a/neutron/services/trunk/drivers/openvswitch/driver.py b/neutron/services/trunk/drivers/openvswitch/driver.py index 600fbe9684e..4eddf18604a 100644 --- a/neutron/services/trunk/drivers/openvswitch/driver.py +++ b/neutron/services/trunk/drivers/openvswitch/driver.py @@ -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'])) diff --git a/neutron/services/trunk/utils.py b/neutron/services/trunk/utils.py new file mode 100644 index 00000000000..69172c38658 --- /dev/null +++ b/neutron/services/trunk/utils.py @@ -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]) diff --git a/neutron/tests/functional/services/trunk/__init__.py b/neutron/tests/functional/services/trunk/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/tests/functional/services/trunk/test_plugin.py b/neutron/tests/functional/services/trunk/test_plugin.py new file mode 100644 index 00000000000..0f716c841cd --- /dev/null +++ b/neutron/tests/functional/services/trunk/test_plugin.py @@ -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)) diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.py index c0883d5564f..beed8eedc04 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.py @@ -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): diff --git a/neutron/tests/unit/services/trunk/drivers/openvswitch/test_driver.py b/neutron/tests/unit/services/trunk/drivers/openvswitch/test_driver.py index 82457da1fc5..3e21388215b 100644 --- a/neutron/tests/unit/services/trunk/drivers/openvswitch/test_driver.py +++ b/neutron/tests/unit/services/trunk/drivers/openvswitch/test_driver.py @@ -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')