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:
joe@midokura.com 2013-10-25 09:01:27 +00:00 committed by Gerrit Code Review
parent da139f6156
commit e19a2ae0b3
6 changed files with 103 additions and 163 deletions

View File

@ -18,6 +18,7 @@ kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP
ovs-vsctl: CommandFilter, ovs-vsctl, root ovs-vsctl: CommandFilter, ovs-vsctl, root
ivs-ctl: CommandFilter, ivs-ctl, root ivs-ctl: CommandFilter, ivs-ctl, root
mm-ctl: CommandFilter, mm-ctl, root
dhcp_release: CommandFilter, dhcp_release, root dhcp_release: CommandFilter, dhcp_release, root
# metadata proxy # metadata proxy

View File

@ -15,6 +15,7 @@ haproxy: CommandFilter, haproxy, root
kill_haproxy_usr: KillFilter, root, /usr/sbin/haproxy, -9, -HUP kill_haproxy_usr: KillFilter, root, /usr/sbin/haproxy, -9, -HUP
ovs-vsctl: CommandFilter, ovs-vsctl, root ovs-vsctl: CommandFilter, ovs-vsctl, root
mm-ctl: CommandFilter, mm-ctl, root
# ip_lib # ip_lib
ip: IpFilter, ip, root ip: IpFilter, ip, root

View File

@ -219,6 +219,50 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
device_name) 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): class IVSInterfaceDriver(LinuxInterfaceDriver):
"""Driver for creating an internal interface on an IVS bridge.""" """Driver for creating an internal interface on an IVS bridge."""

View File

@ -19,13 +19,7 @@
# @author: Tomoe Sugihara, Midokura Japan KK # @author: Tomoe Sugihara, Midokura Japan KK
# @author: Ryu Ishimoto, 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 dhcp
from neutron.agent.linux import interface
from neutron.agent.linux import ip_lib
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from neutron.plugins.midonet.common import config # noqa from neutron.plugins.midonet.common import config # noqa
@ -59,83 +53,3 @@ class DhcpNoOpDriver(dhcp.DhcpLocalProcess):
def spawn_process(self): def spawn_process(self):
pass 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()

View File

@ -19,93 +19,16 @@
# @author: Rossella Sblendido, Midokura Japan KK # @author: Rossella Sblendido, Midokura Japan KK
import mock import mock
from oslo.config import cfg
import sys import sys
sys.modules["midonetclient"] = mock.Mock() sys.modules["midonetclient"] = mock.Mock()
from neutron.agent.common import config from neutron.agent.common import config
from neutron.agent.linux import dhcp 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.common import config as base_config
from neutron.openstack.common import uuidutils
import neutron.plugins.midonet.agent.midonet_driver as driver import neutron.plugins.midonet.agent.midonet_driver as driver
from neutron.tests import base 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: class FakeNetwork:
id = 'aaaabbbb-cccc-dddd-eeee-ffff00001111' id = 'aaaabbbb-cccc-dddd-eeee-ffff00001111'
namespace = 'qdhcp-ns' namespace = 'qdhcp-ns'

View File

@ -23,6 +23,7 @@ from neutron.agent.linux import interface
from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils from neutron.agent.linux import utils
from neutron.extensions.flavor import (FLAVOR_NETWORK) from neutron.extensions.flavor import (FLAVOR_NETWORK)
from neutron.openstack.common import uuidutils
from neutron.tests import base from neutron.tests import base
@ -468,3 +469,59 @@ class TestIVSInterfaceDriver(TestBase):
execute.assert_called_once_with(ivsctl_cmd, 'sudo') execute.assert_called_once_with(ivsctl_cmd, 'sudo')
self.ip_dev.assert_has_calls([mock.call('ns-0', 'sudo', None), self.ip_dev.assert_has_calls([mock.call('ns-0', 'sudo', None),
mock.call().link.delete()]) 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())