From e2926d043f35d75d0245afb479491041f1992a5c Mon Sep 17 00:00:00 2001 From: "joe@midokura.com" Date: Fri, 25 Oct 2013 09:01:27 +0000 Subject: [PATCH] Move MidonetInterfaceDriver and use mm-ctl * Change the plug method in MidonetInterfaceDriver to use mm-ctl * Move MidonetInterfaceDriver to interface.py * adapt interface driver midonet unit tests to mm-ctl Change-Id: Ib6cfbc212b793fa939cad17017c0b2b8b0a5b7fb Closes-Bug: #1245797 --- etc/neutron/rootwrap.d/dhcp.filters | 1 + etc/neutron/rootwrap.d/lbaas-haproxy.filters | 1 + neutron/agent/linux/interface.py | 44 ++++++++++ .../plugins/midonet/agent/midonet_driver.py | 86 ------------------- .../tests/unit/midonet/test_midonet_driver.py | 77 ----------------- neutron/tests/unit/test_linux_interface.py | 57 ++++++++++++ 6 files changed, 103 insertions(+), 163 deletions(-) diff --git a/etc/neutron/rootwrap.d/dhcp.filters b/etc/neutron/rootwrap.d/dhcp.filters index 02e091375a..88d61e8e30 100644 --- a/etc/neutron/rootwrap.d/dhcp.filters +++ b/etc/neutron/rootwrap.d/dhcp.filters @@ -18,6 +18,7 @@ kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP ovs-vsctl: CommandFilter, ovs-vsctl, root ivs-ctl: CommandFilter, ivs-ctl, root +mm-ctl: CommandFilter, mm-ctl, root dhcp_release: CommandFilter, dhcp_release, root # metadata proxy diff --git a/etc/neutron/rootwrap.d/lbaas-haproxy.filters b/etc/neutron/rootwrap.d/lbaas-haproxy.filters index aaf6be399f..fb2641d2ce 100644 --- a/etc/neutron/rootwrap.d/lbaas-haproxy.filters +++ b/etc/neutron/rootwrap.d/lbaas-haproxy.filters @@ -15,6 +15,7 @@ haproxy: CommandFilter, haproxy, root kill_haproxy_usr: KillFilter, root, /usr/sbin/haproxy, -9, -HUP ovs-vsctl: CommandFilter, ovs-vsctl, root +mm-ctl: CommandFilter, mm-ctl, root # ip_lib ip: IpFilter, ip, root diff --git a/neutron/agent/linux/interface.py b/neutron/agent/linux/interface.py index 42a1f82d15..8bb62058a3 100644 --- a/neutron/agent/linux/interface.py +++ b/neutron/agent/linux/interface.py @@ -219,6 +219,50 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): device_name) +class MidonetInterfaceDriver(LinuxInterfaceDriver): + + def plug(self, network_id, port_id, device_name, mac_address, + bridge=None, namespace=None, prefix=None): + """This method is called by the Dhcp agent or by the L3 agent + when a new network is created + """ + if not ip_lib.device_exists(device_name, + self.root_helper, + namespace=namespace): + ip = ip_lib.IPWrapper(self.root_helper) + tap_name = device_name.replace(prefix or 'tap', 'tap') + + # Create ns_dev in a namespace if one is configured. + root_dev, ns_dev = ip.add_veth(tap_name, device_name, + namespace2=namespace) + + ns_dev.link.set_address(mac_address) + + # Add an interface created by ovs to the namespace. + namespace_obj = ip.ensure_namespace(namespace) + namespace_obj.add_device_to_namespace(ns_dev) + + ns_dev.link.set_up() + root_dev.link.set_up() + + cmd = ['mm-ctl', '--bind-port', port_id, device_name] + utils.execute(cmd, self.root_helper) + + else: + LOG.warn(_("Device %s already exists"), device_name) + + def unplug(self, device_name, bridge=None, namespace=None, prefix=None): + # the port will be deleted by the dhcp agent that will call the plugin + device = ip_lib.IPDevice(device_name, + self.root_helper, + namespace) + device.link.delete() + LOG.debug(_("Unplugged interface '%s'"), device_name) + + ip_lib.IPWrapper( + self.root_helper, namespace).garbage_collect_namespace() + + class IVSInterfaceDriver(LinuxInterfaceDriver): """Driver for creating an internal interface on an IVS bridge.""" diff --git a/neutron/plugins/midonet/agent/midonet_driver.py b/neutron/plugins/midonet/agent/midonet_driver.py index ccdc4ade2e..2bbd573df2 100644 --- a/neutron/plugins/midonet/agent/midonet_driver.py +++ b/neutron/plugins/midonet/agent/midonet_driver.py @@ -19,13 +19,7 @@ # @author: Tomoe Sugihara, Midokura Japan KK # @author: Ryu Ishimoto, Midokura Japan KK -from midonetclient import api -from oslo.config import cfg -from webob import exc as w_exc - from neutron.agent.linux import dhcp -from neutron.agent.linux import interface -from neutron.agent.linux import ip_lib from neutron.openstack.common import log as logging from neutron.plugins.midonet.common import config # noqa @@ -59,83 +53,3 @@ class DhcpNoOpDriver(dhcp.DhcpLocalProcess): def spawn_process(self): pass - - -class MidonetInterfaceDriver(interface.LinuxInterfaceDriver): - def __init__(self, conf): - super(MidonetInterfaceDriver, self).__init__(conf) - # Read config values - midonet_conf = conf.MIDONET - midonet_uri = midonet_conf.midonet_uri - admin_user = midonet_conf.username - admin_pass = midonet_conf.password - admin_project_id = midonet_conf.project_id - - self.mido_api = api.MidonetApi(midonet_uri, admin_user, - admin_pass, - project_id=admin_project_id) - - def _get_host_uuid(self): - """Get MidoNet host id from host_uuid.properties file.""" - f = open(cfg.CONF.MIDONET.midonet_host_uuid_path) - lines = f.readlines() - host_uuid = filter(lambda x: x.startswith('host_uuid='), - lines)[0].strip()[len('host_uuid='):] - return host_uuid - - def plug(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): - """This method is called by the Dhcp agent or by the L3 agent - when a new network is created - """ - if not ip_lib.device_exists(device_name, - self.root_helper, - namespace=namespace): - ip = ip_lib.IPWrapper(self.root_helper) - tap_name = device_name.replace(prefix or 'tap', 'tap') - - # Create ns_dev in a namespace if one is configured. - root_dev, ns_dev = ip.add_veth(tap_name, - device_name, - namespace2=namespace) - - ns_dev.link.set_address(mac_address) - - # Add an interface created by ovs to the namespace. - namespace_obj = ip.ensure_namespace(namespace) - namespace_obj.add_device_to_namespace(ns_dev) - - ns_dev.link.set_up() - root_dev.link.set_up() - - vport_id = port_id - host_dev_name = device_name - - # create if-vport mapping. - host_uuid = self._get_host_uuid() - try: - host = self.mido_api.get_host(host_uuid) - except w_exc.HTTPError as e: - LOG.error(_('Failed to create a if-vport mapping on host=%s'), - host_uuid) - raise e - try: - self.mido_api.add_host_interface_port( - host, vport_id, host_dev_name) - except w_exc.HTTPError: - LOG.warn(_( - 'Faild binding vport=%(vport)s to device=%(device)s'), - {"vport": vport_id, "device": host_dev_name}) - else: - LOG.warn(_("Device %s already exists"), device_name) - - def unplug(self, device_name, bridge=None, namespace=None, prefix=None): - # the port will be deleted by the dhcp agent that will call the plugin - device = ip_lib.IPDevice(device_name, - self.root_helper, - namespace) - device.link.delete() - LOG.debug(_("Unplugged interface '%s'"), device_name) - - ip_lib.IPWrapper( - self.root_helper, namespace).garbage_collect_namespace() diff --git a/neutron/tests/unit/midonet/test_midonet_driver.py b/neutron/tests/unit/midonet/test_midonet_driver.py index a4662e1636..cc550cfbe9 100644 --- a/neutron/tests/unit/midonet/test_midonet_driver.py +++ b/neutron/tests/unit/midonet/test_midonet_driver.py @@ -19,93 +19,16 @@ # @author: Rossella Sblendido, Midokura Japan KK import mock -from oslo.config import cfg import sys sys.modules["midonetclient"] = mock.Mock() from neutron.agent.common import config from neutron.agent.linux import dhcp -from neutron.agent.linux import interface -from neutron.agent.linux import ip_lib from neutron.common import config as base_config -from neutron.openstack.common import uuidutils import neutron.plugins.midonet.agent.midonet_driver as driver from neutron.tests import base -class MidoInterfaceDriverTestCase(base.BaseTestCase): - def setUp(self): - self.conf = config.setup_conf() - self.conf.register_opts(interface.OPTS) - config.register_root_helper(self.conf) - self.ip_dev_p = mock.patch.object(ip_lib, 'IPDevice') - self.ip_dev = self.ip_dev_p.start() - self.ip_p = mock.patch.object(ip_lib, 'IPWrapper') - self.ip = self.ip_p.start() - self.device_exists_p = mock.patch.object(ip_lib, 'device_exists') - self.device_exists = self.device_exists_p.start() - self.device_exists.return_value = False - self.addCleanup(mock.patch.stopall) - midonet_opts = [ - cfg.StrOpt('midonet_uri', - default='http://localhost:8080/midonet-api', - help=_('MidoNet API server URI.')), - cfg.StrOpt('username', default='admin', - help=_('MidoNet admin username.')), - cfg.StrOpt('password', default='passw0rd', - secret=True, - help=_('MidoNet admin password.')), - cfg.StrOpt('project_id', - default='77777777-7777-7777-7777-777777777777', - help=_('ID of the project that MidoNet admin user' - 'belongs to.')) - ] - self.conf.register_opts(midonet_opts, "MIDONET") - self.driver = driver.MidonetInterfaceDriver(self.conf) - self.root_dev = mock.Mock() - self.ns_dev = mock.Mock() - self.ip().add_veth = mock.Mock(return_value=( - self.root_dev, self.ns_dev)) - self.driver._get_host_uuid = mock.Mock( - return_value=uuidutils.generate_uuid()) - self.network_id = uuidutils.generate_uuid() - self.port_id = uuidutils.generate_uuid() - self.device_name = "tap0" - self.mac_address = "aa:bb:cc:dd:ee:ff" - self.bridge = "br-test" - self.namespace = "ns-test" - super(MidoInterfaceDriverTestCase, self).setUp() - - def test_plug(self): - self.driver.plug( - self.network_id, self.port_id, - self.device_name, self.mac_address, - self.bridge, self.namespace) - - expected = [mock.call(), mock.call('sudo'), - mock.call().add_veth(self.device_name, - self.device_name, - namespace2=self.namespace), - mock.call().ensure_namespace(self.namespace), - mock.call().ensure_namespace().add_device_to_namespace( - mock.ANY)] - self.ns_dev.assert_has_calls( - [mock.call.link.set_address(self.mac_address)]) - - self.root_dev.assert_has_calls([mock.call.link.set_up()]) - self.ns_dev.assert_has_calls([mock.call.link.set_up()]) - self.ip.assert_has_calls(expected, True) - - def test_unplug(self): - self.driver.unplug(self.device_name, self.bridge, self.namespace) - - self.ip_dev.assert_has_calls([ - mock.call(self.device_name, self.driver.root_helper, - self.namespace), - mock.call().link.delete()]) - self.ip.assert_has_calls(mock.call().garbage_collect_namespace()) - - class FakeNetwork: id = 'aaaabbbb-cccc-dddd-eeee-ffff00001111' namespace = 'qdhcp-ns' diff --git a/neutron/tests/unit/test_linux_interface.py b/neutron/tests/unit/test_linux_interface.py index d5576439e4..a260944299 100644 --- a/neutron/tests/unit/test_linux_interface.py +++ b/neutron/tests/unit/test_linux_interface.py @@ -23,6 +23,7 @@ from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.extensions.flavor import (FLAVOR_NETWORK) +from neutron.openstack.common import uuidutils from neutron.tests import base @@ -468,3 +469,59 @@ class TestIVSInterfaceDriver(TestBase): execute.assert_called_once_with(ivsctl_cmd, 'sudo') self.ip_dev.assert_has_calls([mock.call('ns-0', 'sudo', None), mock.call().link.delete()]) + + +class TestMidonetInterfaceDriver(TestBase): + def setUp(self): + self.conf = config.setup_conf() + self.conf.register_opts(interface.OPTS) + config.register_root_helper(self.conf) + self.device_exists_p = mock.patch.object(ip_lib, 'device_exists') + self.device_exists = self.device_exists_p.start() + self.addCleanup(mock.patch.stopall) + self.driver = interface.MidonetInterfaceDriver(self.conf) + self.network_id = uuidutils.generate_uuid() + self.port_id = uuidutils.generate_uuid() + self.device_name = "tap0" + self.mac_address = "aa:bb:cc:dd:ee:ff" + self.bridge = "br-test" + self.namespace = "ns-test" + super(TestMidonetInterfaceDriver, self).setUp() + + def test_plug(self): + cmd = ['mm-ctl', '--bind-port', self.port_id, 'tap0'] + self.device_exists.return_value = False + + root_dev = mock.Mock() + ns_dev = mock.Mock() + self.ip().add_veth = mock.Mock(return_value=(root_dev, ns_dev)) + with mock.patch.object(utils, 'execute') as execute: + self.driver.plug( + self.network_id, self.port_id, + self.device_name, self.mac_address, + self.bridge, self.namespace) + execute.assert_called_once_with(cmd, 'sudo') + + expected = [mock.call(), mock.call('sudo'), + mock.call().add_veth(self.device_name, + self.device_name, + namespace2=self.namespace), + mock.call().ensure_namespace(self.namespace), + mock.call().ensure_namespace().add_device_to_namespace( + mock.ANY)] + + ns_dev.assert_has_calls( + [mock.call.link.set_address(self.mac_address)]) + + root_dev.assert_has_calls([mock.call.link.set_up()]) + ns_dev.assert_has_calls([mock.call.link.set_up()]) + self.ip.assert_has_calls(expected, True) + + def test_unplug(self): + self.driver.unplug(self.device_name, self.bridge, self.namespace) + + self.ip_dev.assert_has_calls([ + mock.call(self.device_name, self.driver.root_helper, + self.namespace), + mock.call().link.delete()]) + self.ip.assert_has_calls(mock.call().garbage_collect_namespace())