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
This commit is contained in:
		
				
					committed by
					
						
						Gerrit Code Review
					
				
			
			
				
	
			
			
			
						parent
						
							cfef5212ed
						
					
				
				
					commit
					e2926d043f
				
			@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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."""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
 
 | 
			
		||||
@@ -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())
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user