# (ListOpt) Ordered list of networking mechanism driver entrypoints
# to be loaded from the neutron.ml2.mechanism_drivers namespace.
# mechanism_drivers =
+# Example: mechanism drivers = openvswitch,mlnx
# Example: mechanism_drivers = arista
# Example: mechanism_drivers = cisco,logger
--- /dev/null
+[eswitch]
+# (StrOpt) Type of Network Interface to allocate for VM:
+# mlnx_direct or hostdev according to libvirt terminology
+# vnic_type = mlnx_direct
VIF_TYPE_802_QBH = '802.1qbh'
VIF_TYPE_HYPERV = 'hyperv'
VIF_TYPE_MIDONET = 'midonet'
+VIF_TYPE_MLNX_DIRECT = 'mlnx_direct'
+VIF_TYPE_MLNX_HOSTDEV = 'hostdev'
VIF_TYPE_OTHER = 'other'
VIF_TYPES = [VIF_TYPE_UNBOUND, VIF_TYPE_BINDING_FAILED, VIF_TYPE_OVS,
VIF_TYPE_IVS, VIF_TYPE_BRIDGE, VIF_TYPE_802_QBG,
VIF_TYPE_802_QBH, VIF_TYPE_HYPERV, VIF_TYPE_MIDONET,
- VIF_TYPE_OTHER]
+ VIF_TYPE_MLNX_DIRECT, VIF_TYPE_MLNX_HOSTDEV, VIF_TYPE_OTHER]
VNIC_NORMAL = 'normal'
VNIC_DIRECT = 'direct'
return
+def get_port_from_device_mac(device_mac):
+ LOG.debug(_("get_port_from_device_mac() called for mac %s"), device_mac)
+ session = db_api.get_session()
+ qry = session.query(models_v2.Port).filter_by(mac_address=device_mac)
+ return qry.first()
+
+
def get_port_and_sgs(port_id):
"""Get port from database with security group info."""
--- /dev/null
+# Copyright (c) 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# 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 oslo.config import cfg
+
+from neutron.extensions import portbindings
+
+eswitch_opts = [
+ cfg.StrOpt('vnic_type',
+ default=portbindings.VIF_TYPE_MLNX_DIRECT,
+ help=_("Type of VM network interface: mlnx_direct or "
+ "hostdev")),
+]
+
+
+cfg.CONF.register_opts(eswitch_opts, "ESWITCH")
--- /dev/null
+# Copyright (c) 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# 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 oslo.config import cfg
+
+from neutron.common import constants
+from neutron.extensions import portbindings
+from neutron.openstack.common import log
+from neutron.plugins.ml2 import driver_api as api
+from neutron.plugins.ml2.drivers import mech_agent
+from neutron.plugins.ml2.drivers.mlnx import config # noqa
+
+LOG = log.getLogger(__name__)
+
+
+class MlnxMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
+ """Attach to networks using Mellanox eSwitch L2 agent.
+
+ The MellanoxMechanismDriver integrates the ml2 plugin with the
+ Mellanox eswitch L2 agent. Port binding with this driver requires the
+ Mellanox eswitch agent to be running on the port's host, and that agent
+ to have connectivity to at least one segment of the port's
+ network.
+ """
+
+ def __init__(self):
+ # REVISIT(irenab): update supported_vnic_types to contain
+ # only VNIC_DIRECT and VNIC_MACVTAP once its possible to specify
+ # vnic_type via nova API/GUI. Currently VNIC_NORMAL is included
+ # to enable VM creation via GUI. It should be noted, that if
+ # several MDs are capable to bing bind port on chosen host, the
+ # first listed MD will bind the port for VNIC_NORMAL.
+ super(MlnxMechanismDriver, self).__init__(
+ constants.AGENT_TYPE_MLNX,
+ cfg.CONF.ESWITCH.vnic_type,
+ {portbindings.CAP_PORT_FILTER: False},
+ portbindings.VNIC_TYPES)
+
+ def check_segment_for_agent(self, segment, agent):
+ mappings = agent['configurations'].get('interface_mappings', {})
+ LOG.debug(_("Checking segment: %(segment)s "
+ "for mappings: %(mappings)s "),
+ {'segment': segment, 'mappings': mappings})
+
+ network_type = segment[api.NETWORK_TYPE]
+ if network_type == 'local':
+ return True
+ elif network_type in ['flat', 'vlan']:
+ return segment[api.PHYSICAL_NETWORK] in mappings
+ else:
+ return False
+
+ def try_to_bind_segment_for_agent(self, context, segment, agent):
+ if self.check_segment_for_agent(segment, agent):
+ vif_type = self._get_vif_type(
+ context.current[portbindings.VNIC_TYPE])
+ context.set_binding(segment[api.ID],
+ vif_type,
+ self.vif_details)
+
+ def _get_vif_type(self, requested_vnic_type):
+ if requested_vnic_type == portbindings.VNIC_MACVTAP:
+ return portbindings.VIF_TYPE_MLNX_DIRECT
+ elif requested_vnic_type == portbindings.VNIC_DIRECT:
+ return portbindings.VIF_TYPE_MLNX_HOSTDEV
+ return self.vif_type
from neutron import manager
from neutron.openstack.common import log
from neutron.openstack.common.rpc import proxy
+from neutron.openstack.common import uuidutils
from neutron.plugins.ml2 import db
from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2.drivers import type_tunnel
if device.startswith(TAP_DEVICE_PREFIX):
return device[TAP_DEVICE_PREFIX_LENGTH:]
else:
- return device
+ # REVISIT(irenab): Consider calling into bound MD to
+ # handle the get_device_details RPC, then remove the 'else' clause
+ if not uuidutils.is_uuid_like(device):
+ port = db.get_port_from_device_mac(device)
+ if port:
+ return port.id
+ return device
@classmethod
def get_port_from_device(cls, device):
# update plugin about port status
self.agent.plugin_rpc.update_device_up(self.context,
port['mac_address'],
- self.agent.agent_id)
+ self.agent.agent_id,
+ cfg.CONF.host)
else:
self.eswitch.port_down(net_id,
physical_network,
self.agent.plugin_rpc.update_device_down(
self.context,
port['mac_address'],
- self.agent.agent_id)
+ self.agent.agent_id,
+ cfg.CONF.host)
except rpc_common.Timeout:
LOG.error(_("RPC timeout while updating port %s"), port['id'])
else:
def __init__(self, interface_mapping):
self._polling_interval = cfg.CONF.AGENT.polling_interval
self._setup_eswitches(interface_mapping)
+ configurations = {'interface_mappings': interface_mapping}
self.agent_state = {
'binary': 'neutron-mlnx-agent',
'host': cfg.CONF.host,
'topic': q_constants.L2_AGENT_TOPIC,
- 'configurations': interface_mapping,
+ 'configurations': configurations,
'agent_type': q_constants.AGENT_TYPE_MLNX,
'start_flag': True}
self._setup_rpc()
def _report_state(self):
try:
devices = len(self.eswitch.get_vnics_mac())
- self.agent_state['configurations']['devices'] = devices
+ self.agent_state.get('configurations')['devices'] = devices
self.state_rpc.report_state(self.context,
self.agent_state)
self.agent_state.pop('start_flag', None)
LOG.info(_("Port %s updated"), device)
LOG.debug(_("Device details %s"), str(dev_details))
self.treat_vif_port(dev_details['port_id'],
- dev_details['port_mac'],
+ dev_details['device'],
dev_details['network_id'],
dev_details['network_type'],
dev_details['physical_network'],
port_id = self.eswitch.get_port_id_by_mac(device)
dev_details = self.plugin_rpc.update_device_down(self.context,
port_id,
- self.agent_id)
+ self.agent_id,
+ cfg.CONF.host)
except Exception as e:
LOG.debug(_("Removing port failed for device %(device)s "
"due to %(exc)s"), {'device': device, 'exc': e})
class FakePortContext(api.PortContext):
- def __init__(self, agent_type, agents, segments):
+ def __init__(self, agent_type, agents, segments,
+ vnic_type=portbindings.VNIC_NORMAL):
self._agent_type = agent_type
self._agents = agents
self._network_context = FakeNetworkContext(segments)
+ self._bound_vnic_type = vnic_type
self._bound_segment_id = None
self._bound_vif_type = None
self._bound_vif_details = None
@property
def current(self):
- return {'id': PORT_ID}
+ return {'id': PORT_ID,
+ 'binding:vnic_type': self._bound_vnic_type}
@property
def original(self):
--- /dev/null
+# Copyright (c) 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# 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.common import constants
+from neutron.extensions import portbindings
+from neutron.plugins.ml2.drivers.mlnx import mech_mlnx
+from neutron.tests.unit.ml2 import _test_mech_agent as base
+
+
+class MlnxMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
+ VIF_TYPE = portbindings.VIF_TYPE_MLNX_DIRECT
+ CAP_PORT_FILTER = False
+ AGENT_TYPE = constants.AGENT_TYPE_MLNX
+
+ GOOD_MAPPINGS = {'fake_physical_network': 'fake_bridge'}
+ GOOD_CONFIGS = {'interface_mappings': GOOD_MAPPINGS}
+
+ BAD_MAPPINGS = {'wrong_physical_network': 'wrong_bridge'}
+ BAD_CONFIGS = {'interface_mappings': BAD_MAPPINGS}
+
+ AGENTS = [{'alive': True,
+ 'configurations': GOOD_CONFIGS}]
+ AGENTS_DEAD = [{'alive': False,
+ 'configurations': GOOD_CONFIGS}]
+ AGENTS_BAD = [{'alive': False,
+ 'configurations': GOOD_CONFIGS},
+ {'alive': True,
+ 'configurations': BAD_CONFIGS}]
+
+ def setUp(self):
+ super(MlnxMechanismBaseTestCase, self).setUp()
+ self.driver = mech_mlnx.MlnxMechanismDriver()
+ self.driver.initialize()
+
+
+class MlnxMechanismGenericTestCase(MlnxMechanismBaseTestCase,
+ base.AgentMechanismGenericTestCase):
+ pass
+
+
+class MlnxMechanismLocalTestCase(MlnxMechanismBaseTestCase,
+ base.AgentMechanismLocalTestCase):
+ pass
+
+
+class MlnxMechanismFlatTestCase(MlnxMechanismBaseTestCase,
+ base.AgentMechanismFlatTestCase):
+ pass
+
+
+class MlnxMechanismVlanTestCase(MlnxMechanismBaseTestCase,
+ base.AgentMechanismVlanTestCase):
+ pass
+
+
+class MlnxMechanismVnicTypeTestCase(MlnxMechanismBaseTestCase,
+ base.AgentMechanismVlanTestCase):
+ def _check_vif_type_for_vnic_type(self, vnic_type,
+ expected_vif_type):
+ context = base.FakePortContext(self.AGENT_TYPE,
+ self.AGENTS,
+ self.VLAN_SEGMENTS,
+ vnic_type)
+ self.driver.bind_port(context)
+ self.assertEqual(expected_vif_type, context._bound_vif_type)
+
+ def test_vnic_type_direct(self):
+ self._check_vif_type_for_vnic_type(portbindings.VNIC_DIRECT,
+ portbindings.VIF_TYPE_MLNX_HOSTDEV)
+
+ def test_vnic_type_macvtap(self):
+ self._check_vif_type_for_vnic_type(portbindings.VNIC_MACVTAP,
+ portbindings.VIF_TYPE_MLNX_DIRECT)
+
+ def test_vnic_type_normal(self):
+ self._check_vif_type_for_vnic_type(portbindings.VNIC_NORMAL,
+ self.VIF_TYPE)
def test_treat_devices_added_updates_known_port_admin_down(self):
details = {'port_id': '1234567890',
- 'port_mac': '01:02:03:04:05:06',
+ 'device': '01:02:03:04:05:06',
'network_id': '123456789',
'network_type': 'vlan',
'physical_network': 'default',
l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver
bigswitch = neutron.plugins.ml2.drivers.mech_bigswitch.driver:BigSwitchMechanismDriver
ofagent = neutron.plugins.ml2.drivers.mech_ofagent:OfagentMechanismDriver
+ mlnx = neutron.plugins.ml2.drivers.mlnx.mech_mlnx:MlnxMechanismDriver
neutron.openstack.common.cache.backends =
memory = neutron.openstack.common.cache._backends.memory:MemoryBackend