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
This commit is contained in:
Kevin Benton 2014-08-21 09:12:15 -07:00
parent cc8a57cb04
commit cd1292fee4
2 changed files with 43 additions and 9 deletions

View File

@ -34,6 +34,7 @@ from neutron.plugins.common import constants as pconst
from neutron.plugins.ml2 import driver_api as api from neutron.plugins.ml2 import driver_api as api
EXTERNAL_PORT_OWNER = 'neutron:external_port'
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
# time in seconds to maintain existence of vswitch response # time in seconds to maintain existence of vswitch response
@ -137,16 +138,32 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base,
return prepped_port return prepped_port
def bind_port(self, context): def bind_port(self, context):
if not self.does_vswitch_exist(context.host): """Marks ports as bound.
# this is not an IVS host
return
# currently only vlan segments are supported Binds external ports and IVS ports.
for segment in context.network.network_segments: Fabric configuration will occur on the subsequent port update.
if segment[api.NETWORK_TYPE] == pconst.TYPE_VLAN: Currently only vlan segments are supported.
context.set_binding(segment[api.ID], portbindings.VIF_TYPE_IVS, """
{portbindings.CAP_PORT_FILTER: True, if context.current['device_owner'] == EXTERNAL_PORT_OWNER:
portbindings.OVS_HYBRID_PLUG: False}) # 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): def does_vswitch_exist(self, host):
"""Check if Indigo vswitch exists with the given hostname. """Check if Indigo vswitch exists with the given hostname.

View File

@ -23,8 +23,10 @@ import webob.exc
from neutron import context as neutron_context from neutron import context as neutron_context
from neutron.extensions import portbindings from neutron.extensions import portbindings
from neutron import manager from neutron import manager
from neutron.openstack.common import jsonutils
from neutron.plugins.bigswitch import servermanager from neutron.plugins.bigswitch import servermanager
from neutron.plugins.ml2 import config as ml2_config 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 from neutron.plugins.ml2.drivers import type_vlan as vlan_config
import neutron.tests.unit.bigswitch.test_restproxy_plugin as trp import neutron.tests.unit.bigswitch.test_restproxy_plugin as trp
from neutron.tests.unit.ml2 import test_ml2_plugin 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.assertEqual('host', pb['binding:host_id'])
self.assertIn('bound_segment', pb) self.assertIn('bound_segment', pb)
self.assertIn('network', 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)