Merge "Add a new interface driver OVSVethInterfaceDriver."
This commit is contained in:
commit
ba0258f120
@ -151,11 +151,13 @@ class L3NATAgent(object):
|
|||||||
for d in ns_ip.get_devices(exclude_loopback=True):
|
for d in ns_ip.get_devices(exclude_loopback=True):
|
||||||
if d.name.startswith(INTERNAL_DEV_PREFIX):
|
if d.name.startswith(INTERNAL_DEV_PREFIX):
|
||||||
# device is on default bridge
|
# device is on default bridge
|
||||||
self.driver.unplug(d.name, namespace=namespace)
|
self.driver.unplug(d.name, namespace=namespace,
|
||||||
|
prefix=INTERNAL_DEV_PREFIX)
|
||||||
elif d.name.startswith(EXTERNAL_DEV_PREFIX):
|
elif d.name.startswith(EXTERNAL_DEV_PREFIX):
|
||||||
self.driver.unplug(d.name,
|
self.driver.unplug(d.name,
|
||||||
bridge=self.conf.external_network_bridge,
|
bridge=self.conf.external_network_bridge,
|
||||||
namespace=namespace)
|
namespace=namespace,
|
||||||
|
prefix=EXTERNAL_DEV_PREFIX)
|
||||||
#(TODO) Address the failure for the deletion of the namespace
|
#(TODO) Address the failure for the deletion of the namespace
|
||||||
|
|
||||||
def _create_router_namespace(self, ri):
|
def _create_router_namespace(self, ri):
|
||||||
@ -386,7 +388,8 @@ class L3NATAgent(object):
|
|||||||
namespace=ri.ns_name()):
|
namespace=ri.ns_name()):
|
||||||
self.driver.unplug(interface_name,
|
self.driver.unplug(interface_name,
|
||||||
bridge=self.conf.external_network_bridge,
|
bridge=self.conf.external_network_bridge,
|
||||||
namespace=ri.ns_name())
|
namespace=ri.ns_name(),
|
||||||
|
prefix=EXTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
ex_gw_ip = ex_gw_port['fixed_ips'][0]['ip_address']
|
ex_gw_ip = ex_gw_port['fixed_ips'][0]['ip_address']
|
||||||
for c, r in self.external_gateway_filter_rules():
|
for c, r in self.external_gateway_filter_rules():
|
||||||
@ -444,7 +447,8 @@ class L3NATAgent(object):
|
|||||||
if ip_lib.device_exists(interface_name,
|
if ip_lib.device_exists(interface_name,
|
||||||
root_helper=self.conf.root_helper,
|
root_helper=self.conf.root_helper,
|
||||||
namespace=ri.ns_name()):
|
namespace=ri.ns_name()):
|
||||||
self.driver.unplug(interface_name, namespace=ri.ns_name())
|
self.driver.unplug(interface_name, namespace=ri.ns_name(),
|
||||||
|
prefix=INTERNAL_DEV_PREFIX)
|
||||||
|
|
||||||
if ex_gw_port:
|
if ex_gw_port:
|
||||||
ex_gw_ip = ex_gw_port['fixed_ips'][0]['ip_address']
|
ex_gw_ip = ex_gw_port['fixed_ips'][0]['ip_address']
|
||||||
|
@ -92,7 +92,7 @@ class LinuxInterfaceDriver(object):
|
|||||||
"""Plug in the interface."""
|
"""Plug in the interface."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def unplug(self, device_name, bridge=None, namespace=None):
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
"""Unplug the interface."""
|
"""Unplug the interface."""
|
||||||
|
|
||||||
|
|
||||||
@ -101,13 +101,27 @@ class NullDriver(LinuxInterfaceDriver):
|
|||||||
bridge=None, namespace=None, prefix=None):
|
bridge=None, namespace=None, prefix=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def unplug(self, device_name, bridge=None, namespace=None):
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OVSInterfaceDriver(LinuxInterfaceDriver):
|
class OVSInterfaceDriver(LinuxInterfaceDriver):
|
||||||
"""Driver for creating an internal interface on an OVS bridge."""
|
"""Driver for creating an internal interface on an OVS bridge."""
|
||||||
|
|
||||||
|
def _ovs_add_port(self, bridge, device_name, port_id, mac_address,
|
||||||
|
internal=True):
|
||||||
|
cmd = ['ovs-vsctl', '--', '--may-exist',
|
||||||
|
'add-port', bridge, device_name]
|
||||||
|
if internal:
|
||||||
|
cmd += ['--', 'set', 'Interface', device_name, 'type=internal']
|
||||||
|
cmd += ['--', 'set', 'Interface', device_name,
|
||||||
|
'external-ids:iface-id=%s' % port_id,
|
||||||
|
'--', 'set', 'Interface', device_name,
|
||||||
|
'external-ids:iface-status=active',
|
||||||
|
'--', 'set', 'Interface', device_name,
|
||||||
|
'external-ids:attached-mac=%s' % mac_address]
|
||||||
|
utils.execute(cmd, self.conf.root_helper)
|
||||||
|
|
||||||
def plug(self, network_id, port_id, device_name, mac_address,
|
def plug(self, network_id, port_id, device_name, mac_address,
|
||||||
bridge=None, namespace=None, prefix=None):
|
bridge=None, namespace=None, prefix=None):
|
||||||
"""Plug in the interface."""
|
"""Plug in the interface."""
|
||||||
@ -120,19 +134,7 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
self.conf.root_helper,
|
self.conf.root_helper,
|
||||||
namespace=namespace):
|
namespace=namespace):
|
||||||
|
|
||||||
utils.execute(['ovs-vsctl',
|
self._ovs_add_port(bridge, device_name, port_id, mac_address)
|
||||||
'--', '--may-exist', 'add-port', bridge,
|
|
||||||
device_name,
|
|
||||||
'--', 'set', 'Interface', device_name,
|
|
||||||
'type=internal',
|
|
||||||
'--', 'set', 'Interface', device_name,
|
|
||||||
'external-ids:iface-id=%s' % port_id,
|
|
||||||
'--', 'set', 'Interface', device_name,
|
|
||||||
'external-ids:iface-status=active',
|
|
||||||
'--', 'set', 'Interface', device_name,
|
|
||||||
'external-ids:attached-mac=%s' %
|
|
||||||
mac_address],
|
|
||||||
self.conf.root_helper)
|
|
||||||
|
|
||||||
ip = ip_lib.IPWrapper(self.conf.root_helper)
|
ip = ip_lib.IPWrapper(self.conf.root_helper)
|
||||||
device = ip.device(device_name)
|
device = ip.device(device_name)
|
||||||
@ -145,7 +147,7 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
namespace_obj.add_device_to_namespace(device)
|
namespace_obj.add_device_to_namespace(device)
|
||||||
device.link.set_up()
|
device.link.set_up()
|
||||||
|
|
||||||
def unplug(self, device_name, bridge=None, namespace=None):
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
"""Unplug the interface."""
|
"""Unplug the interface."""
|
||||||
if not bridge:
|
if not bridge:
|
||||||
bridge = self.conf.ovs_integration_bridge
|
bridge = self.conf.ovs_integration_bridge
|
||||||
@ -186,7 +188,7 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
else:
|
else:
|
||||||
LOG.warn(_("Device %s already exists") % device_name)
|
LOG.warn(_("Device %s already exists") % device_name)
|
||||||
|
|
||||||
def unplug(self, device_name, bridge=None, namespace=None):
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
"""Unplug the interface."""
|
"""Unplug the interface."""
|
||||||
device = ip_lib.IPDevice(device_name, self.conf.root_helper, namespace)
|
device = ip_lib.IPDevice(device_name, self.conf.root_helper, namespace)
|
||||||
try:
|
try:
|
||||||
@ -224,6 +226,69 @@ class RyuInterfaceDriver(OVSInterfaceDriver):
|
|||||||
self.ryu_client.create_port(network_id, datapath_id, port_no)
|
self.ryu_client.create_port(network_id, datapath_id, port_no)
|
||||||
|
|
||||||
|
|
||||||
|
class OVSVethInterfaceDriver(OVSInterfaceDriver):
|
||||||
|
"""Driver for creating an OVS interface using veth."""
|
||||||
|
|
||||||
|
DEV_NAME_PREFIX = 'ns-'
|
||||||
|
|
||||||
|
def _get_tap_name(self, device_name, prefix=None):
|
||||||
|
if not prefix:
|
||||||
|
prefix = self.DEV_NAME_PREFIX
|
||||||
|
return device_name.replace(prefix, 'tap')
|
||||||
|
|
||||||
|
def plug(self, network_id, port_id, device_name, mac_address,
|
||||||
|
bridge=None, namespace=None, prefix=None):
|
||||||
|
"""Plugin the interface."""
|
||||||
|
if not bridge:
|
||||||
|
bridge = self.conf.ovs_integration_bridge
|
||||||
|
|
||||||
|
self.check_bridge_exists(bridge)
|
||||||
|
|
||||||
|
if not ip_lib.device_exists(device_name,
|
||||||
|
self.conf.root_helper,
|
||||||
|
namespace=namespace):
|
||||||
|
ip = ip_lib.IPWrapper(self.conf.root_helper)
|
||||||
|
|
||||||
|
tap_name = self._get_tap_name(device_name, prefix)
|
||||||
|
root_veth, ns_veth = ip.add_veth(tap_name, device_name)
|
||||||
|
|
||||||
|
self._ovs_add_port(bridge, tap_name, port_id, mac_address,
|
||||||
|
internal=False)
|
||||||
|
|
||||||
|
ns_veth.link.set_address(mac_address)
|
||||||
|
if self.conf.network_device_mtu:
|
||||||
|
ns_veth.link.set_mtu(self.conf.network_device_mtu)
|
||||||
|
root_veth.link.set_mtu(self.conf.network_device_mtu)
|
||||||
|
|
||||||
|
if namespace:
|
||||||
|
namespace_obj = ip.ensure_namespace(namespace)
|
||||||
|
namespace_obj.add_device_to_namespace(ns_veth)
|
||||||
|
|
||||||
|
root_veth.link.set_up()
|
||||||
|
ns_veth.link.set_up()
|
||||||
|
else:
|
||||||
|
LOG.warn(_("Device %s already exists") % device_name)
|
||||||
|
|
||||||
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
|
"""Unplug the interface."""
|
||||||
|
if not bridge:
|
||||||
|
bridge = self.conf.ovs_integration_bridge
|
||||||
|
|
||||||
|
tap_name = self._get_tap_name(device_name, prefix)
|
||||||
|
self.check_bridge_exists(bridge)
|
||||||
|
ovs = ovs_lib.OVSBridge(bridge, self.conf.root_helper)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ovs.delete_port(tap_name)
|
||||||
|
device = ip_lib.IPDevice(device_name, self.conf.root_helper,
|
||||||
|
namespace)
|
||||||
|
device.link.delete()
|
||||||
|
LOG.debug(_("Unplugged interface '%s'") % device_name)
|
||||||
|
except RuntimeError:
|
||||||
|
LOG.error(_("Failed unplugging interface '%s'") %
|
||||||
|
device_name)
|
||||||
|
|
||||||
|
|
||||||
class MetaInterfaceDriver(LinuxInterfaceDriver):
|
class MetaInterfaceDriver(LinuxInterfaceDriver):
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
super(MetaInterfaceDriver, self).__init__(conf)
|
super(MetaInterfaceDriver, self).__init__(conf)
|
||||||
@ -266,9 +331,9 @@ class MetaInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
return driver.plug(network_id, port_id, device_name, mac_address,
|
return driver.plug(network_id, port_id, device_name, mac_address,
|
||||||
bridge=bridge, namespace=namespace, prefix=prefix)
|
bridge=bridge, namespace=namespace, prefix=prefix)
|
||||||
|
|
||||||
def unplug(self, device_name, bridge=None, namespace=None):
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
driver = self._get_driver_by_device_name(device_name, namespace=None)
|
driver = self._get_driver_by_device_name(device_name, namespace=None)
|
||||||
return driver.unplug(device_name, bridge, namespace)
|
return driver.unplug(device_name, bridge, namespace, prefix)
|
||||||
|
|
||||||
def _load_driver(self, driver_provider):
|
def _load_driver(self, driver_provider):
|
||||||
LOG.debug("Driver location:%s", driver_provider)
|
LOG.debug("Driver location:%s", driver_provider)
|
||||||
|
@ -171,6 +171,83 @@ class TestOVSInterfaceDriver(TestBase):
|
|||||||
mock.call().delete_port('tap0')])
|
mock.call().delete_port('tap0')])
|
||||||
|
|
||||||
|
|
||||||
|
class TestOVSVethInterfaceDriver(TestOVSInterfaceDriver):
|
||||||
|
|
||||||
|
def test_get_device_name(self):
|
||||||
|
br = interface.OVSVethInterfaceDriver(self.conf)
|
||||||
|
device_name = br.get_device_name(FakePort())
|
||||||
|
self.assertEqual('ns-abcdef01-12', device_name)
|
||||||
|
|
||||||
|
def test_plug_with_prefix(self):
|
||||||
|
self._test_plug(devname='qr-0', prefix='qr-')
|
||||||
|
|
||||||
|
def _test_plug(self, devname=None, bridge=None, namespace=None,
|
||||||
|
prefix=None, mtu=None):
|
||||||
|
|
||||||
|
if not devname:
|
||||||
|
devname = 'ns-0'
|
||||||
|
if not bridge:
|
||||||
|
bridge = 'br-int'
|
||||||
|
|
||||||
|
def device_exists(dev, root_helper=None, namespace=None):
|
||||||
|
return dev == bridge
|
||||||
|
|
||||||
|
ovs = interface.OVSVethInterfaceDriver(self.conf)
|
||||||
|
self.device_exists.side_effect = device_exists
|
||||||
|
|
||||||
|
root_veth = mock.Mock()
|
||||||
|
ns_veth = mock.Mock()
|
||||||
|
self.ip().add_veth = mock.Mock(return_value=(root_veth, ns_veth))
|
||||||
|
expected = [mock.call('sudo'), mock.call().add_veth('tap0', devname)]
|
||||||
|
|
||||||
|
vsctl_cmd = ['ovs-vsctl', '--', '--may-exist', 'add-port',
|
||||||
|
bridge, 'tap0', '--', 'set', 'Interface', 'tap0',
|
||||||
|
'external-ids:iface-id=port-1234', '--', 'set',
|
||||||
|
'Interface', 'tap0',
|
||||||
|
'external-ids:iface-status=active', '--', 'set',
|
||||||
|
'Interface', 'tap0',
|
||||||
|
'external-ids:attached-mac=aa:bb:cc:dd:ee:ff']
|
||||||
|
with mock.patch.object(utils, 'execute') as execute:
|
||||||
|
ovs.plug('01234567-1234-1234-99',
|
||||||
|
'port-1234',
|
||||||
|
devname,
|
||||||
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
bridge=bridge,
|
||||||
|
namespace=namespace,
|
||||||
|
prefix=prefix)
|
||||||
|
execute.assert_called_once_with(vsctl_cmd, 'sudo')
|
||||||
|
|
||||||
|
ns_veth.assert_has_calls(
|
||||||
|
[mock.call.link.set_address('aa:bb:cc:dd:ee:ff')])
|
||||||
|
if mtu:
|
||||||
|
ns_veth.assert_has_calls([mock.call.link.set_mtu(mtu)])
|
||||||
|
root_veth.assert_has_calls([mock.call.link.set_mtu(mtu)])
|
||||||
|
if namespace:
|
||||||
|
expected.extend(
|
||||||
|
[mock.call().ensure_namespace(namespace),
|
||||||
|
mock.call().ensure_namespace().add_device_to_namespace(
|
||||||
|
mock.ANY)])
|
||||||
|
|
||||||
|
self.ip.assert_has_calls(expected)
|
||||||
|
root_veth.assert_has_calls([mock.call.link.set_up()])
|
||||||
|
ns_veth.assert_has_calls([mock.call.link.set_up()])
|
||||||
|
|
||||||
|
def test_plug_mtu(self):
|
||||||
|
self.conf.set_override('network_device_mtu', 9000)
|
||||||
|
self._test_plug(mtu=9000)
|
||||||
|
|
||||||
|
def test_unplug(self, bridge=None):
|
||||||
|
if not bridge:
|
||||||
|
bridge = 'br-int'
|
||||||
|
with mock.patch('quantum.agent.linux.ovs_lib.OVSBridge') as ovs_br:
|
||||||
|
ovs = interface.OVSVethInterfaceDriver(self.conf)
|
||||||
|
ovs.unplug('ns-0', bridge=bridge)
|
||||||
|
ovs_br.assert_has_calls([mock.call(bridge, 'sudo'),
|
||||||
|
mock.call().delete_port('tap0')])
|
||||||
|
self.ip_dev.assert_has_calls([mock.call('ns-0', 'sudo', None),
|
||||||
|
mock.call().link.delete()])
|
||||||
|
|
||||||
|
|
||||||
class TestBridgeInterfaceDriver(TestBase):
|
class TestBridgeInterfaceDriver(TestBase):
|
||||||
def test_get_device_name(self):
|
def test_get_device_name(self):
|
||||||
br = interface.BridgeInterfaceDriver(self.conf)
|
br = interface.BridgeInterfaceDriver(self.conf)
|
||||||
|
Loading…
Reference in New Issue
Block a user