From eb9bda12d25a565e117a518d93abd75bfa50730a Mon Sep 17 00:00:00 2001 From: Andreas Scheuring Date: Mon, 3 Aug 2015 15:11:20 +0200 Subject: [PATCH] macvtap: ML2 mech driver for macvtap network attachments This driver uses the vif_type 'macvtap'. It enriches the vif_details with the corresponding attributes required by nova [1] to support macvtap attachments for libvirt qemu/kvm guests. The review is submitted in three parts: - Part 1 Common functions that are used by the ml2 driver and the agent - Part 2 (this part) The Mechanism Driver to support port binding for macvtap attachments - Part 3 The Macvtap L2 Agent. [1] https://review.openstack.org/#/c/182283 Change-Id: I206f58a21c36e55de957d8a23993aa9bc26d1595 Partial-Bug: #1480979 --- neutron/extensions/portbindings.py | 15 +++ .../drivers/macvtap/mech_driver/__init__.py | 0 .../macvtap/mech_driver/mech_macvtap.py | 80 ++++++++++++++++ .../drivers/macvtap/mech_driver/__init__.py | 0 .../macvtap/mech_driver/test_mech_macvtap.py | 92 +++++++++++++++++++ setup.cfg | 1 + 6 files changed, 188 insertions(+) create mode 100644 neutron/plugins/ml2/drivers/macvtap/mech_driver/__init__.py create mode 100644 neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py create mode 100644 neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/__init__.py create mode 100644 neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/test_mech_macvtap.py diff --git a/neutron/extensions/portbindings.py b/neutron/extensions/portbindings.py index e9dc8b8261c..3b61175f9e4 100644 --- a/neutron/extensions/portbindings.py +++ b/neutron/extensions/portbindings.py @@ -46,6 +46,9 @@ PROFILE = 'binding:profile' CAP_PORT_FILTER = 'port_filter' OVS_HYBRID_PLUG = 'ovs_hybrid_plug' VIF_DETAILS_VLAN = 'vlan' +VIF_DETAILS_MACVTAP_SOURCE = 'macvtap_source' +VIF_DETAILS_MACVTAP_MODE = 'macvtap_mode' +VIF_DETAILS_PHYSICAL_INTERFACE = 'physical_interface' # The keys below are used in the VIF_DETAILS attribute to convey # information related to the configuration of the vhost-user VIF driver. @@ -63,6 +66,10 @@ VHOST_USER_SOCKET = 'vhostuser_socket' # method should be used when binding the # vhost-user vif. VHOST_USER_OVS_PLUG = 'vhostuser_ovs_plug' + +# VIF_TYPE: vif_types are required by Nova to determine which vif_driver to +# use to attach a virtual server to the network + # - vhost-user: The vhost-user interface type is a standard virtio interface # provided by qemu 2.1+. This constant defines the neutron side # of the vif binding type to provide a common definition @@ -75,7 +82,15 @@ VIF_TYPE_DISTRIBUTED = 'distributed' VIF_TYPE_OVS = 'ovs' VIF_TYPE_BRIDGE = 'bridge' VIF_TYPE_OTHER = 'other' +# vif_type_macvtap: Tells Nova that the macvtap vif_driver should be used to +# create a vif. It does not require the VNIC_TYPE_MACVTAP, +# which is defined further below. E.g. Macvtap agent uses +# vnic_type 'normal'. +VIF_TYPE_MACVTAP = 'macvtap' +# VNIC_TYPE: It's used to determine which mechanism driver to use to bind a +# port. It can be specified via the Neutron API. Default is normal, +# used by OVS and LinuxBridge agent. VNIC_NORMAL = 'normal' VNIC_DIRECT = 'direct' VNIC_MACVTAP = 'macvtap' diff --git a/neutron/plugins/ml2/drivers/macvtap/mech_driver/__init__.py b/neutron/plugins/ml2/drivers/macvtap/mech_driver/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py b/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py new file mode 100644 index 00000000000..5da8a760001 --- /dev/null +++ b/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py @@ -0,0 +1,80 @@ +# Copyright (c) 2016 IBM Corp. +# +# 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_log import log + +from neutron.common import constants +from neutron.extensions import portbindings +from neutron.plugins.common import constants as p_constants +from neutron.plugins.ml2 import driver_api as api +from neutron.plugins.ml2.drivers.macvtap import macvtap_common +from neutron.plugins.ml2.drivers import mech_agent + +LOG = log.getLogger(__name__) + +MACVTAP_MODE_BRIDGE = 'bridge' + + +class MacvtapMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): + """Attach to networks using Macvtap L2 agent. + + The MacvtapMechanismDriver integrates the ml2 plugin with the + macvtap L2 agent. Port binding with this driver requires the + macvtap 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): + super(MacvtapMechanismDriver, self).__init__( + constants.AGENT_TYPE_MACVTAP, + portbindings.VIF_TYPE_MACVTAP, + {portbindings.CAP_PORT_FILTER: False}) + + def get_allowed_network_types(self, agent): + return [p_constants.TYPE_FLAT, p_constants.TYPE_VLAN] + + def get_mappings(self, agent): + return agent['configurations'].get('interface_mappings', {}) + + def check_vlan_transparency(self, context): + """Macvtap driver vlan transparency support.""" + return False + + def try_to_bind_segment_for_agent(self, context, segment, agent): + if self.check_segment_for_agent(segment, agent): + vif_details_segment = self.vif_details + mappings = self.get_mappings(agent) + interface = mappings[segment['physical_network']] + network_type = segment[api.NETWORK_TYPE] + + if network_type == p_constants.TYPE_VLAN: + vlan_id = segment[api.SEGMENTATION_ID] + macvtap_src = macvtap_common.get_vlan_device_name(interface, + vlan_id) + vif_details_segment['vlan'] = vlan_id + else: + macvtap_src = interface + + vif_details_segment['physical_interface'] = interface + vif_details_segment['macvtap_source'] = macvtap_src + vif_details_segment['macvtap_mode'] = MACVTAP_MODE_BRIDGE + LOG.debug("Macvtap vif_details added to context binding: %s", + vif_details_segment) + context.set_binding(segment[api.ID], self.vif_type, + vif_details_segment) + return True + return False diff --git a/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/__init__.py b/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/test_mech_macvtap.py b/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/test_mech_macvtap.py new file mode 100644 index 00000000000..918b153d9b7 --- /dev/null +++ b/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/test_mech_macvtap.py @@ -0,0 +1,92 @@ +# Copyright (c) 2015 IBM Corp. +# 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.macvtap.mech_driver import mech_macvtap +from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base + + +class MacvtapMechanismBaseTestCase(base.AgentMechanismBaseTestCase): + VIF_TYPE = portbindings.VIF_TYPE_MACVTAP + CAP_PORT_FILTER = False + AGENT_TYPE = constants.AGENT_TYPE_MACVTAP + + GOOD_MAPPINGS = {'fake_physical_network': 'fake_if'} + GOOD_CONFIGS = {'interface_mappings': GOOD_MAPPINGS} + + BAD_MAPPINGS = {'wrong_physical_network': 'wrong_if'} + BAD_CONFIGS = {'interface_mappings': BAD_MAPPINGS} + + AGENTS = [{'alive': True, + 'configurations': GOOD_CONFIGS, + 'host': 'host'}] + AGENTS_DEAD = [{'alive': False, + 'configurations': GOOD_CONFIGS, + 'host': 'dead_host'}] + AGENTS_BAD = [{'alive': False, + 'configurations': GOOD_CONFIGS, + 'host': 'bad_host_1'}, + {'alive': True, + 'configurations': BAD_CONFIGS, + 'host': 'bad_host_2'}] + + def setUp(self): + super(MacvtapMechanismBaseTestCase, self).setUp() + self.driver = mech_macvtap.MacvtapMechanismDriver() + self.driver.initialize() + + +class MacvtapMechanismGenericTestCase(MacvtapMechanismBaseTestCase, + base.AgentMechanismGenericTestCase): + pass + + +class MacvtapMechanismFlatTestCase(MacvtapMechanismBaseTestCase, + base.AgentMechanismFlatTestCase): + def test_type_flat_vif_details(self): + context = base.FakePortContext(self.AGENT_TYPE, + self.AGENTS, + self.FLAT_SEGMENTS, + vnic_type=self.VNIC_TYPE) + self.driver.bind_port(context) + vif_details = context._bound_vif_details + + self.assertIsNone(vif_details.get(portbindings.VIF_DETAILS_VLAN)) + self.assertEqual("bridge", vif_details.get( + portbindings.VIF_DETAILS_MACVTAP_MODE)) + self.assertEqual("fake_if", vif_details.get( + portbindings.VIF_DETAILS_PHYSICAL_INTERFACE)) + self.assertEqual("fake_if", vif_details.get( + portbindings.VIF_DETAILS_MACVTAP_SOURCE)) + + +class MacvtapMechanismVlanTestCase(MacvtapMechanismBaseTestCase, + base.AgentMechanismVlanTestCase): + def test_type_vlan_vif_details(self): + context = base.FakePortContext(self.AGENT_TYPE, + self.AGENTS, + self.VLAN_SEGMENTS, + vnic_type=self.VNIC_TYPE) + self.driver.bind_port(context) + vif_details = context._bound_vif_details + + self.assertEqual(1234, vif_details.get(portbindings.VIF_DETAILS_VLAN)) + self.assertEqual("bridge", vif_details.get( + portbindings.VIF_DETAILS_MACVTAP_MODE)) + self.assertEqual("fake_if", vif_details.get( + portbindings.VIF_DETAILS_PHYSICAL_INTERFACE)) + self.assertEqual("fake_if.1234", vif_details.get( + portbindings.VIF_DETAILS_MACVTAP_SOURCE)) diff --git a/setup.cfg b/setup.cfg index 922a3e58e27..e8ef675e6ea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -91,6 +91,7 @@ neutron.ml2.mechanism_drivers = logger = neutron.tests.unit.plugins.ml2.drivers.mechanism_logger:LoggerMechanismDriver test = neutron.tests.unit.plugins.ml2.drivers.mechanism_test:TestMechanismDriver linuxbridge = neutron.plugins.ml2.drivers.linuxbridge.mech_driver.mech_linuxbridge:LinuxbridgeMechanismDriver + macvtap = neutron.plugins.ml2.drivers.macvtap.mech_driver.mech_macvtap:MacvtapMechanismDriver openvswitch = neutron.plugins.ml2.drivers.openvswitch.mech_driver.mech_openvswitch:OpenvswitchMechanismDriver l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver sriovnicswitch = neutron.plugins.ml2.drivers.mech_sriov.mech_driver.mech_driver:SriovNicSwitchMechanismDriver