From cd1292fee48a7a5ee423cc40598b7377f35ee3fd Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Thu, 21 Aug 2014 09:12:15 -0700 Subject: [PATCH] BSN: Bind external ports in ML2 driver Add a binding capability to the Big Switch ML2 mechanism driver to mark external ports as bound. Ports with the owner type 'neutron:external_port' will be assigned to the first VLAN segment available in the network. The port update operation will then carry the HOST ID with the identifier to the backend for the necessary fabric configuration. Implements: blueprint bsn-ml2-ext-attach Change-Id: I7b749ba5ef9a47b45be24f77656a10ed38e5e6ae --- .../ml2/drivers/mech_bigswitch/driver.py | 35 ++++++++++++++----- .../unit/ml2/drivers/test_bigswitch_mech.py | 17 +++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py b/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py index 75f25cea92..dc8c12c339 100644 --- a/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py +++ b/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py @@ -34,6 +34,7 @@ from neutron.plugins.common import constants as pconst from neutron.plugins.ml2 import driver_api as api +EXTERNAL_PORT_OWNER = 'neutron:external_port' LOG = log.getLogger(__name__) # time in seconds to maintain existence of vswitch response @@ -137,16 +138,32 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base, return prepped_port def bind_port(self, context): - if not self.does_vswitch_exist(context.host): - # this is not an IVS host - return + """Marks ports as bound. - # currently only vlan segments are supported - for segment in context.network.network_segments: - if segment[api.NETWORK_TYPE] == pconst.TYPE_VLAN: - context.set_binding(segment[api.ID], portbindings.VIF_TYPE_IVS, - {portbindings.CAP_PORT_FILTER: True, - portbindings.OVS_HYBRID_PLUG: False}) + Binds external ports and IVS ports. + Fabric configuration will occur on the subsequent port update. + Currently only vlan segments are supported. + """ + if context.current['device_owner'] == EXTERNAL_PORT_OWNER: + # TODO(kevinbenton): check controller to see if the port exists + # so this driver can be run in parallel with others that add + # support for external port bindings + for segment in context.network.network_segments: + if segment[api.NETWORK_TYPE] == pconst.TYPE_VLAN: + context.set_binding( + segment[api.ID], portbindings.VIF_TYPE_BRIDGE, + {portbindings.CAP_PORT_FILTER: False, + portbindings.OVS_HYBRID_PLUG: False}) + return + + # IVS hosts will have a vswitch with the same name as the hostname + if self.does_vswitch_exist(context.host): + for segment in context.network.network_segments: + if segment[api.NETWORK_TYPE] == pconst.TYPE_VLAN: + context.set_binding( + segment[api.ID], portbindings.VIF_TYPE_IVS, + {portbindings.CAP_PORT_FILTER: True, + portbindings.OVS_HYBRID_PLUG: False}) def does_vswitch_exist(self, host): """Check if Indigo vswitch exists with the given hostname. diff --git a/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py b/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py index 2dbe2a6d6d..f1c844734b 100644 --- a/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py +++ b/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py @@ -23,8 +23,10 @@ import webob.exc from neutron import context as neutron_context from neutron.extensions import portbindings from neutron import manager +from neutron.openstack.common import jsonutils from neutron.plugins.bigswitch import servermanager from neutron.plugins.ml2 import config as ml2_config +from neutron.plugins.ml2.drivers.mech_bigswitch import driver as bsn_driver from neutron.plugins.ml2.drivers import type_vlan as vlan_config import neutron.tests.unit.bigswitch.test_restproxy_plugin as trp from neutron.tests.unit.ml2 import test_ml2_plugin @@ -195,3 +197,18 @@ class TestBigSwitchMechDriverPortsV2(test_db_plugin.TestPortsV2, self.assertEqual('host', pb['binding:host_id']) self.assertIn('bound_segment', pb) self.assertIn('network', pb) + + def test_bind_external_port(self): + ext_id = jsonutils.dumps({'type': 'vlan', 'chassis_id': 'FF', + 'port_id': '1'}) + port_kwargs = { + portbindings.HOST_ID: ext_id, + 'device_owner': bsn_driver.EXTERNAL_PORT_OWNER + } + with contextlib.nested( + mock.patch(SERVER_POOL + '.rest_create_port'), + self.port(arg_list=(portbindings.HOST_ID,), **port_kwargs) + ) as (rmock, port): + create_body = rmock.mock_calls[-1][1][2] + self.assertIsNotNone(create_body['bound_segment']) + self.assertEqual(create_body[portbindings.HOST_ID], ext_id)