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:
parent
da139f6156
commit
e19a2ae0b3
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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."""
|
||||||
|
|
||||||
|
@ -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()
|
|
||||||
|
@ -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'
|
||||||
|
@ -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())
|
||||||
|
Loading…
Reference in New Issue
Block a user